2727public class ProcessorRunner {
2828
2929 private static class GeneratedSourceClassLoader extends ClassLoader {
30- private final JavaFileObject classFile ;
31- private final String className ;
30+ private final Map <String , JavaFileObject > classes = new LinkedHashMap <>();
3231
33- public GeneratedSourceClassLoader (ClassLoader parent , JavaFileObject source ) {
32+ public GeneratedSourceClassLoader (ClassLoader parent , Map < String , JavaFileObject > sources ) {
3433 super (parent );
35- this .classFile = javac ().compile (List .of (source )).generatedFiles ().get (0 );
36- this .className = source .getName ().replace ('/' , '.' ).replace (".java" , "" );
37- }
38-
39- public String getClassName () {
40- return className ;
34+ for (var e : sources .entrySet ()) {
35+ classes .put (e .getKey (), javac ().compile (List .of (e .getValue ())).generatedFiles ().get (0 ));
36+ }
4137 }
4238
4339 protected Class <?> findClass (String name ) throws ClassNotFoundException {
44- if (name .equals (className )) {
40+ if (classes .containsKey (name )) {
41+ var classFile = classes .get (name );
4542 try (var in = classFile .openInputStream ()) {
4643 var bytes = in .readAllBytes ();
4744 return defineClass (name , bytes , 0 , bytes .length );
@@ -54,49 +51,52 @@ protected Class<?> findClass(String name) throws ClassNotFoundException {
5451 }
5552
5653 private static class HookJoobyProcessor extends JoobyProcessor {
57- private JavaFileObject source ;
58- private String kotlinSource ;
54+ private Map < String , JavaFileObject > javaFiles = new LinkedHashMap <>() ;
55+ private Map < String , String > kotlinFiles = new LinkedHashMap <>() ;
5956
6057 public HookJoobyProcessor (Consumer <String > console ) {
6158 super ((kind , message ) -> console .accept (message ));
6259 }
6360
6461 public GeneratedSourceClassLoader createClassLoader () {
65- Objects .requireNonNull (source );
66- return new GeneratedSourceClassLoader (getClass ().getClassLoader (), source );
62+ return new GeneratedSourceClassLoader (getClass ().getClassLoader (), javaFiles );
6763 }
6864
6965 public JavaFileObject getSource () {
70- return source ;
66+ return javaFiles . isEmpty () ? null : javaFiles . entrySet (). iterator (). next (). getValue () ;
7167 }
7268
7369 public String getKotlinSource () {
74- return kotlinSource ;
70+ return kotlinFiles . entrySet (). iterator (). next (). getValue () ;
7571 }
7672
7773 public MvcContext getContext () {
7874 return context ;
7975 }
8076
8177 @ Override
82- protected void onGeneratedSource (JavaFileObject source ) {
83- this . source = source ;
78+ protected void onGeneratedSource (String classname , JavaFileObject source ) {
79+ javaFiles . put ( classname , source ) ;
8480 try {
8581 // Generate kotlin source code inside the compiler scope... avoid false positive errors
86- this . kotlinSource = context .getRouters ().get (0 ).toSourceCode (true );
82+ kotlinFiles . put ( classname , context .getRouters ().get (0 ).toSourceCode (true ) );
8783 } catch (IOException e ) {
8884 SneakyThrows .propagate (e );
8985 }
9086 }
9187 }
9288
93- private final Object instance ;
89+ private final List < Object > instances ;
9490 private final HookJoobyProcessor processor ;
9591
9692 public ProcessorRunner (Object instance ) throws IOException {
9793 this (instance , Map .of ());
9894 }
9995
96+ public ProcessorRunner (List <Object > instances ) throws IOException {
97+ this (instances , System .out ::println , Map .of ());
98+ }
99+
100100 public ProcessorRunner (Object instance , Consumer <String > stdout ) throws IOException {
101101 this (instance , stdout , Map .of ());
102102 }
@@ -107,13 +107,19 @@ public ProcessorRunner(Object instance, Map<String, Object> options) throws IOEx
107107
108108 public ProcessorRunner (Object instance , Consumer <String > stdout , Map <String , Object > options )
109109 throws IOException {
110- this .instance = instance ;
111- this .processor = new HookJoobyProcessor (stdout ::accept );
110+ this (List .of (instance ), stdout , options );
111+ }
112+
113+ public ProcessorRunner (
114+ List <Object > instances , Consumer <String > stdout , Map <String , Object > options )
115+ throws IOException {
116+ this .instances = instances ;
117+ this .processor = new HookJoobyProcessor (stdout );
112118 var optionsArray =
113119 options .entrySet ().stream ().map (e -> "-A" + e .getKey () + "=" + e .getValue ()).toList ();
114120 Truth .assert_ ()
115121 .about (JavaSourcesSubjectFactory .javaSources ())
116- .that (sources (sourceNames (instance . getClass ())))
122+ .that (sources (sourceNames (instances . stream (). map ( Object :: getClass ). toList ())))
117123 .withCompilerOptions (optionsArray .toArray (new String [0 ]))
118124 .processedWith (processor )
119125 .compilesWithoutError ();
@@ -125,15 +131,31 @@ public ProcessorRunner withRouter(SneakyThrows.Consumer<Jooby> consumer) throws
125131
126132 public ProcessorRunner withRouter (SneakyThrows .Consumer2 <Jooby , JavaFileObject > consumer )
127133 throws Exception {
134+ return withRouter (instances .get (0 ).getClass (), consumer );
135+ }
136+
137+ public ProcessorRunner withRouter (Class <?> routerType , SneakyThrows .Consumer <Jooby > consumer )
138+ throws Exception {
139+ return withRouter (routerType , (app , source ) -> consumer .accept (app ));
140+ }
141+
142+ public ProcessorRunner withRouter (
143+ Class <?> routerType , SneakyThrows .Consumer2 <Jooby , JavaFileObject > consumer )
144+ throws Exception {
128145 var classLoader = processor .createClassLoader ();
129- var factoryName = classLoader . getClassName () ;
146+ var factoryName = routerType . getName () + "_" ;
130147 var factoryClass = (Class <? extends Extension >) classLoader .loadClass (factoryName );
131148 Extension extension ;
132149 try {
133150 var constructor = factoryClass .getDeclaredConstructor ();
134151 extension = constructor .newInstance ();
135152 } catch (NoSuchMethodException x ) {
136- extension = factoryClass .getDeclaredConstructor (instance .getClass ()).newInstance (instance );
153+ var instance =
154+ instances .stream ()
155+ .filter (it -> it .getClass ().equals (routerType ))
156+ .findFirst ()
157+ .orElseThrow (() -> new IllegalArgumentException ("Not found: " + routerType ));
158+ extension = factoryClass .getDeclaredConstructor (routerType ).newInstance (instance );
137159 }
138160
139161 var application = new Jooby ();
@@ -154,17 +176,22 @@ public ProcessorRunner withSourceCode(SneakyThrows.Consumer<String> consumer) {
154176 public ProcessorRunner withSourceCode (boolean kt , SneakyThrows .Consumer <String > consumer ) {
155177 consumer .accept (
156178 kt
157- ? processor .kotlinSource
179+ ? processor .kotlinFiles . values (). iterator (). next ()
158180 : Optional .ofNullable (processor .getSource ()).map (Objects ::toString ).orElse (null ));
159181 return this ;
160182 }
161183
162- private String [] sourceNames (Class input ) {
184+ private String [] sourceNames (List < Class <? extends Object >> inputs ) {
163185 List <String > result = new ArrayList <>();
164- while (input != Object .class ) {
165- result .add (input .getName ());
166- input = input .getSuperclass ();
167- }
186+ Set <Class > visited = new HashSet <>();
187+ inputs .stream ()
188+ .forEach (
189+ input -> {
190+ while (input != Object .class && visited .add (input )) {
191+ result .add (input .getName ());
192+ input = input .getSuperclass ();
193+ }
194+ });
168195 return result .toArray (new String [0 ]);
169196 }
170197
0 commit comments