[CXF-6760] Adding missing resources
Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/636252c2 Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/636252c2 Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/636252c2 Branch: refs/heads/master-jaxrs-2.1 Commit: 636252c2ac66648fc5221ccdb71ebafde38feff6 Parents: 21031e3 Author: Sergey Beryozkin <[email protected]> Authored: Mon Jul 4 15:01:25 2016 +0100 Committer: Sergey Beryozkin <[email protected]> Committed: Mon Jul 4 15:01:25 2016 +0100 ---------------------------------------------------------------------- .../jaxrs/model/doc/DocumentationProvider.java | 29 ++ .../cxf/jaxrs/model/doc/JavaDocProvider.java | 380 +++++++++++++++++++ .../jaxrs/model/doc/JavaDocProviderTest.java | 107 ++++++ .../cxf/jaxrs/model/wadl/petstore/PetStore.java | 102 +++++ .../resources/javadocs/pet-store-javadoc16.jar | Bin 0 -> 3569 bytes .../resources/javadocs/pet-store-javadoc17.jar | Bin 0 -> 3601 bytes .../resources/javadocs/pet-store-javadoc18.jar | Bin 0 -> 3873 bytes .../jaxrs/swagger/AbstractSwaggerFeature.java | 191 ++++++++++ .../cxf/jaxrs/swagger/JaxRs2Extension.java | 225 +++++++++++ .../cxf/jaxrs/swagger/MatrixParameter.java | 28 ++ .../cxf/jaxrs/swagger/Swagger2Feature.java | 259 +++++++++++++ .../cxf/jaxrs/swagger/Swagger2Serializers.java | 164 ++++++++ .../cxf/jaxrs/swagger/SwaggerFeature.java | 115 ++++++ .../apache/cxf/jaxrs/swagger/SwaggerUtils.java | 233 ++++++++++++ .../io.swagger.jaxrs.ext.SwaggerExtension | 1 + .../cxf/jaxrs/swagger/Swagger2FeatureTest.java | 53 +++ .../cxf/jaxrs/swagger/SwaggerFeatureTest.java | 45 +++ .../cxf/jaxrs/swagger/SwaggerUtilsTest.java | 74 ++++ .../src/test/resources/swagger12.json | 27 ++ .../src/test/resources/swagger20.json | 44 +++ 20 files changed, 2077 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/636252c2/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/doc/DocumentationProvider.java ---------------------------------------------------------------------- diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/doc/DocumentationProvider.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/doc/DocumentationProvider.java new file mode 100644 index 0000000..dff4fd6 --- /dev/null +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/doc/DocumentationProvider.java @@ -0,0 +1,29 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.jaxrs.model.doc; + +import org.apache.cxf.jaxrs.model.ClassResourceInfo; +import org.apache.cxf.jaxrs.model.OperationResourceInfo; + +public interface DocumentationProvider { + String getClassDoc(ClassResourceInfo cri); + String getMethodDoc(OperationResourceInfo ori); + String getMethodResponseDoc(OperationResourceInfo ori); + String getMethodParameterDoc(OperationResourceInfo ori, int paramIndex); +} http://git-wip-us.apache.org/repos/asf/cxf/blob/636252c2/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/doc/JavaDocProvider.java ---------------------------------------------------------------------- diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/doc/JavaDocProvider.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/doc/JavaDocProvider.java new file mode 100644 index 0000000..5e702fe --- /dev/null +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/doc/JavaDocProvider.java @@ -0,0 +1,380 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.jaxrs.model.doc; + +import java.io.InputStream; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +import javax.ws.rs.Path; + +import org.apache.cxf.Bus; +import org.apache.cxf.BusFactory; +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.helpers.IOUtils; +import org.apache.cxf.jaxrs.model.ClassResourceInfo; +import org.apache.cxf.jaxrs.model.OperationResourceInfo; +import org.apache.cxf.jaxrs.utils.ResourceUtils; + +public class JavaDocProvider implements DocumentationProvider { + public static final double JAVA_VERSION = getVersion(); + public static final double JAVA_VERSION_16 = 1.6D; + public static final double JAVA_VERSION_17 = 1.7D; + public static final double JAVA_VERSION_18 = 1.8D; + + private ClassLoader javaDocLoader; + private final ConcurrentHashMap<String, ClassDocs> docs = new ConcurrentHashMap<>(); + private double javaDocsBuiltByVersion = JAVA_VERSION; + + public JavaDocProvider(URL... javaDocUrls) { + if (javaDocUrls == null) { + throw new IllegalArgumentException("URL are null"); + } + + javaDocLoader = new URLClassLoader(javaDocUrls); + } + + public JavaDocProvider(String path) throws Exception { + this(BusFactory.getDefaultBus(), path); + } + + public JavaDocProvider(String... paths) throws Exception { + this(BusFactory.getDefaultBus(), paths == null ? null : paths); + } + + public JavaDocProvider(Bus bus, String... paths) throws Exception { + if (paths == null) { + throw new IllegalArgumentException("paths are null"); + } + + URL[] javaDocUrls = new URL[paths.length]; + for (int i = 0; i < paths.length; i++) { + javaDocUrls[i] = ResourceUtils.getResourceURL(paths[i], bus); + } + javaDocLoader = new URLClassLoader(javaDocUrls); + } + + private static double getVersion() { + String version = System.getProperty("java.version"); + try { + return Double.parseDouble(version.substring(0, 3)); + } catch (Exception ex) { + return JAVA_VERSION_16; + } + } + + @Override + public String getClassDoc(ClassResourceInfo cri) { + try { + ClassDocs doc = getClassDocInternal(cri.getServiceClass()); + if (doc == null) { + return null; + } + return doc.getClassInfo(); + } catch (Exception ex) { + // ignore + } + return null; + } + + @Override + public String getMethodDoc(OperationResourceInfo ori) { + try { + MethodDocs doc = getOperationDocInternal(ori); + if (doc == null) { + return null; + } + return doc.getMethodInfo(); + } catch (Exception ex) { + // ignore + } + return null; + } + + @Override + public String getMethodResponseDoc(OperationResourceInfo ori) { + try { + MethodDocs doc = getOperationDocInternal(ori); + if (doc == null) { + return null; + } + return doc.getResponseInfo(); + } catch (Exception ex) { + // ignore + } + return null; + } + + @Override + public String getMethodParameterDoc(OperationResourceInfo ori, int paramIndex) { + try { + MethodDocs doc = getOperationDocInternal(ori); + if (doc == null) { + return null; + } + List<String> params = doc.getParamInfo(); + if (paramIndex < params.size()) { + return params.get(paramIndex); + } else { + return null; + } + } catch (Exception ex) { + // ignore + } + return null; + } + + private Class<?> getPathAnnotatedClass(Class<?> cls) { + if (cls.getAnnotation(Path.class) != null) { + return cls; + } + if (cls.getSuperclass() != null && cls.getSuperclass().getAnnotation(Path.class) != null) { + return cls.getSuperclass(); + } + for (Class<?> i : cls.getInterfaces()) { + if (i.getAnnotation(Path.class) != null) { + return i; + } + } + return cls; + } + + private ClassDocs getClassDocInternal(Class<?> cls) throws Exception { + Class<?> annotatedClass = getPathAnnotatedClass(cls); + String resource = annotatedClass.getName().replace(".", "/") + ".html"; + ClassDocs classDocs = docs.get(resource); + if (classDocs == null) { + InputStream resourceStream = javaDocLoader.getResourceAsStream(resource); + if (resourceStream != null) { + String doc = IOUtils.readStringFromStream(resourceStream); + + String qualifier = annotatedClass.isInterface() ? "Interface" : "Class"; + String classMarker = qualifier + " " + annotatedClass.getSimpleName(); + int index = doc.indexOf(classMarker); + if (index != -1) { + String classInfoTag = getClassInfoTag(); + String classInfo = getJavaDocText(doc, classInfoTag, + "Method Summary", index + classMarker.length()); + classDocs = new ClassDocs(doc, classInfo); + docs.putIfAbsent(resource, classDocs); + } + } + } + return classDocs; + } + + + private MethodDocs getOperationDocInternal(OperationResourceInfo ori) throws Exception { + Method method = ori.getAnnotatedMethod() == null + ? ori.getMethodToInvoke() + : ori.getAnnotatedMethod(); + ClassDocs classDoc = getClassDocInternal(method.getDeclaringClass()); + if (classDoc == null) { + return null; + } + MethodDocs mDocs = classDoc.getMethodDocs(method); + if (mDocs == null) { + String operLink = getOperLink(); + String operMarker = operLink + method.getName() + getOperationMarkerOpen(); + + int operMarkerIndex = classDoc.getClassDoc().indexOf(operMarker); + while (operMarkerIndex != -1) { + int startOfOpSigIndex = operMarkerIndex + operMarker.length(); + int endOfOpSigIndex = classDoc.getClassDoc().indexOf(getOperationMarkerClose(), + startOfOpSigIndex); + int paramLen = method.getParameterTypes().length; + if (endOfOpSigIndex == startOfOpSigIndex && paramLen == 0) { + break; + } else if (endOfOpSigIndex > startOfOpSigIndex + 1) { + String paramSequence = classDoc.getClassDoc().substring(operMarkerIndex, endOfOpSigIndex); + if (paramSequence.startsWith(operMarker)) { + paramSequence = paramSequence.substring(operMarker.length()); + String[] opBits = paramSequence.split(getOperationParamSeparator()); + if (opBits.length == paramLen) { + break; + } + } + } + operMarkerIndex = classDoc.getClassDoc().indexOf(operMarker, + operMarkerIndex + operMarker.length()); + } + + if (operMarkerIndex == -1) { + return null; + } + + String operDoc = classDoc.getClassDoc().substring(operMarkerIndex + operMarker.length()); + String operInfoTag = getOperInfoTag(); + String operInfo = getJavaDocText(operDoc, operInfoTag, operLink, 0); + String responseInfo = null; + List<String> paramDocs = new LinkedList<>(); + if (!StringUtils.isEmpty(operInfo)) { + int returnsIndex = operDoc.indexOf("Returns:", operLink.length()); + int nextOpIndex = operDoc.indexOf(operLink); + if (returnsIndex != -1 && (nextOpIndex > returnsIndex || nextOpIndex == -1)) { + responseInfo = getJavaDocText(operDoc, getResponseMarker(), operLink, returnsIndex + 8); + } + + int paramIndex = operDoc.indexOf("Parameters:"); + if (paramIndex != -1 && (nextOpIndex == -1 || paramIndex < nextOpIndex)) { + String paramString = returnsIndex == -1 ? operDoc.substring(paramIndex) + : operDoc.substring(paramIndex, returnsIndex); + + String codeTag = getCodeTag(); + + int codeIndex = paramString.indexOf(codeTag); + while (codeIndex != -1) { + int next = paramString.indexOf('<', codeIndex + 7); + if (next == -1) { + next = paramString.length(); + } + String param = paramString.substring(codeIndex + 7, next).trim(); + if (param.startsWith("-")) { + param = param.substring(1).trim(); + } + paramDocs.add(param); + if (next == paramString.length()) { + break; + } else { + codeIndex = next + 1; + } + codeIndex = paramString.indexOf(codeTag, codeIndex); + } + + } + } + mDocs = new MethodDocs(operInfo, paramDocs, responseInfo); + classDoc.addMethodDocs(method, mDocs); + } + + return mDocs; + } + + + + private String getJavaDocText(String doc, String tag, String notAfterTag, int index) { + int tagIndex = doc.indexOf(tag, index); + if (tagIndex != -1) { + int notAfterIndex = doc.indexOf(notAfterTag, index); + if (notAfterIndex == -1 || notAfterIndex > tagIndex) { + int nextIndex = doc.indexOf('<', tagIndex + tag.length()); + if (nextIndex != -1) { + return doc.substring(tagIndex + tag.length(), nextIndex).trim(); + } + } + } + return null; + } + + protected String getClassInfoTag() { + if (javaDocsBuiltByVersion == JAVA_VERSION_16) { + return "<P>"; + } else { + return "<div class=\"block\">"; + } + } + protected String getOperInfoTag() { + if (javaDocsBuiltByVersion == JAVA_VERSION_16) { + return "<DD>"; + } else { + return "<div class=\"block\">"; + } + } + protected String getOperLink() { + String operLink = "<A NAME=\""; + return javaDocsBuiltByVersion == JAVA_VERSION_16 ? operLink : operLink.toLowerCase(); + } + + protected String getResponseMarker() { + String tag = "<DD>"; + return javaDocsBuiltByVersion == JAVA_VERSION_16 ? tag : tag.toLowerCase(); + } + + protected String getCodeTag() { + String tag = "</CODE>"; + return javaDocsBuiltByVersion == JAVA_VERSION_16 ? tag : tag.toLowerCase(); + } + + protected String getOperationMarkerOpen() { + return javaDocsBuiltByVersion == JAVA_VERSION_18 ? "-" : "("; + } + protected String getOperationMarkerClose() { + return javaDocsBuiltByVersion == JAVA_VERSION_18 ? "-\"" : ")"; + } + protected String getOperationParamSeparator() { + return javaDocsBuiltByVersion == JAVA_VERSION_18 ? "-" : ","; + } + public void setJavaDocsBuiltByVersion(String version) { + javaDocsBuiltByVersion = Double.valueOf(version); + } + + private static class ClassDocs { + private final String classDoc; + private final String classInfo; + private final ConcurrentHashMap<Method, MethodDocs> mdocs = new ConcurrentHashMap<>(); + ClassDocs(String classDoc, String classInfo) { + this.classDoc = classDoc; + this.classInfo = classInfo; + } + + public String getClassDoc() { + return classDoc; + } + + public String getClassInfo() { + return classInfo; + } + + public MethodDocs getMethodDocs(Method method) { + return mdocs.get(method); + } + + public void addMethodDocs(Method method, MethodDocs doc) { + mdocs.putIfAbsent(method, doc); + } + } + + private static class MethodDocs { + private final String methodInfo; + private final List<String> paramInfo; + private final String responseInfo; + MethodDocs(String methodInfo, List<String> paramInfo, String responseInfo) { + this.methodInfo = methodInfo; + this.paramInfo = paramInfo; + this.responseInfo = responseInfo; + } + + public String getMethodInfo() { + return methodInfo; + } + + public List<String> getParamInfo() { + return paramInfo; + } + + public String getResponseInfo() { + return responseInfo; + } + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/636252c2/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/doc/JavaDocProviderTest.java ---------------------------------------------------------------------- diff --git a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/doc/JavaDocProviderTest.java b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/doc/JavaDocProviderTest.java new file mode 100644 index 0000000..94319e4 --- /dev/null +++ b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/doc/JavaDocProviderTest.java @@ -0,0 +1,107 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.jaxrs.model.doc; + +import org.apache.cxf.jaxrs.model.ClassResourceInfo; +import org.apache.cxf.jaxrs.model.OperationResourceInfo; +import org.apache.cxf.jaxrs.model.wadl.petstore.PetStore; +import org.apache.cxf.jaxrs.utils.ResourceUtils; + +import org.junit.Assert; +import org.junit.Test; + +public class JavaDocProviderTest extends Assert { + + @Test + public void testJava6Docs() throws Exception { + doTestJavaDocs("classpath:/javadocs/pet-store-javadoc16.jar", "1.6"); + } + + @Test + public void testJava7Docs() throws Exception { + doTestJavaDocs("classpath:/javadocs/pet-store-javadoc17.jar", "1.7"); + } + @Test + public void testJava8Docs() throws Exception { + doTestJavaDocs("classpath:/javadocs/pet-store-javadoc18.jar", "1.8"); + } + + private void doTestJavaDocs(String path, String version) throws Exception { + JavaDocProvider p = new JavaDocProvider(path); + p.setJavaDocsBuiltByVersion(version); + ClassResourceInfo cri = + ResourceUtils.createClassResourceInfo(PetStore.class, PetStore.class, true, true); + String classDoc = p.getClassDoc(cri); + assertEquals("The Pet Store", classDoc); + + boolean getStatus1Tested = false; + boolean getStatus2Tested = false; + boolean getStatus3Tested = false; + boolean noDocsTested = false; + for (OperationResourceInfo ori : cri.getMethodDispatcher().getOperationResourceInfos()) { + if ("getStatus1Param".equals(ori.getMethodToInvoke().getName())) { + testGetStatus1JavaDocs(p, ori); + getStatus1Tested = true; + } else if ("getStatus2Params".equals(ori.getMethodToInvoke().getName())) { + testGetStatus2JavaDocs(p, ori); + getStatus2Tested = true; + } else if ("getStatus3Params".equals(ori.getMethodToInvoke().getName())) { + testGetStatus3JavaDocs(p, ori); + getStatus3Tested = true; + } else if ("getBaseStatus".equals(ori.getMethodToInvoke().getName())) { + testOperWithNoJavaDocs(p, ori); + noDocsTested = true; + } + } + assertTrue(getStatus1Tested); + assertTrue(getStatus2Tested); + assertTrue(getStatus3Tested); + assertTrue(noDocsTested); + assertTrue(true); + } + + private void testOperWithNoJavaDocs(JavaDocProvider p, OperationResourceInfo ori) { + assertEquals(0, ori.getParameters().size()); + assertEquals("Return Pet Status with no params", p.getMethodDoc(ori)); + assertEquals("status", p.getMethodResponseDoc(ori)); + } + + private void testGetStatus1JavaDocs(JavaDocProvider p, OperationResourceInfo ori) { + assertEquals("Return Pet Status With 1 Param", p.getMethodDoc(ori)); + assertEquals(1, ori.getParameters().size()); + assertEquals("status", p.getMethodResponseDoc(ori)); + assertEquals("the pet id", p.getMethodParameterDoc(ori, 0)); + } + private void testGetStatus2JavaDocs(JavaDocProvider p, OperationResourceInfo ori) { + assertEquals("Return Pet Status with 2 params", p.getMethodDoc(ori)); + assertEquals(2, ori.getParameters().size()); + assertEquals("status", p.getMethodResponseDoc(ori)); + assertEquals("the pet id", p.getMethodParameterDoc(ori, 0)); + assertEquals("the query", p.getMethodParameterDoc(ori, 1)); + } + private void testGetStatus3JavaDocs(JavaDocProvider p, OperationResourceInfo ori) { + assertEquals("Return Pet Status With 3 Params", p.getMethodDoc(ori)); + assertEquals(3, ori.getParameters().size()); + assertEquals("status", p.getMethodResponseDoc(ori)); + assertEquals("the pet id", p.getMethodParameterDoc(ori, 0)); + assertEquals("the query", p.getMethodParameterDoc(ori, 1)); + assertEquals("the query2", p.getMethodParameterDoc(ori, 2)); + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/636252c2/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/wadl/petstore/PetStore.java ---------------------------------------------------------------------- diff --git a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/wadl/petstore/PetStore.java b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/wadl/petstore/PetStore.java new file mode 100644 index 0000000..356e702 --- /dev/null +++ b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/wadl/petstore/PetStore.java @@ -0,0 +1,102 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.jaxrs.model.wadl.petstore; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Response; + +/** + * The Pet Store + */ +@Path("/") +public class PetStore { + + public static final String CLOSED = "The Pet Store is closed"; + + public PetStore() { + } + + @GET + @Produces("text/plain") + /** + * Return Pet Status with no params + * + * @return status + * @throws Exception + */ + public Response getBaseStatus() throws Exception { + + return Response.ok(CLOSED).build(); + } + + /** + * Return Pet Status with 2 params + * @param petId the pet id + * @param query the query + * @return status + * @throws Exception + */ + @GET + @Path("/petstore/pets/{petId}/") + @Produces("text/xml") + public Response getStatus2Params(@PathParam("petId") String petId, + @QueryParam("query") String query) throws Exception { + + return Response.ok(CLOSED).build(); + } + + /** + * Return Pet Status With 1 Param + * @param petId the pet id + * @return status + * @throws Exception + */ + @GET + @Path("/petstore/pets/id/{petId}/") + @Produces("text/xml") + public Response getStatus1Param(@PathParam("petId") String petId) throws Exception { + + return Response.ok(CLOSED).build(); + } + + + /** + * Return Pet Status With 3 Params + * @param petId the pet id + * @param query the query + * @param query2 the query2 + * @return status + * @throws Exception + */ + @GET + @Path("/petstore/pets/{petId}/") + @Produces("text/xml") + public Response getStatus3Params(@PathParam("petId") String petId, + @QueryParam("query") String query, + @QueryParam("query2") String query2) throws Exception { + + return Response.ok(CLOSED).build(); + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/636252c2/rt/frontend/jaxrs/src/test/resources/javadocs/pet-store-javadoc16.jar ---------------------------------------------------------------------- diff --git a/rt/frontend/jaxrs/src/test/resources/javadocs/pet-store-javadoc16.jar b/rt/frontend/jaxrs/src/test/resources/javadocs/pet-store-javadoc16.jar new file mode 100644 index 0000000..b7f2cb6 Binary files /dev/null and b/rt/frontend/jaxrs/src/test/resources/javadocs/pet-store-javadoc16.jar differ http://git-wip-us.apache.org/repos/asf/cxf/blob/636252c2/rt/frontend/jaxrs/src/test/resources/javadocs/pet-store-javadoc17.jar ---------------------------------------------------------------------- diff --git a/rt/frontend/jaxrs/src/test/resources/javadocs/pet-store-javadoc17.jar b/rt/frontend/jaxrs/src/test/resources/javadocs/pet-store-javadoc17.jar new file mode 100644 index 0000000..d49a58d Binary files /dev/null and b/rt/frontend/jaxrs/src/test/resources/javadocs/pet-store-javadoc17.jar differ http://git-wip-us.apache.org/repos/asf/cxf/blob/636252c2/rt/frontend/jaxrs/src/test/resources/javadocs/pet-store-javadoc18.jar ---------------------------------------------------------------------- diff --git a/rt/frontend/jaxrs/src/test/resources/javadocs/pet-store-javadoc18.jar b/rt/frontend/jaxrs/src/test/resources/javadocs/pet-store-javadoc18.jar new file mode 100644 index 0000000..24a8c63 Binary files /dev/null and b/rt/frontend/jaxrs/src/test/resources/javadocs/pet-store-javadoc18.jar differ http://git-wip-us.apache.org/repos/asf/cxf/blob/636252c2/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/AbstractSwaggerFeature.java ---------------------------------------------------------------------- diff --git a/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/AbstractSwaggerFeature.java b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/AbstractSwaggerFeature.java new file mode 100644 index 0000000..c933b30 --- /dev/null +++ b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/AbstractSwaggerFeature.java @@ -0,0 +1,191 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.jaxrs.swagger; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.apache.cxf.Bus; +import org.apache.cxf.common.util.PackageUtils; +import org.apache.cxf.endpoint.Server; +import org.apache.cxf.feature.AbstractFeature; +import org.apache.cxf.jaxrs.JAXRSServiceFactoryBean; +import org.apache.cxf.jaxrs.model.ClassResourceInfo; + +public abstract class AbstractSwaggerFeature extends AbstractFeature { + + private static final boolean SWAGGER_JAXRS_AVAILABLE; + + static { + SWAGGER_JAXRS_AVAILABLE = isSwaggerJaxRsAvailable(); + } + + protected boolean scan = true; + protected boolean runAsFilter; + private boolean activateOnlyIfJaxrsSupported; + private String resourcePackage; + private String version = "1.0.0"; + // depending on swagger version basePath is set differently + private String basePath; + private String title = "Sample REST Application"; + private String description = "The Application"; + private String contact = "[email protected]"; + private String license = "Apache 2.0 License"; + private String licenseUrl = "http://www.apache.org/licenses/LICENSE-2.0.html"; + private String termsOfServiceUrl; + private String filterClass; + + private static boolean isSwaggerJaxRsAvailable() { + try { + Class.forName("io.swagger.jaxrs.DefaultParameterExtension"); + return true; + } catch (Throwable ex) { + return false; + } + } + + @Override + public void initialize(Server server, Bus bus) { + if (!activateOnlyIfJaxrsSupported || SWAGGER_JAXRS_AVAILABLE) { + calculateDefaultResourcePackage(server); + calculateDefaultBasePath(server); + addSwaggerResource(server); + + initializeProvider(server.getEndpoint(), bus); + bus.setProperty("swagger.service.descrition.available", "true"); + } + } + + protected abstract void addSwaggerResource(Server server); + + protected abstract void setBasePathByAddress(String address); + + private void calculateDefaultResourcePackage(Server server) { + if (!StringUtils.isEmpty(getResourcePackage())) { + return; + } + JAXRSServiceFactoryBean serviceFactoryBean = + (JAXRSServiceFactoryBean)server.getEndpoint().get(JAXRSServiceFactoryBean.class.getName()); + List<ClassResourceInfo> resourceInfos = serviceFactoryBean.getClassResourceInfo(); + + if (resourceInfos.size() == 1) { + setResourcePackage(resourceInfos.get(0).getServiceClass().getPackage().getName()); + } else { + List<Class<?>> serviceClasses = new ArrayList<Class<?>>(resourceInfos.size()); + for (ClassResourceInfo cri : resourceInfos) { + serviceClasses.add(cri.getServiceClass()); + } + String sharedPackage = PackageUtils.getSharedPackageName(serviceClasses); + if (!StringUtils.isEmpty(sharedPackage)) { + setResourcePackage(sharedPackage); + } + } + } + + private void calculateDefaultBasePath(Server server) { + if (getBasePath() == null || getBasePath().length() == 0) { + String address = server.getEndpoint().getEndpointInfo().getAddress(); + setBasePathByAddress(address); + } + } + + public String getResourcePackage() { + return resourcePackage; + } + public void setResourcePackage(String resourcePackage) { + this.resourcePackage = resourcePackage; + } + public String getVersion() { + return version; + } + public void setVersion(String version) { + this.version = version; + } + public String getBasePath() { + return basePath; + } + public void setBasePath(String basePath) { + this.basePath = basePath; + } + public String getTitle() { + return title; + } + public void setTitle(String title) { + this.title = title; + } + public String getDescription() { + return description; + } + public void setDescription(String description) { + this.description = description; + } + public String getContact() { + return contact; + } + public void setContact(String contact) { + this.contact = contact; + } + public String getLicense() { + return license; + } + public void setLicense(String license) { + this.license = license; + } + public String getLicenseUrl() { + return licenseUrl; + } + public void setLicenseUrl(String licenseUrl) { + this.licenseUrl = licenseUrl; + } + public String getTermsOfServiceUrl() { + return termsOfServiceUrl; + } + public void setTermsOfServiceUrl(String termsOfServiceUrl) { + this.termsOfServiceUrl = termsOfServiceUrl; + } + public boolean isScan() { + return scan; + } + public void setScan(boolean scan) { + this.scan = scan; + } + public String getFilterClass() { + return filterClass; + } + public void setFilterClass(String filterClass) { + this.filterClass = filterClass; + } + + public boolean isRunAsFilter() { + return runAsFilter; + } + public void setRunAsFilter(boolean runAsFilter) { + this.runAsFilter = runAsFilter; + } + + public boolean isActivateOnlyIfJaxrsSupported() { + return activateOnlyIfJaxrsSupported; + } + + public void setActivateOnlyIfJaxrsSupported(boolean activateOnlyIfJaxrsSupported) { + this.activateOnlyIfJaxrsSupported = activateOnlyIfJaxrsSupported; + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/636252c2/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/JaxRs2Extension.java ---------------------------------------------------------------------- diff --git a/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/JaxRs2Extension.java b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/JaxRs2Extension.java new file mode 100644 index 0000000..87e0cf2 --- /dev/null +++ b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/JaxRs2Extension.java @@ -0,0 +1,225 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.jaxrs.swagger; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.validation.constraints.DecimalMax; +import javax.validation.constraints.DecimalMin; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; +import javax.ws.rs.BeanParam; +import javax.ws.rs.MatrixParam; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.introspect.AnnotatedField; +import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; + +import io.swagger.converter.ModelConverters; +import io.swagger.jaxrs.ext.AbstractSwaggerExtension; +import io.swagger.jaxrs.ext.SwaggerExtension; +import io.swagger.jaxrs.ext.SwaggerExtensions; +import io.swagger.models.parameters.AbstractSerializableParameter; +import io.swagger.models.parameters.Parameter; +import io.swagger.models.properties.ArrayProperty; +import io.swagger.models.properties.Property; +import io.swagger.models.properties.RefProperty; +import io.swagger.models.properties.StringProperty; +import io.swagger.util.Json; +import io.swagger.util.ParameterProcessor; + +public class JaxRs2Extension extends AbstractSwaggerExtension { + + private final ObjectMapper mapper = Json.mapper(); + + @Override + public List<Parameter> extractParameters( + final List<Annotation> annotations, + final Type type, + final Set<Type> typesToSkip, + final Iterator<SwaggerExtension> chain) { + + if (shouldIgnoreType(type, typesToSkip)) { + return new ArrayList<>(); + } + + List<Parameter> parameters = new ArrayList<>(); + for (Annotation annotation : annotations) { + if (annotation instanceof MatrixParam) { + MatrixParam param = (MatrixParam) annotation; + MatrixParameter mp = new MatrixParameter().name(param.value()); + + Property schema = createProperty(type); + if (schema != null) { + mp.setProperty(schema); + } + applyBeanValidatorAnnotations(mp, annotations); + parameters.add(mp); + } else if (annotation instanceof BeanParam) { + // Use Jackson's logic for processing Beans + final BeanDescription beanDesc = mapper.getSerializationConfig().introspect(constructType(type)); + final List<BeanPropertyDefinition> properties = beanDesc.findProperties(); + + for (final BeanPropertyDefinition propDef : properties) { + final AnnotatedField field = propDef.getField(); + final AnnotatedMethod setter = propDef.getSetter(); + final List<Annotation> paramAnnotations = new ArrayList<>(); + final Iterator<SwaggerExtension> extensions = SwaggerExtensions.chain(); + Type paramType = null; + + // Gather the field's details + if (field != null) { + paramType = field.getGenericType(); + + for (final Annotation fieldAnnotation : field.annotations()) { + if (!paramAnnotations.contains(fieldAnnotation)) { + paramAnnotations.add(fieldAnnotation); + } + } + } + + // Gather the setter's details but only the ones we need + if (setter != null) { + // Do not set the param class/type from the setter if the values are already identified + if (paramType == null && setter.getGenericParameterTypes() != null) { + paramType = setter.getGenericParameterTypes()[0]; + } + + for (final Annotation fieldAnnotation : setter.annotations()) { + if (!paramAnnotations.contains(fieldAnnotation)) { + paramAnnotations.add(fieldAnnotation); + } + } + } + + // Re-process all Bean fields and let the default swagger-jaxrs processor do its thing + List<Parameter> extracted = + extensions.next().extractParameters(paramAnnotations, paramType, typesToSkip, extensions); + + // since downstream processors won't know how to introspect @BeanParam, process here + for (Parameter param : extracted) { + if (ParameterProcessor.applyAnnotations(null, param, paramType, paramAnnotations) != null) { + applyBeanValidatorAnnotations(param, paramAnnotations); + parameters.add(param); + } + } + } + } + } + + // Only call down to the other items in the chain if no parameters were produced + if (parameters.isEmpty()) { + parameters = super.extractParameters(annotations, type, typesToSkip, chain); + } + + return parameters; + } + + private Property createProperty(final Type type) { + return enforcePrimitive(ModelConverters.getInstance().readAsProperty(type), 0); + } + + private Property enforcePrimitive(final Property in, final int level) { + if (in instanceof RefProperty) { + return new StringProperty(); + } + if (in instanceof ArrayProperty) { + if (level == 0) { + final ArrayProperty array = (ArrayProperty) in; + array.setItems(enforcePrimitive(array.getItems(), level + 1)); + } else { + return new StringProperty(); + } + } + return in; + } + + /** + * This is essentially a duplicate of {@link io.swagger.jackson.ModelResolver.applyBeanValidatorAnnotations}. + * + * @param parameter + * @param annotations + */ + private void applyBeanValidatorAnnotations(final Parameter parameter, final List<Annotation> annotations) { + Map<String, Annotation> annos = new HashMap<>(); + if (annotations != null) { + for (Annotation annotation : annotations) { + annos.put(annotation.annotationType().getName(), annotation); + } + } + + if (annos.containsKey(NotNull.class.getName())) { + parameter.setRequired(true); + } + + if (parameter instanceof AbstractSerializableParameter) { + AbstractSerializableParameter<?> serializable = (AbstractSerializableParameter<?>) parameter; + + if (annos.containsKey(Min.class.getName())) { + Min min = (Min) annos.get(Min.class.getName()); + serializable.setMinimum(new Double(min.value())); + } + if (annos.containsKey(Max.class.getName())) { + Max max = (Max) annos.get(Max.class.getName()); + serializable.setMaximum(new Double(max.value())); + } + if (annos.containsKey(Size.class.getName())) { + Size size = (Size) annos.get(Size.class.getName()); + + serializable.setMinimum(new Double(size.min())); + serializable.setMaximum(new Double(size.max())); + + serializable.setMinItems(size.min()); + serializable.setMaxItems(size.max()); + } + if (annos.containsKey(DecimalMin.class.getName())) { + DecimalMin min = (DecimalMin) annos.get(DecimalMin.class.getName()); + if (min.inclusive()) { + serializable.setMinimum(new Double(min.value())); + } else { + serializable.setExclusiveMinimum(!min.inclusive()); + } + } + if (annos.containsKey(DecimalMax.class.getName())) { + DecimalMax max = (DecimalMax) annos.get(DecimalMax.class.getName()); + if (max.inclusive()) { + serializable.setMaximum(new Double(max.value())); + } else { + serializable.setExclusiveMaximum(!max.inclusive()); + } + } + if (annos.containsKey(Pattern.class.getName())) { + Pattern pattern = (Pattern) annos.get(Pattern.class.getName()); + serializable.setPattern(pattern.regexp()); + } + } + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/636252c2/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/MatrixParameter.java ---------------------------------------------------------------------- diff --git a/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/MatrixParameter.java b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/MatrixParameter.java new file mode 100644 index 0000000..f1472c2 --- /dev/null +++ b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/MatrixParameter.java @@ -0,0 +1,28 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.jaxrs.swagger; + +import io.swagger.models.parameters.AbstractSerializableParameter; + +public class MatrixParameter extends AbstractSerializableParameter<MatrixParameter> { + + public MatrixParameter() { + super.setIn("matrix"); + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/636252c2/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Feature.java ---------------------------------------------------------------------- diff --git a/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Feature.java b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Feature.java new file mode 100644 index 0000000..bc9102f --- /dev/null +++ b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Feature.java @@ -0,0 +1,259 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.jaxrs.swagger; + +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import javax.servlet.ServletContext; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.PreMatching; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.apache.cxf.annotations.Provider; +import org.apache.cxf.annotations.Provider.Type; +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.endpoint.Server; +import org.apache.cxf.jaxrs.JAXRSServiceFactoryBean; +import org.apache.cxf.jaxrs.ext.MessageContext; +import org.apache.cxf.jaxrs.model.ClassResourceInfo; +import org.apache.cxf.jaxrs.model.doc.DocumentationProvider; +import org.apache.cxf.jaxrs.model.doc.JavaDocProvider; +import org.apache.cxf.jaxrs.provider.ServerProviderFactory; +import org.apache.cxf.jaxrs.utils.InjectionUtils; +import org.apache.cxf.jaxrs.utils.JAXRSUtils; + +import io.swagger.jaxrs.config.BeanConfig; +import io.swagger.jaxrs.config.DefaultReaderConfig; +import io.swagger.jaxrs.config.ReaderConfig; +import io.swagger.jaxrs.listing.ApiListingResource; + +@Provider(Type.Feature) +public class Swagger2Feature extends AbstractSwaggerFeature { + + protected boolean dynamicBasePath; + + protected boolean replaceTags; + + protected DocumentationProvider javadocProvider; + + private String host; + + private String[] schemes; + + private boolean prettyPrint; + + private boolean scanAllResources; + + private String ignoreRoutes; + + @Override + protected void addSwaggerResource(Server server) { + ApiListingResource apiListingResource = new ApiListingResource(); + JAXRSServiceFactoryBean sfb = + (JAXRSServiceFactoryBean) server.getEndpoint().get(JAXRSServiceFactoryBean.class.getName()); + sfb.setResourceClassesFromBeans(Collections.<Object>singletonList(apiListingResource)); + List<ClassResourceInfo> cris = sfb.getClassResourceInfo(); + + List<Object> providers = new ArrayList<>(); + if (runAsFilter) { + providers.add(new SwaggerContainerRequestFilter()); + } else { + for (ClassResourceInfo cri : cris) { + if (ApiListingResource.class == cri.getResourceClass()) { + InjectionUtils.injectContextProxies(cri, apiListingResource); + } + } + } + providers.add(new Swagger2Serializers(dynamicBasePath, replaceTags, javadocProvider, cris)); + providers.add(new ReaderConfigFilter()); + ((ServerProviderFactory) server.getEndpoint().get( + ServerProviderFactory.class.getName())).setUserProviders(providers); + + BeanConfig beanConfig = new BeanConfig(); + beanConfig.setResourcePackage(getResourcePackage()); + beanConfig.setVersion(getVersion()); + beanConfig.setBasePath(getBasePath()); + beanConfig.setHost(getHost()); + beanConfig.setSchemes(getSchemes()); + beanConfig.setTitle(getTitle()); + beanConfig.setDescription(getDescription()); + beanConfig.setContact(getContact()); + beanConfig.setLicense(getLicense()); + beanConfig.setLicenseUrl(getLicenseUrl()); + beanConfig.setTermsOfServiceUrl(getTermsOfServiceUrl()); + beanConfig.setScan(isScan()); + beanConfig.setPrettyPrint(isPrettyPrint()); + beanConfig.setFilterClass(getFilterClass()); + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public String[] getSchemes() { + return schemes; + } + + public void setSchemes(String[] schemes) { + this.schemes = schemes; + } + + public boolean isPrettyPrint() { + return prettyPrint; + } + + public void setPrettyPrint(boolean prettyPrint) { + this.prettyPrint = prettyPrint; + } + + public boolean isScanAllResources() { + return scanAllResources; + } + + public void setScanAllResources(boolean scanAllResources) { + this.scanAllResources = scanAllResources; + } + + public String getIgnoreRoutes() { + return ignoreRoutes; + } + + public void setIgnoreRoutes(String ignoreRoutes) { + this.ignoreRoutes = ignoreRoutes; + } + + public void setDynamicBasePath(final boolean dynamicBasePath) { + this.dynamicBasePath = dynamicBasePath; + } + + public void setReplaceTags(final boolean replaceTags) { + this.replaceTags = replaceTags; + } + + public void setJavaDocPath(final String javaDocPath) throws Exception { + this.javadocProvider = new JavaDocProvider(javaDocPath); + } + + public void setJavaDocPaths(final String... javaDocPaths) throws Exception { + this.javadocProvider = new JavaDocProvider(javaDocPaths); + } + + public void setJavaDocURLs(final URL[] javaDocURLs) { + this.javadocProvider = new JavaDocProvider(javaDocURLs); + } + + @Override + protected void setBasePathByAddress(String address) { + if (!address.startsWith("/")) { + // get the path part + URI u = URI.create(address); + setBasePath(u.getPath()); + setHost(u.getPort() < 0 ? u.getHost() : u.getHost() + ":" + u.getPort()); + } else { + setBasePath(address); + } + } + + @PreMatching + protected static class SwaggerContainerRequestFilter extends ApiListingResource implements ContainerRequestFilter { + + protected static final MediaType APPLICATION_YAML_TYPE = JAXRSUtils.toMediaType("application/yaml"); + + protected static final String APIDOCS_LISTING_PATH = "swagger"; + + protected static final String APIDOCS_LISTING_PATH_JSON = APIDOCS_LISTING_PATH + ".json"; + + protected static final String APIDOCS_LISTING_PATH_YAML = APIDOCS_LISTING_PATH + ".yaml"; + + @Context + protected MessageContext mc; + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + UriInfo ui = mc.getUriInfo(); + List<MediaType> mediaTypes = mc.getHttpHeaders().getAcceptableMediaTypes(); + + Response response = null; + if ((ui.getPath().endsWith(APIDOCS_LISTING_PATH) + && !JAXRSUtils.intersectMimeTypes(mediaTypes, MediaType.APPLICATION_JSON_TYPE).isEmpty()) + || ui.getPath().endsWith(APIDOCS_LISTING_PATH_JSON)) { + + response = getListingJsonResponse( + null, mc.getServletContext(), mc.getServletConfig(), mc.getHttpHeaders(), ui); + } else if ((ui.getPath().endsWith(APIDOCS_LISTING_PATH) + && !JAXRSUtils.intersectMimeTypes(mediaTypes, APPLICATION_YAML_TYPE).isEmpty()) + || ui.getPath().endsWith(APIDOCS_LISTING_PATH_YAML)) { + + response = getListingYamlResponse( + null, mc.getServletContext(), mc.getServletConfig(), mc.getHttpHeaders(), ui); + } + + if (response != null) { + requestContext.abortWith(response); + } + } + } + + protected class ReaderConfigFilter implements ContainerRequestFilter { + + @Context + protected MessageContext mc; + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + ServletContext servletContext = mc.getServletContext(); + if (servletContext != null && servletContext.getAttribute(ReaderConfig.class.getName()) == null) { + if (mc.getServletConfig() != null + && Boolean.valueOf(mc.getServletConfig().getInitParameter("scan.all.resources"))) { + addReaderConfig(mc.getServletConfig().getInitParameter("ignore.routes")); + } else if (isScanAllResources()) { + addReaderConfig(getIgnoreRoutes()); + } + } + } + + protected void addReaderConfig(String ignoreRoutesParam) { + DefaultReaderConfig rc = new DefaultReaderConfig(); + rc.setScanAllResources(true); + if (ignoreRoutesParam != null) { + Set<String> routes = new LinkedHashSet<>(); + for (String route : StringUtils.split(ignoreRoutesParam, ",")) { + routes.add(route.trim()); + } + rc.setIgnoredRoutes(routes); + } + mc.getServletContext().setAttribute(ReaderConfig.class.getName(), rc); + } + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/636252c2/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Serializers.java ---------------------------------------------------------------------- diff --git a/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Serializers.java b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Serializers.java new file mode 100644 index 0000000..e6f7be2 --- /dev/null +++ b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Serializers.java @@ -0,0 +1,164 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.jaxrs.swagger; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.cxf.jaxrs.ext.MessageContext; +import org.apache.cxf.jaxrs.model.ClassResourceInfo; +import org.apache.cxf.jaxrs.model.OperationResourceInfo; +import org.apache.cxf.jaxrs.model.doc.DocumentationProvider; +import org.apache.cxf.jaxrs.utils.JAXRSUtils; + +import io.swagger.jaxrs.listing.SwaggerSerializers; +import io.swagger.models.HttpMethod; +import io.swagger.models.Operation; +import io.swagger.models.Path; +import io.swagger.models.Swagger; +import io.swagger.models.Tag; + +public class Swagger2Serializers extends SwaggerSerializers { + + protected final boolean dynamicBasePath; + + protected final boolean replaceTags; + + protected final DocumentationProvider javadocProvider; + + protected final List<ClassResourceInfo> cris; + + public Swagger2Serializers( + final boolean dynamicBasePath, + final boolean replaceTags, + final DocumentationProvider javadocProvider, + final List<ClassResourceInfo> cris) { + + super(); + + this.dynamicBasePath = dynamicBasePath; + this.replaceTags = replaceTags; + this.javadocProvider = javadocProvider; + this.cris = cris; + } + + @Override + public void writeTo( + final Swagger data, + final Class<?> type, + final Type genericType, + final Annotation[] annotations, + final MediaType mediaType, + final MultivaluedMap<String, Object> headers, + final OutputStream out) throws IOException { + + if (dynamicBasePath) { + MessageContext ctx = JAXRSUtils.createContextValue( + JAXRSUtils.getCurrentMessage(), null, MessageContext.class); + data.setBasePath(StringUtils.substringBeforeLast(ctx.getHttpServletRequest().getRequestURI(), "/")); + } + + if (replaceTags || javadocProvider != null) { + Map<String, ClassResourceInfo> operations = new HashMap<>(); + Map<Pair<String, String>, OperationResourceInfo> methods = new HashMap<>(); + for (ClassResourceInfo cri : cris) { + for (OperationResourceInfo ori : cri.getMethodDispatcher().getOperationResourceInfos()) { + String normalizedPath = getNormalizedPath( + cri.getURITemplate().getValue(), ori.getURITemplate().getValue()); + + operations.put(normalizedPath, cri); + methods.put(ImmutablePair.of(ori.getHttpMethod(), normalizedPath), ori); + } + } + + if (replaceTags && data.getTags() != null) { + data.getTags().clear(); + } + for (final Map.Entry<String, Path> entry : data.getPaths().entrySet()) { + Tag tag = null; + if (replaceTags && operations.containsKey(entry.getKey())) { + ClassResourceInfo cri = operations.get(entry.getKey()); + + tag = new Tag(); + tag.setName(cri.getURITemplate().getValue()); + if (javadocProvider != null) { + tag.setDescription(javadocProvider.getClassDoc(cri)); + } + + data.addTag(tag); + } + + for (Map.Entry<HttpMethod, Operation> subentry : entry.getValue().getOperationMap().entrySet()) { + if (replaceTags && tag != null) { + subentry.getValue().setTags(Collections.singletonList(tag.getName())); + } + + Pair<String, String> key = ImmutablePair.of(subentry.getKey().name(), entry.getKey()); + if (methods.containsKey(key) && javadocProvider != null) { + OperationResourceInfo ori = methods.get(key); + + subentry.getValue().setSummary(javadocProvider.getMethodDoc(ori)); + for (int i = 0; i < subentry.getValue().getParameters().size(); i++) { + subentry.getValue().getParameters().get(i). + setDescription(javadocProvider.getMethodParameterDoc(ori, i)); + } + + if (subentry.getValue().getResponses() != null + && !subentry.getValue().getResponses().isEmpty()) { + + subentry.getValue().getResponses().entrySet().iterator().next().getValue(). + setDescription(javadocProvider.getMethodResponseDoc(ori)); + } + } + } + } + } + + super.writeTo(data, type, genericType, annotations, mediaType, headers, out); + } + + protected String getNormalizedPath(String classResourcePath, String operationResourcePath) { + StringBuilder normalizedPath = new StringBuilder(); + + String[] segments = StringUtils.split(classResourcePath + operationResourcePath, "/"); + for (String segment : segments) { + if (!StringUtils.isEmpty(segment)) { + normalizedPath.append("/").append(segment); + } + } + // Adapt to Swagger's path expression + if (normalizedPath.toString().endsWith(":.*}")) { + normalizedPath.setLength(normalizedPath.length() - 4); + normalizedPath.append('}'); + } + return StringUtils.EMPTY.equals(normalizedPath.toString()) ? "/" : normalizedPath.toString(); + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/636252c2/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/SwaggerFeature.java ---------------------------------------------------------------------- diff --git a/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/SwaggerFeature.java b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/SwaggerFeature.java new file mode 100644 index 0000000..0a4162b --- /dev/null +++ b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/SwaggerFeature.java @@ -0,0 +1,115 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.jaxrs.swagger; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.PreMatching; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import com.wordnik.swagger.jaxrs.config.BeanConfig; +import com.wordnik.swagger.jaxrs.listing.ApiDeclarationProvider; +import com.wordnik.swagger.jaxrs.listing.ApiListingResourceJSON; +import com.wordnik.swagger.jaxrs.listing.ResourceListingProvider; + +import org.apache.cxf.endpoint.Server; +import org.apache.cxf.jaxrs.JAXRSServiceFactoryBean; +import org.apache.cxf.jaxrs.ext.MessageContext; +import org.apache.cxf.jaxrs.provider.ServerProviderFactory; + +public class SwaggerFeature extends AbstractSwaggerFeature { + + @Override + protected void addSwaggerResource(Server server) { + ApiListingResourceJSON apiListingResource = new ApiListingResourceJSON(); + if (!runAsFilter) { + List<Object> serviceBeans = new ArrayList<Object>(); + serviceBeans.add(apiListingResource); + ((JAXRSServiceFactoryBean)server.getEndpoint().get(JAXRSServiceFactoryBean.class.getName())). + setResourceClassesFromBeans(serviceBeans); + } + List<Object> providers = new ArrayList<Object>(); + if (runAsFilter) { + providers.add(new SwaggerContainerRequestFilter(apiListingResource)); + } + providers.add(new ResourceListingProvider()); + providers.add(new ApiDeclarationProvider()); + ((ServerProviderFactory)server.getEndpoint().get( + ServerProviderFactory.class.getName())).setUserProviders(providers); + + BeanConfig beanConfig = new BeanConfig(); + beanConfig.setResourcePackage(getResourcePackage()); + beanConfig.setVersion(getVersion()); + beanConfig.setBasePath(getBasePath()); + beanConfig.setTitle(getTitle()); + beanConfig.setDescription(getDescription()); + beanConfig.setContact(getContact()); + beanConfig.setLicense(getLicense()); + beanConfig.setLicenseUrl(getLicenseUrl()); + beanConfig.setTermsOfServiceUrl(getTermsOfServiceUrl()); + beanConfig.setScan(isScan()); + beanConfig.setFilterClass(getFilterClass()); + } + + @Override + protected void setBasePathByAddress(String address) { + setBasePath(address); + } + + @PreMatching + private static class SwaggerContainerRequestFilter implements ContainerRequestFilter { + private static final String APIDOCS_LISTING_PATH = "api-docs"; + private static final Pattern APIDOCS_RESOURCE_PATH = Pattern.compile(APIDOCS_LISTING_PATH + "(/.+)"); + + private ApiListingResourceJSON apiListingResource; + @Context + private MessageContext mc; + SwaggerContainerRequestFilter(ApiListingResourceJSON apiListingResource) { + this.apiListingResource = apiListingResource; + } + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + UriInfo ui = mc.getUriInfo(); + if (ui.getPath().endsWith(APIDOCS_LISTING_PATH)) { + Response r = + apiListingResource.resourceListing(null, mc.getServletConfig(), mc.getHttpHeaders(), ui); + requestContext.abortWith(r); + } else { + final Matcher matcher = APIDOCS_RESOURCE_PATH.matcher(ui.getPath()); + + if (matcher.find()) { + Response r = + apiListingResource.apiDeclaration(matcher.group(1), + null, mc.getServletConfig(), mc.getHttpHeaders(), ui); + requestContext.abortWith(r); + } + } + } + + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/636252c2/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/SwaggerUtils.java ---------------------------------------------------------------------- diff --git a/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/SwaggerUtils.java b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/SwaggerUtils.java new file mode 100644 index 0000000..2438458 --- /dev/null +++ b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/SwaggerUtils.java @@ -0,0 +1,233 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.jaxrs.swagger; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; + +import org.apache.cxf.Bus; +import org.apache.cxf.BusFactory; +import org.apache.cxf.common.classloader.ClassLoaderUtils; +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.helpers.CastUtils; +import org.apache.cxf.helpers.IOUtils; +import org.apache.cxf.jaxrs.json.basic.JsonMapObjectReaderWriter; +import org.apache.cxf.jaxrs.model.Parameter; +import org.apache.cxf.jaxrs.model.ParameterType; +import org.apache.cxf.jaxrs.model.UserOperation; +import org.apache.cxf.jaxrs.model.UserResource; +import org.apache.cxf.jaxrs.utils.ResourceUtils; + +public final class SwaggerUtils { + private static final Logger LOG = LogUtils.getL7dLogger(ResourceUtils.class); + private static final Map<String, String> SWAGGER_TYPE_MAP; + static { + SWAGGER_TYPE_MAP = new HashMap<String, String>(); + SWAGGER_TYPE_MAP.put("string", "String"); + SWAGGER_TYPE_MAP.put("integer", "long"); + SWAGGER_TYPE_MAP.put("float", "float"); + SWAGGER_TYPE_MAP.put("double", "double"); + SWAGGER_TYPE_MAP.put("int", "int"); + SWAGGER_TYPE_MAP.put("long", "long"); + SWAGGER_TYPE_MAP.put("byte", "byte"); + SWAGGER_TYPE_MAP.put("boolean", "boolean"); + SWAGGER_TYPE_MAP.put("date", "java.util.Date"); + SWAGGER_TYPE_MAP.put("dateTime", "java.util.Date"); + SWAGGER_TYPE_MAP.put("File", "java.io.InputStream"); + SWAGGER_TYPE_MAP.put("file", "java.io.InputStream"); + } + private SwaggerUtils() { + + } + public static UserResource getUserResource(String loc) { + return getUserResource(loc, BusFactory.getThreadDefaultBus()); + } + public static UserResource getUserResource(String loc, Bus bus) { + try { + InputStream is = ResourceUtils.getResourceStream(loc, bus); + if (is == null) { + return null; + } + return getUserResourceFromJson(IOUtils.readStringFromStream(is)); + } catch (Exception ex) { + LOG.warning("Problem with processing a user model at " + loc); + } + return null; + } + public static List<UserResource> getUserResourcesFromResourceObjects(List<String> jsonObjects) { + List<UserResource> resources = new ArrayList<UserResource>(); + for (String json : jsonObjects) { + resources.add(getUserResourceFromJson(json)); + } + return resources; + } + public static UserResource getUserResourceFromJson(String json) { + JsonMapObjectReaderWriter reader = new JsonMapObjectReaderWriter(); + Map<String, Object> map = reader.fromJson(json); + + if (map.containsKey("swaggerVersion")) { + return getUserResourceFromSwagger12(map); + } else { + return getUserResourceFromSwagger20(map); + } + + } + private static UserResource getUserResourceFromSwagger20(Map<String, Object> map) { + UserResource ur = new UserResource(); + String relativePath = (String)map.get("basePath"); + ur.setPath(relativePath == null ? "/" : relativePath); + + List<String> resourceProduces = CastUtils.cast((List<?>)map.get("produces")); + ur.setProduces(listToString(resourceProduces)); + + List<String> resourceConsumes = CastUtils.cast((List<?>)map.get("consumes")); + ur.setConsumes(listToString(resourceConsumes)); + + List<UserOperation> userOps = new LinkedList<UserOperation>(); + Map<String, Map<String, Object>> paths = CastUtils.cast((Map<?, ?>)map.get("paths")); + for (Map.Entry<String, Map<String, Object>> pathEntry : paths.entrySet()) { + + String operPath = pathEntry.getKey(); + + Map<String, Object> operations = pathEntry.getValue(); + for (Map.Entry<String, Object> operEntry : operations.entrySet()) { + UserOperation userOp = new UserOperation(); + userOp.setVerb(operEntry.getKey().toUpperCase()); + userOp.setPath(operPath); + + Map<String, Object> oper = CastUtils.cast((Map<?, ?>)operEntry.getValue()); + + userOp.setName((String)oper.get("operationId")); + List<String> opProduces = CastUtils.cast((List<?>)oper.get("produces")); + userOp.setProduces(listToString(opProduces)); + + List<String> opConsumes = CastUtils.cast((List<?>)oper.get("consumes")); + userOp.setConsumes(listToString(opConsumes)); + + List<Parameter> userOpParams = new LinkedList<Parameter>(); + List<Map<String, Object>> params = CastUtils.cast((List<?>)oper.get("parameters")); + for (Map<String, Object> param : params) { + String name = (String)param.get("name"); + //"query", "header", "path", "formData" or "body" + String paramType = (String)param.get("in"); + ParameterType pType = "body".equals(paramType) ? ParameterType.REQUEST_BODY + : "formData".equals(paramType) + ? ParameterType.FORM : ParameterType.valueOf(paramType.toUpperCase()); + Parameter userParam = new Parameter(pType, name); + + setJavaType(userParam, (String)param.get("type")); + + userOpParams.add(userParam); + } + if (!userOpParams.isEmpty()) { + userOp.setParameters(userOpParams); + } + userOps.add(userOp); + } + } + ur.setOperations(userOps); + return ur; + } + private static UserResource getUserResourceFromSwagger12(Map<String, Object> map) { + UserResource ur = new UserResource(); + String relativePath = (String)map.get("resourcePath"); + ur.setPath(relativePath == null ? "/" : relativePath); + + List<String> resourceProduces = CastUtils.cast((List<?>)map.get("produces")); + ur.setProduces(listToString(resourceProduces)); + + List<String> resourceConsumes = CastUtils.cast((List<?>)map.get("consumes")); + ur.setConsumes(listToString(resourceConsumes)); + + List<UserOperation> userOps = new LinkedList<UserOperation>(); + List<Map<String, Object>> apis = CastUtils.cast((List<?>)map.get("apis")); + for (Map<String, Object> api : apis) { + String operPath = (String)api.get("path"); + if (relativePath != null && operPath.startsWith(relativePath) + && operPath.length() > relativePath.length()) { + // relative resource and operation paths overlap in Swagger 1.2 + operPath = operPath.substring(relativePath.length()); + } + + List<Map<String, Object>> operations = CastUtils.cast((List<?>)api.get("operations")); + for (Map<String, Object> oper : operations) { + UserOperation userOp = new UserOperation(); + userOp.setPath(operPath); + userOp.setName((String)oper.get("nickname")); + userOp.setVerb((String)oper.get("method")); + + List<String> opProduces = CastUtils.cast((List<?>)oper.get("produces")); + userOp.setProduces(listToString(opProduces)); + + List<String> opConsumes = CastUtils.cast((List<?>)oper.get("consumes")); + userOp.setConsumes(listToString(opConsumes)); + + List<Parameter> userOpParams = new LinkedList<Parameter>(); + List<Map<String, Object>> params = CastUtils.cast((List<?>)oper.get("parameters")); + for (Map<String, Object> param : params) { + String name = (String)param.get("name"); + //"path", "query", "body", "header", "form" + String paramType = (String)param.get("paramType"); + ParameterType pType = "body".equals(paramType) + ? ParameterType.REQUEST_BODY : ParameterType.valueOf(paramType.toUpperCase()); + Parameter userParam = new Parameter(pType, name); + setJavaType(userParam, (String)param.get("type")); + + userOpParams.add(userParam); + } + if (!userOpParams.isEmpty()) { + userOp.setParameters(userOpParams); + } + userOps.add(userOp); + } + } + ur.setOperations(userOps); + return ur; + } + private static void setJavaType(Parameter userParam, String typeName) { + String javaTypeName = SWAGGER_TYPE_MAP.get(typeName); + if (javaTypeName != null) { + try { + userParam.setJavaType(ClassLoaderUtils.loadClass(javaTypeName, SwaggerUtils.class)); + } catch (Throwable t) { + // ignore - can be a reference to a JSON model class, etc + } + } + + } + private static String listToString(List<String> list) { + if (list != null) { + StringBuilder sb = new StringBuilder(); + for (String s : list) { + if (sb.length() > 0) { + sb.append(','); + } + sb.append(s); + } + return sb.toString(); + } else { + return null; + } + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/636252c2/rt/rs/description-swagger/src/main/resources/META-INF/services/io.swagger.jaxrs.ext.SwaggerExtension ---------------------------------------------------------------------- diff --git a/rt/rs/description-swagger/src/main/resources/META-INF/services/io.swagger.jaxrs.ext.SwaggerExtension b/rt/rs/description-swagger/src/main/resources/META-INF/services/io.swagger.jaxrs.ext.SwaggerExtension new file mode 100644 index 0000000..9803485 --- /dev/null +++ b/rt/rs/description-swagger/src/main/resources/META-INF/services/io.swagger.jaxrs.ext.SwaggerExtension @@ -0,0 +1 @@ +org.apache.cxf.jaxrs.swagger.JaxRs2Extension http://git-wip-us.apache.org/repos/asf/cxf/blob/636252c2/rt/rs/description-swagger/src/test/java/org/apache/cxf/jaxrs/swagger/Swagger2FeatureTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/description-swagger/src/test/java/org/apache/cxf/jaxrs/swagger/Swagger2FeatureTest.java b/rt/rs/description-swagger/src/test/java/org/apache/cxf/jaxrs/swagger/Swagger2FeatureTest.java new file mode 100644 index 0000000..c87b041 --- /dev/null +++ b/rt/rs/description-swagger/src/test/java/org/apache/cxf/jaxrs/swagger/Swagger2FeatureTest.java @@ -0,0 +1,53 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.jaxrs.swagger; + +import org.junit.Assert; +import org.junit.Test; + +/** + * + */ +public class Swagger2FeatureTest extends Assert { + @Test + public void testSetBasePathByAddress() { + Swagger2Feature f = new Swagger2Feature(); + + f.setBasePathByAddress("http://localhost:8080/foo"); + assertEquals("/foo", f.getBasePath()); + assertEquals("localhost:8080", f.getHost()); + unsetBasePath(f); + + f.setBasePathByAddress("http://localhost/foo"); + assertEquals("/foo", f.getBasePath()); + assertEquals("localhost", f.getHost()); + unsetBasePath(f); + + f.setBasePathByAddress("/foo"); + assertEquals("/foo", f.getBasePath()); + assertNull(f.getHost()); + unsetBasePath(f); + } + + private static void unsetBasePath(Swagger2Feature f) { + f.setBasePath(null); + f.setHost(null); + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/636252c2/rt/rs/description-swagger/src/test/java/org/apache/cxf/jaxrs/swagger/SwaggerFeatureTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/description-swagger/src/test/java/org/apache/cxf/jaxrs/swagger/SwaggerFeatureTest.java b/rt/rs/description-swagger/src/test/java/org/apache/cxf/jaxrs/swagger/SwaggerFeatureTest.java new file mode 100644 index 0000000..a1922d5 --- /dev/null +++ b/rt/rs/description-swagger/src/test/java/org/apache/cxf/jaxrs/swagger/SwaggerFeatureTest.java @@ -0,0 +1,45 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.jaxrs.swagger; + +import org.junit.Assert; +import org.junit.Test; + +/** + * + */ +public class SwaggerFeatureTest extends Assert { + @Test + public void testSetBasePathByAddress() { + SwaggerFeature f = new SwaggerFeature(); + + f.setBasePathByAddress("http://localhost:8080/foo"); + assertEquals("http://localhost:8080/foo", f.getBasePath()); + unsetBasePath(f); + + f.setBasePathByAddress("/foo"); + assertEquals("/foo", f.getBasePath()); + unsetBasePath(f); + } + + private static void unsetBasePath(SwaggerFeature f) { + f.setBasePath(null); + } +}
