Build Jersey on JDK13 (#4341) * Able to compile with jdk13; Added in new doclet Signed-off-by: Jorge Bescos Gascon <jorge.bescos.gascon@oracle.com>
diff --git a/.gitignore b/.gitignore index 367c54d..9f7d5b9 100644 --- a/.gitignore +++ b/.gitignore
@@ -1,5 +1,6 @@ # maven noise target/ +target-*/ # gradle noise .gradle
diff --git a/.travis.yml b/.travis.yml index de1dd18..7ab7480 100644 --- a/.travis.yml +++ b/.travis.yml
@@ -10,6 +10,7 @@ jdk: - oraclejdk8 - openjdk11 + - openjdk13 cache: directories:
diff --git a/ext/wadl-doclet/pom.xml b/ext/wadl-doclet/pom.xml index c64f88b..d1d4a01 100644 --- a/ext/wadl-doclet/pom.xml +++ b/ext/wadl-doclet/pom.xml
@@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v. 2.0, which is available at @@ -32,6 +32,14 @@ of resource classes, so that this can be used for extending generated wadl with useful documentation. </description> + + <properties> + <java.sourceDirectory>${project.basedir}/src/main/java</java.sourceDirectory> + <java8_11.build.outputDirectory>${project.basedir}/target</java8_11.build.outputDirectory> + <java8_11.sourceDirectory>${project.basedir}/src/main/java8_11</java8_11.sourceDirectory> + <java12.build.outputDirectory>${project.basedir}/target-java12</java12.build.outputDirectory> + <java12.sourceDirectory>${project.basedir}/src/main/java12</java12.sourceDirectory> + </properties> <profiles> @@ -66,6 +74,134 @@ </dependencies> </profile> <profile> + <id>jdk1.8_11</id> + <activation> + <jdk>[1.8,12)</jdk> + </activation> + <build> + <directory>${java8_11.build.outputDirectory}</directory> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <phase>generate-sources</phase> + <goals> + <goal>add-source</goal> + </goals> + <configuration> + <sources> + <source>${java.sourceDirectory}</source> + <source>${java8_11.sourceDirectory}</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>jdk12+</id> + <activation> + <jdk>[12,)</jdk> + </activation> + <build> + <directory>${java12.build.outputDirectory}</directory> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <phase>generate-sources</phase> + <goals> + <goal>add-source</goal> + </goals> + <configuration> + <sources> + <source>${java.sourceDirectory}</source> + <source>${java12.sourceDirectory}</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + <!-- Only multi-release is supported when compiling from higher version to lower --> + <profile> + <id>copyJDK12FilesToMultiReleaseJar</id> + <activation> + <file> + <!-- ${java12.build.outputDirectory} does not work here --> + <exists>target-java12/classes/org/glassfish/jersey/wadl/doclet/ResourceDoclet.class</exists> + </file> + <jdk>[1.8,12)</jdk> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <inherited>true</inherited> + <extensions>true</extensions> + <configuration> + <instructions> + <Multi-Release>true</Multi-Release> + </instructions> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-resources-plugin</artifactId> + <inherited>true</inherited> + <executions> + <execution> + <id>copy-jdk12-classes</id> + <phase>prepare-package</phase> + <goals> + <goal>copy-resources</goal> + </goals> + <configuration> + <outputDirectory>${java8_11.build.outputDirectory}/classes/META-INF/versions/12</outputDirectory> + <resources> + <resource> + <directory>${java12.build.outputDirectory}/classes</directory> + </resource> + </resources> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-antrun-plugin</artifactId> + <executions> + <execution> + <id>copy-jdk12-sources</id> + <phase>verify</phase> + <configuration> + <target> + <property name="sources-jar" value="${java8_11.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/> + <echo>sources-jar: ${sources-jar}</echo> + <zip destfile="${sources-jar}" update="true"> + <zipfileset dir="${java12.sourceDirectory}" prefix="META-INF/versions/12"/> + </zip> + </target> + </configuration> + <goals> + <goal>run</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + <profile> <id>tools.jar</id> <activation> <!-- Activation should be done with <file>${java.home}/../lib/tools.jar</file But this is not working with maven as the property is not expansed. --> @@ -105,4 +241,5 @@ </resource> </resources> </build> + </project>
diff --git a/ext/wadl-doclet/src/main/java/org/glassfish/jersey/wadl/doclet/DocletUtils.java b/ext/wadl-doclet/src/main/java/org/glassfish/jersey/wadl/doclet/DocletUtils.java new file mode 100644 index 0000000..4dea216 --- /dev/null +++ b/ext/wadl-doclet/src/main/java/org/glassfish/jersey/wadl/doclet/DocletUtils.java
@@ -0,0 +1,160 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.wadl.doclet; + +import com.sun.org.apache.xml.internal.serialize.OutputFormat; +import com.sun.org.apache.xml.internal.serialize.XMLSerializer; + +import java.io.BufferedOutputStream; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.io.StringWriter; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.Marshaller; + +import org.glassfish.jersey.server.wadl.internal.generators.resourcedoc.model.ResourceDocType; + +class DocletUtils { + + private static final Logger LOG = Logger.getLogger(DocletUtils.class.getName()); + + private static String[] getCDataElements(DocProcessor docProcessor) { + String[] original = new String[]{"ns1^commentText", "ns2^commentText", "^commentText" }; + if (docProcessor == null) { + return original; + } else { + String[] cdataElements = docProcessor.getCDataElements(); + if (cdataElements == null || cdataElements.length == 0) { + return original; + } else { + + String[] result = copyOf(original, original.length + cdataElements.length); + for (int i = 0; i < cdataElements.length; i++) { + result[original.length + i] = cdataElements[i]; + } + return result; + } + } + } + + @SuppressWarnings("unchecked") + private static <T, U> T[] copyOf(U[] original, int newLength) { + T[] copy = (original.getClass() == Object[].class) ? (T[]) new Object[newLength] + : (T[]) Array.newInstance(original.getClass().getComponentType(), newLength); + System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); + return copy; + } + + private static XMLSerializer getXMLSerializer(OutputStream os, String[] cdataElements) + throws InstantiationException, IllegalAccessException, ClassNotFoundException { + // configure an OutputFormat to handle CDATA + OutputFormat of = new OutputFormat(); + + // specify which of your elements you want to be handled as CDATA. + // The use of the '^' between the namespaceURI and the localname + // seems to be an implementation detail of the xerces code. + // When processing xml that doesn't use namespaces, simply omit the + // namespace prefix as shown in the third CDataElement below. + of.setCDataElements(cdataElements); + + // set any other options you'd like + of.setPreserveSpace(true); + of.setIndenting(true); + + // create the serializer + XMLSerializer serializer = new XMLSerializer(of); + + serializer.setOutputByteStream(os); + + return serializer; + } + + private static Class<?>[] getJAXBContextClasses(ResourceDocType result, DocProcessor docProcessor) { + Class<?>[] clazzes; + if (docProcessor == null) { + clazzes = new Class<?>[1]; + } else { + Class<?>[] requiredJaxbContextClasses = docProcessor.getRequiredJaxbContextClasses(); + if (requiredJaxbContextClasses != null) { + clazzes = new Class<?>[1 + requiredJaxbContextClasses.length]; + for (int i = 0; i < requiredJaxbContextClasses.length; i++) { + clazzes[i + 1] = requiredJaxbContextClasses[i]; + } + } else { + clazzes = new Class<?>[1]; + } + } + clazzes[0] = result.getClass(); + return clazzes; + } + + static boolean createOutputFile(String filePath, DocProcessor docProcessor, ResourceDocType result) { + try (OutputStream out = new BufferedOutputStream(new FileOutputStream(filePath))) { + Class<?>[] clazzes = getJAXBContextClasses(result, docProcessor); + JAXBContext c = JAXBContext.newInstance(clazzes); + Marshaller m = c.createMarshaller(); + m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + String[] cdataElements = getCDataElements(docProcessor); + XMLSerializer serializer = getXMLSerializer(out, cdataElements); + m.marshal(result, serializer); + LOG.info("Wrote " + result); + return true; + } catch (Exception e) { + LOG.log(Level.SEVERE, "Could not serialize ResourceDoc.", e); + return false; + } + } + + static String getLinkClass(String className, String field) { + Object object; + try { + Field declaredField = Class.forName(className, false, Thread.currentThread() + .getContextClassLoader()).getDeclaredField(field); + declaredField.setAccessible(true); + object = declaredField.get(null); + LOG.log(Level.FINE, "Got object " + object); + } catch (final Exception e) { + LOG.info("Have classloader: " + ResourceDoclet.class.getClassLoader().getClass()); + LOG.info("Have thread classloader " + Thread.currentThread().getContextClassLoader().getClass()); + LOG.info("Have system classloader " + ClassLoader.getSystemClassLoader().getClass()); + LOG.log(Level.SEVERE, "Could not get field " + className, e); + return null; + } + + /* marshal the bean to xml + */ + try { + JAXBContext jaxbContext = JAXBContext.newInstance(object.getClass()); + StringWriter stringWriter = new StringWriter(); + Marshaller marshaller = jaxbContext.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + marshaller.marshal(object, stringWriter); + String result = stringWriter.getBuffer().toString(); + LOG.log(Level.FINE, "Got marshalled output:\n" + result); + return result; + } catch (final Exception e) { + LOG.log(Level.SEVERE, "Could serialize bean to xml: " + object, e); + return null; + } + } + +}
diff --git a/ext/wadl-doclet/src/main/java/org/glassfish/jersey/wadl/doclet/DocProcessor.java b/ext/wadl-doclet/src/main/java12/org/glassfish/jersey/wadl/doclet/DocProcessor.java similarity index 81% copy from ext/wadl-doclet/src/main/java/org/glassfish/jersey/wadl/doclet/DocProcessor.java copy to ext/wadl-doclet/src/main/java12/org/glassfish/jersey/wadl/doclet/DocProcessor.java index 61d832e..3f10dc8 100644 --- a/ext/wadl-doclet/src/main/java/org/glassfish/jersey/wadl/doclet/DocProcessor.java +++ b/ext/wadl-doclet/src/main/java12/org/glassfish/jersey/wadl/doclet/DocProcessor.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -20,10 +20,9 @@ import org.glassfish.jersey.server.wadl.internal.generators.resourcedoc.model.MethodDocType; import org.glassfish.jersey.server.wadl.internal.generators.resourcedoc.model.ParamDocType; -import com.sun.javadoc.ClassDoc; -import com.sun.javadoc.MethodDoc; -import com.sun.javadoc.ParamTag; -import com.sun.javadoc.Parameter; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.VariableElement; /** * A doc processor is handed over javadoc elements so that it can turn this into @@ -55,33 +54,31 @@ /** * Use this method to extend the provided {@link ClassDocType} with the information from - * the given {@link ClassDoc}. + * the given {@link TypeElement}. * * @param classDoc the class javadoc * @param classDocType the {@link ClassDocType} to extend. This will later be processed by the * {@link org.glassfish.jersey.server.wadl.WadlGenerator}s. */ - void processClassDoc(ClassDoc classDoc, ClassDocType classDocType); + void processClassDoc(TypeElement classDoc, ClassDocType classDocType); /** * Process the provided methodDoc and add your custom information to the methodDocType.<br> - * Use e.g. {@link MethodDocType#getAny()} to store custom elements. * - * @param methodDoc the {@link MethodDoc} representing the docs of your method. + * @param methodDoc the {@link ExecutableElement} representing the docs of your method. * @param methodDocType the related {@link MethodDocType} that will later be processed by the * {@link org.glassfish.jersey.server.wadl.WadlGenerator}s. */ - void processMethodDoc(MethodDoc methodDoc, MethodDocType methodDocType); + void processMethodDoc(ExecutableElement methodDoc, MethodDocType methodDocType); /** * Use this method to extend the provided {@link ParamDocType} with the information from the * given {@link ParamTag} and {@link Parameter}. * - * @param paramTag the parameter javadoc * @param parameter the parameter (that is documented or not) * @param paramDocType the {@link ParamDocType} to extend. This will later be processed by the * {@link org.glassfish.jersey.server.wadl.WadlGenerator}s. */ - void processParamTag(ParamTag paramTag, Parameter parameter, ParamDocType paramDocType); + void processParamTag(VariableElement parameter, ParamDocType paramDocType); }
diff --git a/ext/wadl-doclet/src/main/java/org/glassfish/jersey/wadl/doclet/DocProcessorWrapper.java b/ext/wadl-doclet/src/main/java12/org/glassfish/jersey/wadl/doclet/DocProcessorWrapper.java similarity index 82% copy from ext/wadl-doclet/src/main/java/org/glassfish/jersey/wadl/doclet/DocProcessorWrapper.java copy to ext/wadl-doclet/src/main/java12/org/glassfish/jersey/wadl/doclet/DocProcessorWrapper.java index 8dec3b5..aa3e28d 100644 --- a/ext/wadl-doclet/src/main/java/org/glassfish/jersey/wadl/doclet/DocProcessorWrapper.java +++ b/ext/wadl-doclet/src/main/java12/org/glassfish/jersey/wadl/doclet/DocProcessorWrapper.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -24,16 +24,11 @@ import org.glassfish.jersey.server.wadl.internal.generators.resourcedoc.model.MethodDocType; import org.glassfish.jersey.server.wadl.internal.generators.resourcedoc.model.ParamDocType; -import com.sun.javadoc.ClassDoc; -import com.sun.javadoc.MethodDoc; -import com.sun.javadoc.ParamTag; -import com.sun.javadoc.Parameter; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.ExecutableElement; +import com.sun.source.doctree.ParamTree; +import javax.lang.model.element.VariableElement; -/** - * This {@link DocProcessor} wraps multiple {@code DocProcessor}s. - * - * @author Martin Grotzke (martin.grotzke at freiheit.com) - */ public class DocProcessorWrapper implements DocProcessor { private final List<DocProcessor> _docProcessors; @@ -79,14 +74,14 @@ } @Override - public void processClassDoc(ClassDoc classDoc, ClassDocType classDocType) { + public void processClassDoc(TypeElement classDoc, ClassDocType classDocType) { for (DocProcessor docProcessor : _docProcessors) { docProcessor.processClassDoc(classDoc, classDocType); } } @Override - public void processMethodDoc(MethodDoc methodDoc, + public void processMethodDoc(ExecutableElement methodDoc, MethodDocType methodDocType) { for (DocProcessor docProcessor : _docProcessors) { docProcessor.processMethodDoc(methodDoc, methodDocType); @@ -94,10 +89,10 @@ } @Override - public void processParamTag(ParamTag paramTag, Parameter parameter, + public void processParamTag(VariableElement parameter, ParamDocType paramDocType) { for (DocProcessor docProcessor : _docProcessors) { - docProcessor.processParamTag(paramTag, parameter, paramDocType); + docProcessor.processParamTag(parameter, paramDocType); } }
diff --git a/ext/wadl-doclet/src/main/java12/org/glassfish/jersey/wadl/doclet/Loader.java b/ext/wadl-doclet/src/main/java12/org/glassfish/jersey/wadl/doclet/Loader.java new file mode 100644 index 0000000..43f2515 --- /dev/null +++ b/ext/wadl-doclet/src/main/java12/org/glassfish/jersey/wadl/doclet/Loader.java
@@ -0,0 +1,48 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.wadl.doclet; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; + +class Loader extends URLClassLoader { + + public Loader(final String[] paths, final ClassLoader parent) { + super(getURLs(paths), parent); + } + + Loader(final String[] paths) { + super(getURLs(paths)); + } + + private static URL[] getURLs(final String[] paths) { + final List<URL> urls = new ArrayList<>(); + for (final String path : paths) { + try { + urls.add(new File(path).toURI().toURL()); + } catch (final MalformedURLException e) { + throw new RuntimeException(e); + } + } + return urls.toArray(new URL[urls.size()]); + } + +}
diff --git a/ext/wadl-doclet/src/main/java12/org/glassfish/jersey/wadl/doclet/OptionClasspath.java b/ext/wadl-doclet/src/main/java12/org/glassfish/jersey/wadl/doclet/OptionClasspath.java new file mode 100644 index 0000000..c11da14 --- /dev/null +++ b/ext/wadl-doclet/src/main/java12/org/glassfish/jersey/wadl/doclet/OptionClasspath.java
@@ -0,0 +1,65 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.wadl.doclet; + +import java.io.File; +import java.util.Arrays; +import java.util.List; + +import jdk.javadoc.doclet.Doclet.Option; + +class OptionClasspath implements Option { + + private final List<String> argNames = Arrays.asList("-classpath"); + private String[] classpathElements; + + @Override + public int getArgumentCount() { + return 1; + } + + @Override + public String getDescription() { + return "Specifies classpath split by :"; + } + + @Override + public Kind getKind() { + return Kind.STANDARD; + } + + @Override + public List<String> getNames() { + return argNames; + } + + @Override + public String getParameters() { + return "classpath"; + } + + @Override + public boolean process(String option, List<String> arguments) { + classpathElements = arguments.get(0).split(File.pathSeparator); + return true; + } + + public String[] getClasspathElements() { + return classpathElements; + } + +}
diff --git a/ext/wadl-doclet/src/main/java12/org/glassfish/jersey/wadl/doclet/OptionDocprocessor.java b/ext/wadl-doclet/src/main/java12/org/glassfish/jersey/wadl/doclet/OptionDocprocessor.java new file mode 100644 index 0000000..1372e8e --- /dev/null +++ b/ext/wadl-doclet/src/main/java12/org/glassfish/jersey/wadl/doclet/OptionDocprocessor.java
@@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.wadl.doclet; + +import java.util.Arrays; +import java.util.List; + +import jdk.javadoc.doclet.Doclet.Option; + +class OptionDocprocessor implements Option { + + private final List<String> argNames = Arrays.asList("-processors"); + private String[] docProcessors = new String[0]; + + @Override + public int getArgumentCount() { + return 1; + } + + @Override + public String getDescription() { + return "Specifies the document processors split by :"; + } + + @Override + public Kind getKind() { + return Kind.STANDARD; + } + + @Override + public List<String> getNames() { + return argNames; + } + + @Override + public String getParameters() { + return "processors"; + } + + @Override + public boolean process(String option, List<String> arguments) { + if (!arguments.isEmpty()) { + docProcessors = arguments.get(0).split(":"); + } + return true; + } + + public String[] getDocProcessors() { + return docProcessors; + } + +}
diff --git a/ext/wadl-doclet/src/main/java12/org/glassfish/jersey/wadl/doclet/OptionOutput.java b/ext/wadl-doclet/src/main/java12/org/glassfish/jersey/wadl/doclet/OptionOutput.java new file mode 100644 index 0000000..822d275 --- /dev/null +++ b/ext/wadl-doclet/src/main/java12/org/glassfish/jersey/wadl/doclet/OptionOutput.java
@@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.wadl.doclet; + +import java.util.Arrays; +import java.util.List; + +import jdk.javadoc.doclet.Doclet.Option; + +class OptionOutput implements Option { + + private final List<String> argNames = Arrays.asList("-output"); + private String value; + + @Override + public int getArgumentCount() { + return 1; + } + + @Override + public String getDescription() { + return "Specifies the output for resourcedoc.xml"; + } + + @Override + public Kind getKind() { + return Kind.STANDARD; + } + + @Override + public List<String> getNames() { + return argNames; + } + + @Override + public String getParameters() { + return "output"; + } + + @Override + public boolean process(String option, List<String> arguments) { + value = arguments.get(0); + return true; + } + + public String getValue() { + return value; + } + +}
diff --git a/ext/wadl-doclet/src/main/java12/org/glassfish/jersey/wadl/doclet/ResourceDoclet.java b/ext/wadl-doclet/src/main/java12/org/glassfish/jersey/wadl/doclet/ResourceDoclet.java new file mode 100644 index 0000000..944413f --- /dev/null +++ b/ext/wadl-doclet/src/main/java12/org/glassfish/jersey/wadl/doclet/ResourceDoclet.java
@@ -0,0 +1,339 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.wadl.doclet; + +import com.sun.source.doctree.DocCommentTree; +import com.sun.source.doctree.DocTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.util.DocTrees; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.lang.model.SourceVersion; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.util.ElementFilter; +import javax.tools.Diagnostic.Kind; +import javax.xml.namespace.QName; + +import org.glassfish.jersey.server.wadl.internal.generators.resourcedoc.model.AnnotationDocType; +import org.glassfish.jersey.server.wadl.internal.generators.resourcedoc.model.ClassDocType; +import org.glassfish.jersey.server.wadl.internal.generators.resourcedoc.model.MethodDocType; +import org.glassfish.jersey.server.wadl.internal.generators.resourcedoc.model.NamedValueType; +import org.glassfish.jersey.server.wadl.internal.generators.resourcedoc.model.ParamDocType; +import org.glassfish.jersey.server.wadl.internal.generators.resourcedoc.model.RepresentationDocType; +import org.glassfish.jersey.server.wadl.internal.generators.resourcedoc.model.RequestDocType; +import org.glassfish.jersey.server.wadl.internal.generators.resourcedoc.model.ResourceDocType; +import org.glassfish.jersey.server.wadl.internal.generators.resourcedoc.model.ResponseDocType; +import org.glassfish.jersey.server.wadl.internal.generators.resourcedoc.model.WadlParamType; + +import jdk.javadoc.doclet.Doclet; +import jdk.javadoc.doclet.DocletEnvironment; +import jdk.javadoc.doclet.Reporter; + +/** + * Creates a resourcedoc XML file. + * <p/> + * <p> + * The ResourceDoc file contains the javadoc documentation of resource classes, + * so that this can be used for extending generated wadl with useful + * documentation. + * </p> + * + * @author <a href="mailto:jorge.bescos.gascon@oracle.com">Jorge Bescos + * Gascon</a> + */ +public class ResourceDoclet implements Doclet { + + private static final Logger LOG = Logger.getLogger(ResourceDoclet.class.getName()); + private static final Pattern PATTERN_RESPONSE_REPRESENTATION = Pattern.compile("@response\\.representation\\.([\\d]+)\\..*"); + private static final Pattern PATTERN_INLINE_TAG = Pattern + .compile("(?!\\{)[\\w\\?\\!\\#\\<\\>\\.\\ \\/\\:\\\\-\\{\\}]+(?=\\})"); + private static final String COMA = ", "; + private static final String EMPTY = ""; + private static final String SPACE = " "; + private final OptionOutput optionOutput = new OptionOutput(); + private final OptionClasspath optionClasspath = new OptionClasspath(); + private final OptionDocprocessor optionDocprocessor = new OptionDocprocessor(); + + @Override + public void init(Locale locale, Reporter reporter) { + reporter.print(Kind.NOTE, "Doclet using locale: " + locale); + } + + private String getComments(DocCommentTree docCommentTree) { + if (docCommentTree != null) { + StringBuilder body = new StringBuilder(); + docCommentTree.getFullBody().forEach(doc -> body.append(doc.toString())); + return body.toString(); + } else { + return EMPTY; + } + } + + private Map<DocTree.Kind, Map<String, String>> getTags(DocCommentTree docCommentTree) { + Map<DocTree.Kind, Map<String, String>> tags = new HashMap<>(); + if (docCommentTree != null) { + for (DocTree tag : docCommentTree.getBlockTags()) { + Map<String, String> tagsInKind = tags.get(tag.getKind()); + if (tagsInKind == null) { + tagsInKind = new HashMap<>(); + tags.put(tag.getKind(), tagsInKind); + } + String[] kindValuePair = getTagPair(tag.toString()); + if (tag.getKind() == DocTree.Kind.PARAM) { + // Adds the parameter name and description + String[] paramValuePair = getTagPair(kindValuePair[1]); + tagsInKind.put(paramValuePair[0], paramValuePair[1]); + } else { + // Adds the @tag name and description + tagsInKind.put(kindValuePair[0], kindValuePair[1]); + } + } + } + return tags; + } + + private String[] getTagPair(String tag) { + String[] pair = tag.split(SPACE, 2); + if (pair.length != 2) { + pair = new String[]{pair[0], null}; + } + return pair; + } + + @Override + public boolean run(DocletEnvironment docEnv) { + boolean success = true; + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + try { + ClassLoader ncl = new Loader(optionClasspath.getClasspathElements(), ResourceDoclet.class.getClassLoader()); + Thread.currentThread().setContextClassLoader(ncl); + DocProcessorWrapper docProcessor = new DocProcessorWrapper(); + if (optionDocprocessor.getDocProcessors().length != 0) { + try { + Class<?> clazz = Class.forName(optionDocprocessor.getDocProcessors()[0], true, + Thread.currentThread().getContextClassLoader()); + Class<? extends DocProcessor> dpClazz = clazz.asSubclass(DocProcessor.class); + docProcessor.add((DocProcessor) dpClazz.getDeclaredConstructors()[0].newInstance()); + } catch (Exception e) { + LOG.log(Level.SEVERE, + "Could not load docProcessors " + Arrays.asList(optionDocprocessor.getDocProcessors()), e); + } + } + ResourceDocType result = new ResourceDocType(); + for (TypeElement element : ElementFilter.typesIn(docEnv.getIncludedElements())) { + DocTrees docTrees = docEnv.getDocTrees(); + DocCommentTree docCommentTree = docTrees.getDocCommentTree(element); + if (docCommentTree != null) { + ClassDocType classDocType = new ClassDocType(); + classDocType.setClassName(element.getQualifiedName().toString()); + classDocType.setCommentText(getComments(docCommentTree)); + docProcessor.processClassDoc(element, classDocType); + for (ExecutableElement method : ElementFilter.methodsIn(element.getEnclosedElements())) { + Map<DocTree.Kind, Map<String, String>> tags = getTags(docTrees.getDocCommentTree(method)); + MethodTree methodTree = docTrees.getTree(method); + MethodDocType methodDocType = new MethodDocType(); + methodDocType.setMethodName(methodTree.getName().toString()); + methodDocType.setCommentText(getComments(docTrees.getDocCommentTree(method))); + getTags(docTrees.getDocCommentTree(method)); + StringBuilder arguments = new StringBuilder("("); + Map<String, String> paramTags = tags.get(DocTree.Kind.PARAM); + for (VariableElement parameter : method.getParameters()) { + ParamDocType paramDocType = buildParamDocType(parameter, paramTags); + arguments.append(parameter.asType()).append(COMA); + if (paramDocType != null) { + methodDocType.getParamDocs().add(paramDocType); + docProcessor.processParamTag(parameter, paramDocType); + } + } + // Remove last comma if there are parameters + if (arguments.length() != 1) { + arguments.delete(arguments.length() - COMA.length(), arguments.length()); + } + arguments.append(")"); + methodDocType.setMethodSignature(arguments.toString()); + docProcessor.processMethodDoc(method, methodDocType); + methodDocType.setRequestDoc(buildRequestDocType(tags)); + methodDocType.setResponseDoc(buildResponseDocType(tags)); + classDocType.getMethodDocs().add(methodDocType); + } + result.getDocs().add(classDocType); + success = DocletUtils.createOutputFile(optionOutput.getValue(), docProcessor, result); + + } + } + } finally { + Thread.currentThread().setContextClassLoader(cl); + } + return success; + } + + private ParamDocType buildParamDocType(VariableElement parameter, Map<String, String> paramTags) { + if (paramTags != null) { + ParamDocType paramDocType = new ParamDocType(); + paramDocType.setParamName(parameter.getSimpleName().toString()); + paramDocType.setCommentText(paramTags.get(paramDocType.getParamName())); + for (AnnotationMirror annotation : parameter.getAnnotationMirrors()) { + AnnotationDocType annotationDocType = new AnnotationDocType(); + annotationDocType.setAnnotationTypeName(annotation.getAnnotationType().toString()); + for (Entry<? extends ExecutableElement, ? extends AnnotationValue> pair : annotation.getElementValues() + .entrySet()) { + NamedValueType namedValueType = new NamedValueType(); + namedValueType.setName(pair.getKey().getSimpleName().toString()); + namedValueType.setValue(pair.getValue().getValue().toString()); + annotationDocType.getAttributeDocs().add(namedValueType); + } + paramDocType.getAnnotationDocs().add(annotationDocType); + } + return paramDocType; + } + return null; + } + + private RequestDocType buildRequestDocType(Map<DocTree.Kind, Map<String, String>> tags) { + Map<String, String> customTags = tags.get(DocTree.Kind.UNKNOWN_BLOCK_TAG); + if (customTags != null) { + RequestDocType requestDoc = new RequestDocType(); + RepresentationDocType representationDoc = new RepresentationDocType(); + String qname = customTags.get("@request.representation.qname"); + String example = customTags.get("@request.representation.example"); + if (qname != null) { + representationDoc.setElement(QName.valueOf(qname)); + } + if (example != null) { + representationDoc.setExample(getSerializedExample(example)); + } + if (qname != null || example != null) { + requestDoc.setRepresentationDoc(representationDoc); + return requestDoc; + } + } + return null; + } + + private ResponseDocType buildResponseDocType(Map<DocTree.Kind, Map<String, String>> tags) { + ResponseDocType responseDoc = new ResponseDocType(); + Map<String, String> returnDoc = tags.get(DocTree.Kind.RETURN); + if (returnDoc != null) { + responseDoc.setReturnDoc(returnDoc.get("@return")); + } + Map<String, String> customTags = tags.get(DocTree.Kind.UNKNOWN_BLOCK_TAG); + if (customTags != null) { + String responseParam = customTags.remove("@response.param"); + if (responseParam != null) { + Matcher matcher = PATTERN_INLINE_TAG.matcher(responseParam); + WadlParamType wadlParam = new WadlParamType(); + while (matcher.find()) { + String group = matcher.group(); + String[] pair = getTagPair(group); + switch (pair[0]) { + case "name": + wadlParam.setName(pair[1]); + break; + case "style": + wadlParam.setStyle(pair[1]); + break; + case "type": + wadlParam.setType(QName.valueOf(pair[1])); + break; + case "doc": + wadlParam.setDoc(pair[1]); + break; + default: + LOG.warning("Unknown inline tag of @response.param: @" + pair[0] + " (value: " + pair[1] + ")"); + break; + } + } + responseDoc.getWadlParams().add(wadlParam); + } + Map<Long, RepresentationDocType> groupedRepresentationDocType = new HashMap<>(); + for (Entry<String, String> entry : customTags.entrySet()) { + if (entry.getKey().startsWith("@response.representation")) { + String[] keySplit = entry.getKey().split("\\."); + long httpCode = Long.parseLong(keySplit[2]); + RepresentationDocType representationDoc = groupedRepresentationDocType.get(httpCode); + if (representationDoc == null) { + representationDoc = new RepresentationDocType(); + representationDoc.setStatus(httpCode); + groupedRepresentationDocType.put(httpCode, representationDoc); + } + if ("qname".equals(keySplit[3])) { + representationDoc.setElement(QName.valueOf(entry.getValue())); + } else if ("mediaType".equals(keySplit[3])) { + representationDoc.setMediaType(entry.getValue()); + } else if ("example".equals(getSerializedExample(keySplit[3]))) { + representationDoc.setExample(getSerializedExample(entry.getValue())); + } else if ("doc".equals(keySplit[3])) { + representationDoc.setDoc(entry.getValue()); + } else { + LOG.warning("Unknown response representation tag " + entry.getKey()); + } + } + } + responseDoc.getRepresentations().addAll(groupedRepresentationDocType.values()); + } + return responseDoc; + } + + @Override + public String getName() { + return getClass().getCanonicalName(); + } + + @Override + public Set<? extends Option> getSupportedOptions() { + return new HashSet<>(Arrays.asList(optionOutput, optionClasspath, optionDocprocessor)); + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + + private String getSerializedExample(String tag) { + if (tag != null) { + Matcher matcher = PATTERN_INLINE_TAG.matcher(tag); + while (matcher.find()) { + String group = matcher.group(); + String[] pair = getTagPair(group); + if ("link".equals(pair[0])) { + String[] classAndField = pair[1].split("#"); + return DocletUtils.getLinkClass(classAndField[0], classAndField[1]); + } else { + return pair[1]; + } + } + } + return tag; + } + +}
diff --git a/ext/wadl-doclet/src/main/java/org/glassfish/jersey/wadl/doclet/DocProcessor.java b/ext/wadl-doclet/src/main/java8_11/org/glassfish/jersey/wadl/doclet/DocProcessor.java similarity index 98% rename from ext/wadl-doclet/src/main/java/org/glassfish/jersey/wadl/doclet/DocProcessor.java rename to ext/wadl-doclet/src/main/java8_11/org/glassfish/jersey/wadl/doclet/DocProcessor.java index 61d832e..962b065 100644 --- a/ext/wadl-doclet/src/main/java/org/glassfish/jersey/wadl/doclet/DocProcessor.java +++ b/ext/wadl-doclet/src/main/java8_11/org/glassfish/jersey/wadl/doclet/DocProcessor.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/ext/wadl-doclet/src/main/java/org/glassfish/jersey/wadl/doclet/DocProcessorWrapper.java b/ext/wadl-doclet/src/main/java8_11/org/glassfish/jersey/wadl/doclet/DocProcessorWrapper.java similarity index 98% rename from ext/wadl-doclet/src/main/java/org/glassfish/jersey/wadl/doclet/DocProcessorWrapper.java rename to ext/wadl-doclet/src/main/java8_11/org/glassfish/jersey/wadl/doclet/DocProcessorWrapper.java index 8dec3b5..e982e84 100644 --- a/ext/wadl-doclet/src/main/java/org/glassfish/jersey/wadl/doclet/DocProcessorWrapper.java +++ b/ext/wadl-doclet/src/main/java8_11/org/glassfish/jersey/wadl/doclet/DocProcessorWrapper.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/ext/wadl-doclet/src/main/java/org/glassfish/jersey/wadl/doclet/ResourceDoclet.java b/ext/wadl-doclet/src/main/java8_11/org/glassfish/jersey/wadl/doclet/ResourceDoclet.java similarity index 78% rename from ext/wadl-doclet/src/main/java/org/glassfish/jersey/wadl/doclet/ResourceDoclet.java rename to ext/wadl-doclet/src/main/java8_11/org/glassfish/jersey/wadl/doclet/ResourceDoclet.java index a6b472d..b19eb11 100644 --- a/ext/wadl-doclet/src/main/java/org/glassfish/jersey/wadl/doclet/ResourceDoclet.java +++ b/ext/wadl-doclet/src/main/java8_11/org/glassfish/jersey/wadl/doclet/ResourceDoclet.java
@@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -96,6 +96,7 @@ * @return true if no exception is thrown. */ public static boolean start(final RootDoc root) { + boolean success = true; final String output = getOptionArg(root.options(), OPTION_OUTPUT); final String classpath = getOptionArg(root.options(), OPTION_CLASSPATH); @@ -151,104 +152,12 @@ result.getDocs().add(classDocType); } - try { - final Class<?>[] clazzes = getJAXBContextClasses(result, docProcessor); - final JAXBContext c = JAXBContext.newInstance(clazzes); - final Marshaller m = c.createMarshaller(); - m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - final OutputStream out = new BufferedOutputStream(new FileOutputStream(output)); - - final String[] cdataElements = getCDataElements(docProcessor); - final XMLSerializer serializer = getXMLSerializer(out, cdataElements); - - m.marshal(result, serializer); - out.close(); - - LOG.info("Wrote " + output); - - } catch (final Exception e) { - LOG.log(Level.SEVERE, "Could not serialize ResourceDoc.", e); - return false; - } + success = DocletUtils.createOutputFile(output, docProcessor, result); } finally { Thread.currentThread().setContextClassLoader(cl); } - return true; - } - - private static String[] getCDataElements(final DocProcessor docProcessor) { - final String[] original = new String[] {"ns1^commentText", "ns2^commentText", "^commentText"}; - if (docProcessor == null) { - return original; - } else { - final String[] cdataElements = docProcessor.getCDataElements(); - if (cdataElements == null || cdataElements.length == 0) { - return original; - } else { - - final String[] result = copyOf(original, original.length + cdataElements.length); - for (int i = 0; i < cdataElements.length; i++) { - result[original.length + i] = cdataElements[i]; - } - return result; - } - } - } - - @SuppressWarnings("unchecked") - private static <T, U> T[] copyOf(final U[] original, final int newLength) { - final T[] copy = (original.getClass() == Object[].class) - ? (T[]) new Object[newLength] - : (T[]) Array.newInstance(original.getClass().getComponentType(), newLength); - System.arraycopy(original, 0, copy, 0, - Math.min(original.length, newLength)); - return copy; - } - - private static Class<?>[] getJAXBContextClasses( - final ResourceDocType result, final DocProcessor docProcessor) { - final Class<?>[] clazzes; - if (docProcessor == null) { - clazzes = new Class<?>[1]; - } else { - final Class<?>[] requiredJaxbContextClasses = docProcessor.getRequiredJaxbContextClasses(); - if (requiredJaxbContextClasses != null) { - clazzes = new Class<?>[1 + requiredJaxbContextClasses.length]; - for (int i = 0; i < requiredJaxbContextClasses.length; i++) { - clazzes[i + 1] = requiredJaxbContextClasses[i]; - } - } else { - clazzes = new Class<?>[1]; - } - } - clazzes[0] = result.getClass(); - return clazzes; - } - - private static XMLSerializer getXMLSerializer(final OutputStream os, final String[] cdataElements) - throws InstantiationException, - IllegalAccessException, ClassNotFoundException { - // configure an OutputFormat to handle CDATA - final OutputFormat of = new OutputFormat(); - - // specify which of your elements you want to be handled as CDATA. - // The use of the '^' between the namespaceURI and the localname - // seems to be an implementation detail of the xerces code. - // When processing xml that doesn't use namespaces, simply omit the - // namespace prefix as shown in the third CDataElement below. - of.setCDataElements(cdataElements); - - // set any other options you'd like - of.setPreserveSpace(true); - of.setIndenting(true); - - // create the serializer - final XMLSerializer serializer = new XMLSerializer(of); - - serializer.setOutputByteStream(os); - - return serializer; + return success; } private static void addResponseDoc(final MethodDoc methodDoc, @@ -479,39 +388,9 @@ /* Get referenced example bean */ + final ClassDoc containingClass = referencedMember.containingClass(); - final Object object; - try { - final Field declaredField = Class.forName(containingClass.qualifiedName(), false, Thread.currentThread() - .getContextClassLoader()).getDeclaredField(referencedMember.name()); - if (referencedMember.isFinal()) { - declaredField.setAccessible(true); - } - object = declaredField.get(null); - LOG.log(Level.FINE, "Got object " + object); - } catch (final Exception e) { - LOG.info("Have classloader: " + ResourceDoclet.class.getClassLoader().getClass()); - LOG.info("Have thread classloader " + Thread.currentThread().getContextClassLoader().getClass()); - LOG.info("Have system classloader " + ClassLoader.getSystemClassLoader().getClass()); - LOG.log(Level.SEVERE, "Could not get field " + referencedMember.qualifiedName(), e); - return null; - } - - /* marshal the bean to xml - */ - try { - final JAXBContext jaxbContext = JAXBContext.newInstance(object.getClass()); - final StringWriter stringWriter = new StringWriter(); - final Marshaller marshaller = jaxbContext.createMarshaller(); - marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); - marshaller.marshal(object, stringWriter); - final String result = stringWriter.getBuffer().toString(); - LOG.log(Level.FINE, "Got marshalled output:\n" + result); - return result; - } catch (final Exception e) { - LOG.log(Level.SEVERE, "Could serialize bean to xml: " + object, e); - return null; - } + return DocletUtils.getLinkClass(containingClass.qualifiedName(), referencedMember.name()); } private static String print(final Tag tag) {