1 package org.apache.maven.classrealm;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.File;
23 import java.net.MalformedURLException;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.LinkedHashSet;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Random;
31 import java.util.Set;
32 import java.util.TreeMap;
33
34 import org.apache.maven.artifact.ArtifactUtils;
35 import org.apache.maven.classrealm.ClassRealmRequest.RealmType;
36 import org.apache.maven.model.Model;
37 import org.apache.maven.model.Plugin;
38 import org.codehaus.plexus.MutablePlexusContainer;
39 import org.codehaus.plexus.PlexusContainer;
40 import org.codehaus.plexus.classworlds.ClassWorld;
41 import org.codehaus.plexus.classworlds.realm.ClassRealm;
42 import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
43 import org.codehaus.plexus.component.annotations.Component;
44 import org.codehaus.plexus.component.annotations.Requirement;
45 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
46 import org.codehaus.plexus.logging.Logger;
47 import org.codehaus.plexus.util.StringUtils;
48 import org.eclipse.aether.artifact.Artifact;
49
50
51
52
53
54
55
56
57 @Component( role = ClassRealmManager.class )
58 public class DefaultClassRealmManager
59 implements ClassRealmManager
60 {
61
62 @Requirement
63 private Logger logger;
64
65 @Requirement
66 protected PlexusContainer container;
67
68 private ClassRealm mavenRealm;
69
70 private ClassWorld getClassWorld()
71 {
72 return ( (MutablePlexusContainer) container ).getClassWorld();
73 }
74
75 private ClassRealm newRealm( String id )
76 {
77 ClassWorld world = getClassWorld();
78
79 synchronized ( world )
80 {
81 String realmId = id;
82
83 Random random = new Random();
84
85 while ( true )
86 {
87 try
88 {
89 ClassRealm classRealm = world.newRealm( realmId, null );
90
91 if ( logger.isDebugEnabled() )
92 {
93 logger.debug( "Created new class realm " + realmId );
94 }
95
96 return classRealm;
97 }
98 catch ( DuplicateRealmException e )
99 {
100 realmId = id + '-' + random.nextInt();
101 }
102 }
103 }
104 }
105
106 public synchronized ClassRealm getMavenApiRealm()
107 {
108 if ( mavenRealm == null )
109 {
110 mavenRealm = newRealm( "maven.api" );
111
112 List<ClassRealmConstituent> constituents = new ArrayList<ClassRealmConstituent>();
113
114 List<String> parentImports = new ArrayList<String>();
115
116 Map<String, ClassLoader> foreignImports = new HashMap<String, ClassLoader>();
117 importMavenApi( foreignImports );
118
119 callDelegates( mavenRealm, RealmType.Core, mavenRealm.getParentClassLoader(), parentImports,
120 foreignImports, constituents );
121
122 wireRealm( mavenRealm, parentImports, foreignImports );
123
124 populateRealm( mavenRealm, constituents );
125 }
126
127 return mavenRealm;
128 }
129
130 private void importMavenApi( Map<String, ClassLoader> imports )
131 {
132 ClassRealm coreRealm = getCoreRealm();
133
134
135 imports.put( "org.apache.maven.*", coreRealm );
136 imports.put( "org.apache.maven.artifact", coreRealm );
137 imports.put( "org.apache.maven.classrealm", coreRealm );
138 imports.put( "org.apache.maven.cli", coreRealm );
139 imports.put( "org.apache.maven.configuration", coreRealm );
140 imports.put( "org.apache.maven.exception", coreRealm );
141 imports.put( "org.apache.maven.execution", coreRealm );
142 imports.put( "org.apache.maven.execution.scope", coreRealm );
143 imports.put( "org.apache.maven.lifecycle", coreRealm );
144 imports.put( "org.apache.maven.model", coreRealm );
145 imports.put( "org.apache.maven.monitor", coreRealm );
146 imports.put( "org.apache.maven.plugin", coreRealm );
147 imports.put( "org.apache.maven.profiles", coreRealm );
148 imports.put( "org.apache.maven.project", coreRealm );
149 imports.put( "org.apache.maven.reporting", coreRealm );
150 imports.put( "org.apache.maven.repository", coreRealm );
151 imports.put( "org.apache.maven.rtinfo", coreRealm );
152 imports.put( "org.apache.maven.settings", coreRealm );
153 imports.put( "org.apache.maven.toolchain", coreRealm );
154 imports.put( "org.apache.maven.usability", coreRealm );
155
156
157 imports.put( "org.apache.maven.wagon.*", coreRealm );
158 imports.put( "org.apache.maven.wagon.authentication", coreRealm );
159 imports.put( "org.apache.maven.wagon.authorization", coreRealm );
160 imports.put( "org.apache.maven.wagon.events", coreRealm );
161 imports.put( "org.apache.maven.wagon.observers", coreRealm );
162 imports.put( "org.apache.maven.wagon.proxy", coreRealm );
163 imports.put( "org.apache.maven.wagon.repository", coreRealm );
164 imports.put( "org.apache.maven.wagon.resource", coreRealm );
165
166
167 imports.put( "org.eclipse.aether.*", coreRealm );
168 imports.put( "org.eclipse.aether.artifact", coreRealm );
169 imports.put( "org.eclipse.aether.collection", coreRealm );
170 imports.put( "org.eclipse.aether.deployment", coreRealm );
171 imports.put( "org.eclipse.aether.graph", coreRealm );
172 imports.put( "org.eclipse.aether.impl", coreRealm );
173 imports.put( "org.eclipse.aether.internal.impl", coreRealm );
174 imports.put( "org.eclipse.aether.installation", coreRealm );
175 imports.put( "org.eclipse.aether.metadata", coreRealm );
176 imports.put( "org.eclipse.aether.repository", coreRealm );
177 imports.put( "org.eclipse.aether.resolution", coreRealm );
178 imports.put( "org.eclipse.aether.spi", coreRealm );
179 imports.put( "org.eclipse.aether.transfer", coreRealm );
180 imports.put( "org.eclipse.aether.version", coreRealm );
181
182
183 imports.put( "org.codehaus.plexus.classworlds", coreRealm );
184
185
186 imports.put( "org.codehaus.classworlds", coreRealm );
187
188
189 imports.put( "org.codehaus.plexus.util.xml.Xpp3Dom", coreRealm );
190 imports.put( "org.codehaus.plexus.util.xml.pull.XmlPullParser", coreRealm );
191 imports.put( "org.codehaus.plexus.util.xml.pull.XmlPullParserException", coreRealm );
192 imports.put( "org.codehaus.plexus.util.xml.pull.XmlSerializer", coreRealm );
193
194
195 imports.put( "org.codehaus.plexus.*", coreRealm );
196 imports.put( "org.codehaus.plexus.component", coreRealm );
197 imports.put( "org.codehaus.plexus.configuration", coreRealm );
198 imports.put( "org.codehaus.plexus.container", coreRealm );
199 imports.put( "org.codehaus.plexus.context", coreRealm );
200 imports.put( "org.codehaus.plexus.lifecycle", coreRealm );
201 imports.put( "org.codehaus.plexus.logging", coreRealm );
202 imports.put( "org.codehaus.plexus.personality", coreRealm );
203
204
205 imports.put( "javax.inject.*", coreRealm );
206
207 imports.put( "javax.enterprise.util.*", coreRealm );
208 imports.put( "javax.enterprise.inject.*", coreRealm );
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225 imports.put( "org.slf4j.*", coreRealm );
226 }
227
228
229
230
231
232
233
234
235
236
237
238
239
240 private ClassRealm createRealm( String baseRealmId, RealmType type, ClassLoader parent, List<String> parentImports,
241 Map<String, ClassLoader> foreignImports, List<Artifact> artifacts )
242 {
243 Set<String> artifactIds = new LinkedHashSet<String>();
244
245 List<ClassRealmConstituent> constituents = new ArrayList<ClassRealmConstituent>();
246
247 if ( artifacts != null )
248 {
249 for ( Artifact artifact : artifacts )
250 {
251 artifactIds.add( getId( artifact ) );
252 if ( artifact.getFile() != null )
253 {
254 constituents.add( new ArtifactClassRealmConstituent( artifact ) );
255 }
256 }
257 }
258
259 if ( parentImports != null )
260 {
261 parentImports = new ArrayList<String>( parentImports );
262 }
263 else
264 {
265 parentImports = new ArrayList<String>();
266 }
267
268 if ( foreignImports != null )
269 {
270 foreignImports = new TreeMap<String, ClassLoader>( foreignImports );
271 }
272 else
273 {
274 foreignImports = new TreeMap<String, ClassLoader>();
275 }
276
277 ClassRealm classRealm = newRealm( baseRealmId );
278
279 if ( parent != null )
280 {
281 classRealm.setParentClassLoader( parent );
282 }
283
284 callDelegates( classRealm, type, parent, parentImports, foreignImports, constituents );
285
286 wireRealm( classRealm, parentImports, foreignImports );
287
288 Set<String> includedIds = populateRealm( classRealm, constituents );
289
290 if ( logger.isDebugEnabled() )
291 {
292 artifactIds.removeAll( includedIds );
293
294 for ( String id : artifactIds )
295 {
296 logger.debug( " Excluded: " + id );
297 }
298 }
299
300 return classRealm;
301 }
302
303 public ClassRealm getCoreRealm()
304 {
305 return container.getContainerRealm();
306 }
307
308 public ClassRealm createProjectRealm( Model model, List<Artifact> artifacts )
309 {
310 if ( model == null )
311 {
312 throw new IllegalArgumentException( "model missing" );
313 }
314
315 ClassLoader parent = getMavenApiRealm();
316
317 return createRealm( getKey( model ), RealmType.Project, parent, null, null, artifacts );
318 }
319
320 private static String getKey( Model model )
321 {
322 return "project>" + model.getGroupId() + ":" + model.getArtifactId() + ":" + model.getVersion();
323 }
324
325 public ClassRealm createExtensionRealm( Plugin plugin, List<Artifact> artifacts )
326 {
327 if ( plugin == null )
328 {
329 throw new IllegalArgumentException( "extension plugin missing" );
330 }
331
332 ClassLoader parent = ClassLoader.getSystemClassLoader();
333
334 Map<String, ClassLoader> foreignImports =
335 Collections.<String, ClassLoader> singletonMap( "", getMavenApiRealm() );
336
337 return createRealm( getKey( plugin, true ), RealmType.Extension, parent, null, foreignImports, artifacts );
338 }
339
340 public ClassRealm createPluginRealm( Plugin plugin, ClassLoader parent, List<String> parentImports,
341 Map<String, ClassLoader> foreignImports, List<Artifact> artifacts )
342 {
343 if ( plugin == null )
344 {
345 throw new IllegalArgumentException( "plugin missing" );
346 }
347
348 if ( parent == null )
349 {
350 parent = ClassLoader.getSystemClassLoader();
351 }
352
353 return createRealm( getKey( plugin, false ), RealmType.Plugin, parent, parentImports, foreignImports, artifacts );
354 }
355
356 private static String getKey( Plugin plugin, boolean extension )
357 {
358 String version = ArtifactUtils.toSnapshotVersion( plugin.getVersion() );
359 return ( extension ? "extension>" : "plugin>" ) + plugin.getGroupId() + ":" + plugin.getArtifactId() + ":"
360 + version;
361 }
362
363 private static String getId( Artifact artifact )
364 {
365 return getId( artifact.getGroupId(), artifact.getArtifactId(), artifact.getExtension(),
366 artifact.getClassifier(), artifact.getBaseVersion() );
367 }
368
369 private static String getId( ClassRealmConstituent constituent )
370 {
371 return getId( constituent.getGroupId(), constituent.getArtifactId(), constituent.getType(),
372 constituent.getClassifier(), constituent.getVersion() );
373 }
374
375 private static String getId( String gid, String aid, String type, String cls, String ver )
376 {
377 return gid + ':' + aid + ':' + type + ( StringUtils.isNotEmpty( cls ) ? ':' + cls : "" ) + ':' + ver;
378 }
379
380 private List<ClassRealmManagerDelegate> getDelegates()
381 {
382 try
383 {
384 return container.lookupList( ClassRealmManagerDelegate.class );
385 }
386 catch ( ComponentLookupException e )
387 {
388 logger.error( "Failed to lookup class realm delegates: " + e.getMessage(), e );
389
390 return Collections.emptyList();
391 }
392 }
393
394 private void callDelegates( ClassRealm classRealm, RealmType type, ClassLoader parent, List<String> parentImports,
395 Map<String, ClassLoader> foreignImports, List<ClassRealmConstituent> constituents )
396 {
397 List<ClassRealmManagerDelegate> delegates = getDelegates();
398
399 if ( !delegates.isEmpty() )
400 {
401 ClassRealmRequest request =
402 new DefaultClassRealmRequest( type, parent, parentImports, foreignImports, constituents );
403
404 for ( ClassRealmManagerDelegate delegate : delegates )
405 {
406 try
407 {
408 delegate.setupRealm( classRealm, request );
409 }
410 catch ( Exception e )
411 {
412 logger.error( delegate.getClass().getName() + " failed to setup class realm " + classRealm + ": "
413 + e.getMessage(), e );
414 }
415 }
416 }
417 }
418
419 private Set<String> populateRealm( ClassRealm classRealm, List<ClassRealmConstituent> constituents )
420 {
421 Set<String> includedIds = new LinkedHashSet<String>();
422
423 if ( logger.isDebugEnabled() )
424 {
425 logger.debug( "Populating class realm " + classRealm.getId() );
426 }
427
428 for ( ClassRealmConstituent constituent : constituents )
429 {
430 File file = constituent.getFile();
431
432 String id = getId( constituent );
433 includedIds.add( id );
434
435 if ( logger.isDebugEnabled() )
436 {
437 logger.debug( " Included: " + id );
438 }
439
440 try
441 {
442 classRealm.addURL( file.toURI().toURL() );
443 }
444 catch ( MalformedURLException e )
445 {
446
447 logger.error( e.getMessage(), e );
448 }
449 }
450
451 return includedIds;
452 }
453
454 private void wireRealm( ClassRealm classRealm, List<String> parentImports, Map<String, ClassLoader> foreignImports )
455 {
456 if ( foreignImports != null && !foreignImports.isEmpty() )
457 {
458 if ( logger.isDebugEnabled() )
459 {
460 logger.debug( "Importing foreign packages into class realm " + classRealm.getId() );
461 }
462
463 for ( Map.Entry<String, ClassLoader> entry : foreignImports.entrySet() )
464 {
465 ClassLoader importedRealm = entry.getValue();
466 String imp = entry.getKey();
467
468 if ( logger.isDebugEnabled() )
469 {
470 logger.debug( " Imported: " + imp + " < " + getId( importedRealm ) );
471 }
472
473 classRealm.importFrom( importedRealm, imp );
474 }
475 }
476
477 if ( parentImports != null && !parentImports.isEmpty() )
478 {
479 if ( logger.isDebugEnabled() )
480 {
481 logger.debug( "Importing parent packages into class realm " + classRealm.getId() );
482 }
483
484 for ( String imp : parentImports )
485 {
486 if ( logger.isDebugEnabled() )
487 {
488 logger.debug( " Imported: " + imp + " < " + getId( classRealm.getParentClassLoader() ) );
489 }
490
491 classRealm.importFromParent( imp );
492 }
493 }
494 }
495
496 private String getId( ClassLoader classLoader )
497 {
498 if ( classLoader instanceof ClassRealm )
499 {
500 return ( (ClassRealm) classLoader ).getId();
501 }
502 return String.valueOf( classLoader );
503 }
504
505 }