Skip to content

Commit 4b845c9

Browse files
input classes from automatic JAX-RS application (closes #28)
1 parent 6d48464 commit 4b845c9

File tree

5 files changed

+86
-65
lines changed

5 files changed

+86
-65
lines changed

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/Input.java

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33

44
import cz.habarta.typescript.generator.parser.*;
55
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;
6-
import java.io.File;
76
import java.lang.reflect.*;
8-
import java.net.URLClassLoader;
97
import java.util.*;
108
import java.util.regex.Matcher;
119
import java.util.regex.Pattern;
@@ -31,11 +29,11 @@ public static Input from(Type... types) {
3129
return new Input(sourceTypes);
3230
}
3331

34-
private static Input fromClassNames(List<String> classNames, ClassLoader classLoader) {
32+
private static Input fromClassNames(List<String> classNames) {
3533
try {
3634
final List<SourceType<Type>> types = new ArrayList<>();
3735
for (String className : classNames) {
38-
final Class<?> cls = classLoader.loadClass(className);
36+
final Class<?> cls = Thread.currentThread().getContextClassLoader().loadClass(className);
3937
// skip synthetic classes (as those generated by java compiler for switch with enum)
4038
// and anonymous classes (should not be processed and they do not have SimpleName)
4139
if (!cls.isSynthetic() && !cls.isAnonymousClass()) {
@@ -48,48 +46,49 @@ private static Input fromClassNames(List<String> classNames, ClassLoader classLo
4846
}
4947
}
5048

51-
private static Input fromClassNamePatterns(List<String> classNamePatterns, ClassLoader classLoader) {
49+
private static Input fromClassNamePatterns(List<String> classNamePatterns) {
5250
System.out.println("Scanning classpath");
53-
if (!(classLoader instanceof URLClassLoader)) {
54-
throw new RuntimeException("Class name globbing only works with 'URLClassLoader'");
55-
}
56-
final URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
57-
final FastClasspathScanner scanner = new FastClasspathScanner().overrideClasspath(classpathFromURLClassLoader(urlClassLoader)).scan();
51+
final FastClasspathScanner scanner = new FastClasspathScanner().scan();
5852
final List<String> allClassNames = new ArrayList<>();
5953
allClassNames.addAll(scanner.getNamesOfAllStandardClasses());
6054
allClassNames.addAll(scanner.getNamesOfAllInterfaceClasses());
6155
Collections.sort(allClassNames);
6256
final List<String> classNames = filterClassNames(allClassNames, classNamePatterns);
6357
System.out.println(String.format("Matched: %d, total: %d.", classNames.size(), allClassNames.size()));
64-
return fromClassNames(classNames, classLoader);
65-
}
66-
67-
private static String classpathFromURLClassLoader(URLClassLoader classLoader) {
68-
return Utils.join(Arrays.asList(classLoader.getURLs()), File.pathSeparator);
58+
return fromClassNames(classNames);
6959
}
7060

71-
private static Input fromJaxrsApplication(String jaxrsApplicationClassName, List<String> excludedClassNames, ClassLoader classLoader) {
72-
final List<SourceType<Type>> sourceTypes = new JaxrsApplicationScanner(classLoader).scanJaxrsApplication(jaxrsApplicationClassName, excludedClassNames);
61+
private static Input fromJaxrsApplication(String jaxrsApplicationClassName, List<String> excludedClassNames) {
62+
final List<SourceType<Type>> sourceTypes = new JaxrsApplicationScanner().scanJaxrsApplication(jaxrsApplicationClassName, excludedClassNames);
7363
return new Input(sourceTypes);
7464
}
7565

76-
public static Input fromClassNamesAndJaxrsApplication(List<String> classNames, List<String> classNamePatterns, String jaxrsApplicationClassName, List<String> excludedClassNames, ClassLoader classLoader) {
77-
final List<SourceType<Type>> types = new ArrayList<>();
78-
if (classNames != null) {
79-
types.addAll(fromClassNames(classNames, classLoader).getSourceTypes());
80-
}
81-
if (classNamePatterns != null) {
82-
types.addAll(fromClassNamePatterns(classNamePatterns, classLoader).getSourceTypes());
83-
}
84-
if (jaxrsApplicationClassName != null) {
85-
types.addAll(fromJaxrsApplication(jaxrsApplicationClassName, excludedClassNames, classLoader).getSourceTypes());
86-
}
87-
if (types.isEmpty()) {
88-
final String errorMessage = "No input classes found.";
89-
System.out.println(errorMessage);
90-
throw new RuntimeException(errorMessage);
66+
public static Input fromClassNamesAndJaxrsApplication(List<String> classNames, List<String> classNamePatterns, String jaxrsApplicationClassName, boolean automaticJaxrsApplication, List<String> excludedClassNames, ClassLoader classLoader) {
67+
final ClassLoader originalContextClassLoader = Thread.currentThread().getContextClassLoader();
68+
try {
69+
Thread.currentThread().setContextClassLoader(classLoader);
70+
final List<SourceType<Type>> types = new ArrayList<>();
71+
if (classNames != null) {
72+
types.addAll(fromClassNames(classNames).getSourceTypes());
73+
}
74+
if (classNamePatterns != null) {
75+
types.addAll(fromClassNamePatterns(classNamePatterns).getSourceTypes());
76+
}
77+
if (jaxrsApplicationClassName != null) {
78+
types.addAll(fromJaxrsApplication(jaxrsApplicationClassName, excludedClassNames).getSourceTypes());
79+
}
80+
if (automaticJaxrsApplication) {
81+
types.addAll(fromJaxrsApplication(null, excludedClassNames).getSourceTypes());
82+
}
83+
if (types.isEmpty()) {
84+
final String errorMessage = "No input classes found.";
85+
System.out.println(errorMessage);
86+
throw new RuntimeException(errorMessage);
87+
}
88+
return new Input(types);
89+
} finally {
90+
Thread.currentThread().setContextClassLoader(originalContextClassLoader);
9191
}
92-
return new Input(types);
9392
}
9493

9594
static List<String> filterClassNames(List<String> classNames, List<String> globs) {

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/JaxrsApplicationScanner.java

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
package cz.habarta.typescript.generator;
33

44
import cz.habarta.typescript.generator.parser.*;
5+
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;
56
import java.lang.annotation.*;
67
import java.lang.reflect.*;
78
import java.util.*;
@@ -11,40 +12,45 @@
1112

1213
public class JaxrsApplicationScanner {
1314

14-
private final ClassLoader classLoader;
1515
private Set<String> excludes;
1616
private Queue<Class<?>> resourceQueue;
1717
private List<SourceType<Type>> discoveredTypes;
1818

19-
public JaxrsApplicationScanner() {
20-
this (JaxrsApplicationScanner.class.getClassLoader());
21-
}
22-
23-
public JaxrsApplicationScanner(ClassLoader classLoader) {
24-
this.classLoader = classLoader;
25-
}
26-
2719
public List<SourceType<Type>> scanJaxrsApplication(String jaxrsApplicationClassName, List<String> excludedClassNames) {
28-
System.out.println("Scanning JAX-RS application: " + jaxrsApplicationClassName);
29-
final ClassLoader originalContextClassLoader = Thread.currentThread().getContextClassLoader();
3020
try {
31-
Thread.currentThread().setContextClassLoader(classLoader);
32-
final Class<?> jaxrsApplicationClass = classLoader.loadClass(jaxrsApplicationClassName);
33-
final Constructor<?> constructor = jaxrsApplicationClass.getDeclaredConstructor();
34-
constructor.setAccessible(true);
35-
final Application application = (Application) constructor.newInstance();
36-
return scanJaxrsApplication(application, excludedClassNames);
21+
final List<Class<?>> resourceClasses = jaxrsApplicationClassName != null
22+
? scanJaxrsApplicationForJaxrsResources(jaxrsApplicationClassName)
23+
: scanClasspathForJaxrsResources();
24+
return scanJaxrsApplication(resourceClasses, excludedClassNames);
3725
} catch (ReflectiveOperationException e) {
3826
final String url = "https://github.com/vojtechhabarta/typescript-generator/wiki/JAX-RS-Application";
3927
final String message = "Cannot load JAX-RS application. For more information see " + url + ".";
4028
System.out.println(message);
4129
throw new RuntimeException(message, e);
42-
} finally {
43-
Thread.currentThread().setContextClassLoader(originalContextClassLoader);
4430
}
4531
}
4632

47-
List<SourceType<Type>> scanJaxrsApplication(Application application, List<String> excludedClassNames) {
33+
private static List<Class<?>> scanJaxrsApplicationForJaxrsResources(String jaxrsApplicationClassName) throws ReflectiveOperationException {
34+
System.out.println("Scanning JAX-RS application: " + jaxrsApplicationClassName);
35+
final Class<?> jaxrsApplicationClass = Thread.currentThread().getContextClassLoader().loadClass(jaxrsApplicationClassName);
36+
final Constructor<?> constructor = jaxrsApplicationClass.getDeclaredConstructor();
37+
constructor.setAccessible(true);
38+
final Application application = (Application) constructor.newInstance();
39+
return new ArrayList<>(application.getClasses());
40+
}
41+
42+
private static List<Class<?>> scanClasspathForJaxrsResources() throws ReflectiveOperationException {
43+
final FastClasspathScanner scanner = new FastClasspathScanner().scan();
44+
final List<String> namesOfResourceClasses = scanner.getNamesOfClassesWithAnnotation(Path.class);
45+
final List<Class<?>> classes = new ArrayList<>();
46+
for (String className : namesOfResourceClasses) {
47+
classes.add(Thread.currentThread().getContextClassLoader().loadClass(className));
48+
}
49+
System.out.println(String.format("Found: %d root resources.", classes.size()));
50+
return classes;
51+
}
52+
53+
List<SourceType<Type>> scanJaxrsApplication(List<Class<?>> resourceClasses, List<String> excludedClassNames) {
4854
resourceQueue = new LinkedList<>();
4955
discoveredTypes = new ArrayList<>();
5056
excludes = new LinkedHashSet<>();
@@ -53,16 +59,15 @@ List<SourceType<Type>> scanJaxrsApplication(Application application, List<String
5359
excludes.addAll(excludedClassNames);
5460
}
5561
final LinkedHashSet<Class<?>> scannedResources = new LinkedHashSet<>();
56-
final List<Class<?>> applicationClasses = new ArrayList<>(application.getClasses());
57-
Collections.sort(applicationClasses, new Comparator<Class<?>>() {
62+
Collections.sort(resourceClasses, new Comparator<Class<?>>() {
5863
@Override
5964
public int compare(Class<?> o1, Class<?> o2) {
6065
return o1.getName().compareToIgnoreCase(o2.getName());
6166
}
6267
});
63-
for (Class<?> applicationClass : applicationClasses) {
64-
if (applicationClass.isAnnotationPresent(Path.class)) {
65-
resourceQueue.add(applicationClass);
68+
for (Class<?> resourceClass : resourceClasses) {
69+
if (resourceClass.isAnnotationPresent(Path.class)) {
70+
resourceQueue.add(resourceClass);
6671
}
6772
}
6873
Class<?> resourceClass;

typescript-generator-core/src/test/java/cz/habarta/typescript/generator/JaxrsApplicationScannerTest.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public class JaxrsApplicationScannerTest<T> {
1919

2020
@Test
2121
public void testReturnedTypes() {
22-
final List<SourceType<Type>> sourceTypes = new JaxrsApplicationScanner().scanJaxrsApplication(new TestApplication(), null);
22+
final List<SourceType<Type>> sourceTypes = new JaxrsApplicationScanner().scanJaxrsApplication(TestApplication.class.getName(), null);
2323
List<Type> types = getTypes(sourceTypes);
2424
final List<Type> expectedTypes = Arrays.asList(
2525
A.class,
@@ -38,8 +38,17 @@ public void testReturnedTypes() {
3838
}
3939

4040
@Test
41-
public void testWithParsing() {
42-
final List<SourceType<Type>> types = new JaxrsApplicationScanner().scanJaxrsApplication(new TestApplication(), null);
41+
public void testWithParsingWithExplicitApplication() {
42+
testWithParsing(TestApplication.class.getName());
43+
}
44+
45+
@Test
46+
public void testWithParsingWithDefaultApplication() {
47+
testWithParsing(null);
48+
}
49+
50+
private void testWithParsing(String applicationClass) {
51+
final List<SourceType<Type>> types = new JaxrsApplicationScanner().scanJaxrsApplication(applicationClass, null);
4352
final Model model = new TypeScriptGenerator(TestUtils.settings()).getModelParser().parseModel(types);
4453
final ArrayList<Class<?>> classes = new ArrayList<>();
4554
for (BeanModel beanModel : model.getBeans()) {
@@ -62,13 +71,13 @@ public void testWithParsing() {
6271

6372
@Test
6473
public void testExcludedResource() {
65-
final List<SourceType<Type>> sourceTypes = new JaxrsApplicationScanner().scanJaxrsApplication(new TestApplication(), Arrays.asList(TestResource1.class.getName()));
74+
final List<SourceType<Type>> sourceTypes = new JaxrsApplicationScanner().scanJaxrsApplication(TestApplication.class.getName(), Arrays.asList(TestResource1.class.getName()));
6675
Assert.assertEquals(0, sourceTypes.size());
6776
}
6877

6978
@Test
7079
public void testExcludedType() {
71-
final List<SourceType<Type>> sourceTypes = new JaxrsApplicationScanner().scanJaxrsApplication(new TestApplication(), Arrays.asList(
80+
final List<SourceType<Type>> sourceTypes = new JaxrsApplicationScanner().scanJaxrsApplication(TestApplication.class.getName(), Arrays.asList(
7281
A.class.getName(),
7382
J.class.getName()
7483
));

typescript-generator-gradle-plugin/src/main/java/cz/habarta/typescript/generator/gradle/GenerateTask.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public class GenerateTask extends DefaultTask {
2020
public List<String> classes;
2121
public List<String> classPatterns;
2222
public String classesFromJaxrsApplication;
23+
public boolean classesFromAutomaticJaxrsApplication;
2324
public List<String> excludeClasses;
2425
public JsonLibrary jsonLibrary;
2526
public boolean declarePropertiesAsOptional;
@@ -96,7 +97,7 @@ public void generate() throws Exception {
9697

9798
// TypeScriptGenerator
9899
new TypeScriptGenerator(settings).generateTypeScript(
99-
Input.fromClassNamesAndJaxrsApplication(classes, classPatterns, classesFromJaxrsApplication, excludeClasses, classLoader),
100+
Input.fromClassNamesAndJaxrsApplication(classes, classPatterns, classesFromJaxrsApplication, classesFromAutomaticJaxrsApplication, excludeClasses, classLoader),
100101
Output.to(getProject().file(outputFile))
101102
);
102103
}

typescript-generator-maven-plugin/src/main/java/cz/habarta/typescript/generator/maven/GenerateMojo.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ public class GenerateMojo extends AbstractMojo {
8181
@Parameter
8282
private String classesFromJaxrsApplication;
8383

84+
/**
85+
* Scans JAX-RS resources for JSON classes to process.
86+
* It is possible to exclude particular REST resource classes using {@link #excludeClasses} parameter.
87+
*/
88+
@Parameter
89+
private boolean classesFromAutomaticJaxrsApplication;
90+
8491
/**
8592
* List of classes excluded from processing.
8693
*/
@@ -277,7 +284,7 @@ public void execute() {
277284

278285
// TypeScriptGenerator
279286
new TypeScriptGenerator(settings).generateTypeScript(
280-
Input.fromClassNamesAndJaxrsApplication(classes, classPatterns, classesFromJaxrsApplication, excludeClasses, classLoader),
287+
Input.fromClassNamesAndJaxrsApplication(classes, classPatterns, classesFromJaxrsApplication, classesFromAutomaticJaxrsApplication, excludeClasses, classLoader),
281288
Output.to(outputFile)
282289
);
283290

0 commit comments

Comments
 (0)