Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Jackson annotations to determine visibility and names of properties. #136

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ dependency-reduced-pom.xml
.idea/

*.class
/.classpath
/.project
/.settings
1 change: 1 addition & 0 deletions Documentation.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ Following available options:
* `-v <project version>` The version of the project
* `-d <project domain>` The domain of the project
* `-o <output file>` The location of the analysis output (will be printed to standard out if omitted)
* `-ta <type analyzer>` Annotations to use for type analyzer: jaxb (default) or jackson

Following available backend specific options (only have effect if the corresponding backend is selected):

Expand Down
5 changes: 5 additions & 0 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ You can download the latest version https://github.com/sdaschner/jaxrs-analyzer/
== Backends
The Analyzer supports Plaintext, AsciiDoc and Swagger as output format.

== Annotations for Type Analyzer
By default JAXB annotations and visibility rules will determine which fields will be serialized to JSON.
Alternatively Jackson annotations can be used which will then determine visibility as well as adhere to property names
as specified by the @JsonProperty("my_property_name") annotation.

== Documentation / Feature list
+...+ can be found https://github.com/sdaschner/jaxrs-analyzer/blob/master/Documentation.adoc[here].

Expand Down
7 changes: 6 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,15 @@
<artifactId>asm-all</artifactId>
<version>5.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.8.5</version>
<version>2.9.2</version>
</dependency>

<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.sebastian_daschner.jaxrs_analyzer;

import com.sebastian_daschner.jaxrs_analyzer.analysis.ProjectAnalyzer;
import com.sebastian_daschner.jaxrs_analyzer.analysis.results.NormalizedTypeAnalyzerFactory;
import com.sebastian_daschner.jaxrs_analyzer.backend.Backend;
import com.sebastian_daschner.jaxrs_analyzer.model.rest.Project;
import com.sebastian_daschner.jaxrs_analyzer.model.rest.Resources;
Expand Down Expand Up @@ -29,6 +30,7 @@ public class JAXRSAnalyzer {
private final String projectVersion;
private final Path outputLocation;
private final Backend backend;
private final NormalizedTypeAnalyzerFactory normalizedTypeAnalyzerFactory;

/**
* Constructs a JAX-RS Analyzer.
Expand All @@ -42,13 +44,14 @@ public class JAXRSAnalyzer {
* @param outputLocation The location of the output file (output will be printed to standard out if {@code null})
*/
public JAXRSAnalyzer(final Set<Path> projectClassPaths, final Set<Path> projectSourcePaths, final Set<Path> classPaths, final String projectName, final String projectVersion,
final Backend backend, final Path outputLocation) {
final Backend backend, final Path outputLocation, final NormalizedTypeAnalyzerFactory normalizedTypeAnalyzerFactory) {
Objects.requireNonNull(projectClassPaths);
Objects.requireNonNull(projectSourcePaths);
Objects.requireNonNull(classPaths);
Objects.requireNonNull(projectName);
Objects.requireNonNull(projectVersion);
Objects.requireNonNull(backend);
Objects.requireNonNull(normalizedTypeAnalyzerFactory);

if (projectClassPaths.isEmpty())
throw new IllegalArgumentException("At least one project path is mandatory");
Expand All @@ -60,13 +63,14 @@ public JAXRSAnalyzer(final Set<Path> projectClassPaths, final Set<Path> projectS
this.projectVersion = projectVersion;
this.outputLocation = outputLocation;
this.backend = backend;
this.normalizedTypeAnalyzerFactory = normalizedTypeAnalyzerFactory;
}

/**
* Analyzes the JAX-RS project at the class path and produces the output as configured.
*/
public void analyze() {
final Resources resources = new ProjectAnalyzer(classPaths).analyze(projectClassPaths, projectSourcePaths);
final Resources resources = new ProjectAnalyzer(classPaths).analyze(projectClassPaths, projectSourcePaths,normalizedTypeAnalyzerFactory);

if (resources.isEmpty()) {
LogProvider.info("Empty JAX-RS analysis result, omitting output");
Expand Down
31 changes: 29 additions & 2 deletions src/main/java/com/sebastian_daschner/jaxrs_analyzer/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
*/
package com.sebastian_daschner.jaxrs_analyzer;

import com.sebastian_daschner.jaxrs_analyzer.analysis.results.JacksonAnalyzerFactory;
import com.sebastian_daschner.jaxrs_analyzer.analysis.results.JaxbAnalyzerFactory;
import com.sebastian_daschner.jaxrs_analyzer.analysis.results.NormalizedTypeAnalyzerFactory;
import com.sebastian_daschner.jaxrs_analyzer.backend.Backend;
import com.sebastian_daschner.jaxrs_analyzer.backend.StringBackend;
import com.sebastian_daschner.jaxrs_analyzer.backend.swagger.SwaggerOptions;
Expand Down Expand Up @@ -44,6 +47,7 @@ public class Main {
private static String version = DEFAULT_VERSION;
private static String backendType = "swagger";
private static Path outputFileLocation;
private static String typeAnalyzer = "jaxb";

/**
* Inspects JAX-RS projects and outputs the gathered information.
Expand Down Expand Up @@ -90,8 +94,10 @@ public static void main(final String... args) {

final Backend backend = JAXRSAnalyzer.constructBackend(backendType);
backend.configure(attributes);

final JAXRSAnalyzer jaxrsAnalyzer = new JAXRSAnalyzer(projectClassPaths, projectSourcePaths, classPaths, name, version, backend, outputFileLocation);

NormalizedTypeAnalyzerFactory normalizedTypeAnalyzerFactory = createTypeAnalyzerFactory();
final JAXRSAnalyzer jaxrsAnalyzer = new JAXRSAnalyzer(projectClassPaths, projectSourcePaths, classPaths, name, version, backend
, outputFileLocation,normalizedTypeAnalyzerFactory);
jaxrsAnalyzer.analyze();
}

Expand Down Expand Up @@ -127,6 +133,9 @@ private static void extractArgs(String[] args) {
case "-e":
System.setProperty("project.build.sourceEncoding", args[++i]);
break;
case "-ta":
typeAnalyzer = args[++i];
break;
case "--swaggerSchemes":
attributes.put(SwaggerOptions.SWAGGER_SCHEMES, args[++i]);
break;
Expand Down Expand Up @@ -208,6 +217,7 @@ private static void printUsageAndExit() {
System.err.println(" -o <output file> The location of the analysis output (will be printed to standard out if omitted)");
System.err.println(" -a <attribute name>=<attribute value> Set custom attributes for backends.");
System.err.println(" -e <encoding> The source file encoding");
System.err.println(" -ta <type analyzer> Annotations to use for type analyzer: jaxb (default) or jackson");
System.err.println("\nFollowing available backend specific options (only have effect if the corresponding backend is selected):\n");
System.err.println(" --swaggerSchemes <scheme>[,schemes] The Swagger schemes: http (default), https, ws, wss");
System.err.println(" --renderSwaggerTags Enables rendering of Swagger tags (default tag will be used per default)");
Expand All @@ -217,4 +227,21 @@ private static void printUsageAndExit() {
System.exit(1);
}

private static NormalizedTypeAnalyzerFactory createTypeAnalyzerFactory() {
NormalizedTypeAnalyzerFactory normalizedTypeAnalyzerFactory = null;
switch (typeAnalyzer) {
case "jaxb":
normalizedTypeAnalyzerFactory = new JaxbAnalyzerFactory();
break;
case "jackson":
normalizedTypeAnalyzerFactory = new JacksonAnalyzerFactory();
break;
default:
System.err.println("Invalid type analyzer: "+typeAnalyzer);
printUsageAndExit();
}
return normalizedTypeAnalyzerFactory;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.sebastian_daschner.jaxrs_analyzer.analysis.classes.ContextClassReader;
import com.sebastian_daschner.jaxrs_analyzer.analysis.classes.JAXRSClassVisitor;
import com.sebastian_daschner.jaxrs_analyzer.analysis.javadoc.JavaDocAnalyzer;
import com.sebastian_daschner.jaxrs_analyzer.analysis.results.NormalizedTypeAnalyzerFactory;
import com.sebastian_daschner.jaxrs_analyzer.analysis.results.ResultInterpreter;
import com.sebastian_daschner.jaxrs_analyzer.model.rest.Resources;
import com.sebastian_daschner.jaxrs_analyzer.model.results.ClassResult;
Expand Down Expand Up @@ -86,7 +87,7 @@ public ProjectAnalyzer(final Set<Path> classPaths) {
* @param projectSourcePaths The project source file paths
* @return The REST resource representations
*/
public Resources analyze(final Set<Path> projectClassPaths, final Set<Path> projectSourcePaths) {
public Resources analyze(final Set<Path> projectClassPaths, final Set<Path> projectSourcePaths, NormalizedTypeAnalyzerFactory normalizedTypeAnalyzerFactory) {
lock.lock();
try {
projectClassPaths.forEach(this::addProjectPath);
Expand All @@ -109,7 +110,7 @@ public Resources analyze(final Set<Path> projectClassPaths, final Set<Path> proj

javaDocAnalyzer.analyze(classResults, packages, projectSourcePaths, classPool);

return resultInterpreter.interpret(classResults);
return resultInterpreter.interpret(classResults,normalizedTypeAnalyzerFactory);
} finally {
lock.unlock();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.sebastian_daschner.jaxrs_analyzer.analysis.results;

import static com.sebastian_daschner.jaxrs_analyzer.model.JavaUtils.getFieldDescriptor;
import static com.sebastian_daschner.jaxrs_analyzer.model.JavaUtils.getMethodSignature;
import static com.sebastian_daschner.jaxrs_analyzer.model.JavaUtils.getReturnType;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.sebastian_daschner.jaxrs_analyzer.model.rest.TypeIdentifier;
import com.sebastian_daschner.jaxrs_analyzer.utils.Pair;

public class JacksonAnalyzer implements NormalizedTypeAnalyzer {

private final JavaTypeAnalyzer javaTypeAnalyzer;
private final ObjectMapper objectMapper = new ObjectMapper();

public JacksonAnalyzer(JavaTypeAnalyzer javaTypeAnalyzer) {
this.javaTypeAnalyzer = javaTypeAnalyzer;
}

@Override
public Map<String, TypeIdentifier> analyzeClass(String type, Class<?> clazz) {
if (clazz == null || JavaTypeAnalyzer.isJDKType(type))
return Collections.emptyMap();

JavaType jacksonType = objectMapper.getTypeFactory().constructType(clazz);
BeanDescription introspection = objectMapper.getSerializationConfig().introspect(jacksonType);
List<BeanPropertyDefinition> jacksonProperties = introspection.findProperties();
final Map<String, TypeIdentifier> properties = new HashMap<>();
jacksonProperties
.stream()
.filter(bp->bp.couldSerialize())
.map(bp->mapProperty(bp, type))
.filter(Objects::nonNull)
.forEach(p->{
properties.put(p.getLeft(), TypeIdentifier.ofType(p.getRight()));
javaTypeAnalyzer.analyze(p.getRight());
});
;
return properties;
}

private Pair<String, String> mapProperty(BeanPropertyDefinition property, final String containedType) {
AnnotatedMember primaryMember = property.getPrimaryMember();
if (primaryMember instanceof AnnotatedField) {
AnnotatedField annotatedField = (AnnotatedField)primaryMember;
return mapField(property.getName(),annotatedField.getAnnotated(),containedType);
} else if (primaryMember instanceof AnnotatedMethod) {
AnnotatedMethod annotatedMethod = (AnnotatedMethod)primaryMember;
return mapGetter(property.getName(),annotatedMethod.getAnnotated(),containedType);
}
return null;
}

private static Pair<String, String> mapField(final String jsonProperty, final Field field, final String containedType) {
final String type = getFieldDescriptor(field, containedType);
if (type == null)
return null;

return Pair.of(jsonProperty, type);
}

private static Pair<String, String> mapGetter(final String jsonProperty,final Method method, final String containedType) {
final String returnType = getReturnType(getMethodSignature(method), containedType);
if (returnType == null)
return null;
return Pair.of(jsonProperty, returnType);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.sebastian_daschner.jaxrs_analyzer.analysis.results;

public class JacksonAnalyzerFactory implements NormalizedTypeAnalyzerFactory {

@Override
public NormalizedTypeAnalyzer create(JavaTypeAnalyzer javaTypeAnalyzer) {
return new JacksonAnalyzer(javaTypeAnalyzer);
}

}
Loading