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.LinkedHashSet;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Random;
30 import java.util.Set;
31 import java.util.TreeMap;
32
33 import javax.inject.Inject;
34 import javax.inject.Named;
35 import javax.inject.Singleton;
36
37 import org.apache.maven.artifact.ArtifactUtils;
38 import org.apache.maven.classrealm.ClassRealmRequest.RealmType;
39 import org.apache.maven.extension.internal.CoreExportsProvider;
40 import org.apache.maven.model.Model;
41 import org.apache.maven.model.Plugin;
42 import org.codehaus.plexus.MutablePlexusContainer;
43 import org.codehaus.plexus.PlexusContainer;
44 import org.codehaus.plexus.classworlds.ClassWorld;
45 import org.codehaus.plexus.classworlds.realm.ClassRealm;
46 import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
47 import org.codehaus.plexus.logging.Logger;
48 import org.codehaus.plexus.util.StringUtils;
49 import org.eclipse.aether.artifact.Artifact;
50
51
52
53
54
55
56
57
58 @Named
59 @Singleton
60 public class DefaultClassRealmManager
61 implements ClassRealmManager
62 {
63 public static final String API_REALMID = "maven.api";
64
65
66
67
68
69
70
71
72
73 private static final ClassLoader PARENT_CLASSLOADER = ClassWorld.class.getClassLoader();
74
75 private final Logger logger;
76
77 private final ClassWorld world;
78
79 private final ClassRealm containerRealm;
80
81
82 private final List<ClassRealmManagerDelegate> delegates;
83
84 private final ClassRealm mavenApiRealm;
85
86
87
88
89
90 private final Set<String> providedArtifacts;
91
92 @Inject
93 public DefaultClassRealmManager( Logger logger, PlexusContainer container,
94 List<ClassRealmManagerDelegate> delegates, CoreExportsProvider exports )
95 {
96 this.logger = logger;
97 this.world = ( (MutablePlexusContainer) container ).getClassWorld();
98 this.containerRealm = container.getContainerRealm();
99 this.delegates = delegates;
100
101 Map<String, ClassLoader> foreignImports = exports.get().getExportedPackages();
102
103 this.mavenApiRealm =
104 createRealm( API_REALMID, RealmType.Core, null , null ,
105 foreignImports, null );
106
107 this.providedArtifacts = exports.get().getExportedArtifacts();
108 }
109
110 private ClassRealm newRealm( String id )
111 {
112 synchronized ( world )
113 {
114 String realmId = id;
115
116 Random random = new Random();
117
118 while ( true )
119 {
120 try
121 {
122 ClassRealm classRealm = world.newRealm( realmId, null );
123
124 if ( logger.isDebugEnabled() )
125 {
126 logger.debug( "Created new class realm " + realmId );
127 }
128
129 return classRealm;
130 }
131 catch ( DuplicateRealmException e )
132 {
133 realmId = id + '-' + random.nextInt();
134 }
135 }
136 }
137 }
138
139 public ClassRealm getMavenApiRealm()
140 {
141 return mavenApiRealm;
142 }
143
144
145
146
147
148
149
150
151
152
153
154
155
156 private ClassRealm createRealm( String baseRealmId, RealmType type, ClassLoader parent, List<String> parentImports,
157 Map<String, ClassLoader> foreignImports, List<Artifact> artifacts )
158 {
159 Set<String> artifactIds = new LinkedHashSet<String>();
160
161 List<ClassRealmConstituent> constituents = new ArrayList<ClassRealmConstituent>();
162
163 if ( artifacts != null )
164 {
165 for ( Artifact artifact : artifacts )
166 {
167 if ( !isProvidedArtifact( artifact ) )
168 {
169 artifactIds.add( getId( artifact ) );
170 if ( artifact.getFile() != null )
171 {
172 constituents.add( new ArtifactClassRealmConstituent( artifact ) );
173 }
174 }
175 }
176 }
177
178 if ( parentImports != null )
179 {
180 parentImports = new ArrayList<String>( parentImports );
181 }
182 else
183 {
184 parentImports = new ArrayList<String>();
185 }
186
187 if ( foreignImports != null )
188 {
189 foreignImports = new TreeMap<String, ClassLoader>( foreignImports );
190 }
191 else
192 {
193 foreignImports = new TreeMap<String, ClassLoader>();
194 }
195
196 ClassRealm classRealm = newRealm( baseRealmId );
197
198 if ( parent != null )
199 {
200 classRealm.setParentClassLoader( parent );
201 }
202
203 callDelegates( classRealm, type, parent, parentImports, foreignImports, constituents );
204
205 wireRealm( classRealm, parentImports, foreignImports );
206
207 Set<String> includedIds = populateRealm( classRealm, constituents );
208
209 if ( logger.isDebugEnabled() )
210 {
211 artifactIds.removeAll( includedIds );
212
213 for ( String id : artifactIds )
214 {
215 logger.debug( " Excluded: " + id );
216 }
217 }
218
219 return classRealm;
220 }
221
222 public ClassRealm getCoreRealm()
223 {
224 return containerRealm;
225 }
226
227 public ClassRealm createProjectRealm( Model model, List<Artifact> artifacts )
228 {
229 if ( model == null )
230 {
231 throw new IllegalArgumentException( "model missing" );
232 }
233
234 ClassLoader parent = getMavenApiRealm();
235
236 return createRealm( getKey( model ), RealmType.Project, parent, null, null, artifacts );
237 }
238
239 private static String getKey( Model model )
240 {
241 return "project>" + model.getGroupId() + ":" + model.getArtifactId() + ":" + model.getVersion();
242 }
243
244 public ClassRealm createExtensionRealm( Plugin plugin, List<Artifact> artifacts )
245 {
246 if ( plugin == null )
247 {
248 throw new IllegalArgumentException( "extension plugin missing" );
249 }
250
251 ClassLoader parent = PARENT_CLASSLOADER;
252
253 Map<String, ClassLoader> foreignImports =
254 Collections.<String, ClassLoader>singletonMap( "", getMavenApiRealm() );
255
256 return createRealm( getKey( plugin, true ), RealmType.Extension, parent, null, foreignImports, artifacts );
257 }
258
259 private boolean isProvidedArtifact( Artifact artifact )
260 {
261 return providedArtifacts.contains( artifact.getGroupId() + ":" + artifact.getArtifactId() );
262 }
263
264 public ClassRealm createPluginRealm( Plugin plugin, ClassLoader parent, List<String> parentImports,
265 Map<String, ClassLoader> foreignImports, List<Artifact> artifacts )
266 {
267 if ( plugin == null )
268 {
269 throw new IllegalArgumentException( "plugin missing" );
270 }
271
272 if ( parent == null )
273 {
274 parent = PARENT_CLASSLOADER;
275 }
276
277 return createRealm( getKey( plugin, false ), RealmType.Plugin, parent, parentImports, foreignImports,
278 artifacts );
279 }
280
281 private static String getKey( Plugin plugin, boolean extension )
282 {
283 String version = ArtifactUtils.toSnapshotVersion( plugin.getVersion() );
284 return ( extension ? "extension>" : "plugin>" ) + plugin.getGroupId() + ":" + plugin.getArtifactId() + ":"
285 + version;
286 }
287
288 private static String getId( Artifact artifact )
289 {
290 return getId( artifact.getGroupId(), artifact.getArtifactId(), artifact.getExtension(),
291 artifact.getClassifier(), artifact.getBaseVersion() );
292 }
293
294 private static String getId( ClassRealmConstituent constituent )
295 {
296 return getId( constituent.getGroupId(), constituent.getArtifactId(), constituent.getType(),
297 constituent.getClassifier(), constituent.getVersion() );
298 }
299
300 private static String getId( String gid, String aid, String type, String cls, String ver )
301 {
302 return gid + ':' + aid + ':' + type + ( StringUtils.isNotEmpty( cls ) ? ':' + cls : "" ) + ':' + ver;
303 }
304
305 private void callDelegates( ClassRealm classRealm, RealmType type, ClassLoader parent, List<String> parentImports,
306 Map<String, ClassLoader> foreignImports, List<ClassRealmConstituent> constituents )
307 {
308 List<ClassRealmManagerDelegate> delegates = new ArrayList<ClassRealmManagerDelegate>( this.delegates );
309
310 if ( !delegates.isEmpty() )
311 {
312 ClassRealmRequest request =
313 new DefaultClassRealmRequest( type, parent, parentImports, foreignImports, constituents );
314
315 for ( ClassRealmManagerDelegate delegate : delegates )
316 {
317 try
318 {
319 delegate.setupRealm( classRealm, request );
320 }
321 catch ( Exception e )
322 {
323 logger.error( delegate.getClass().getName() + " failed to setup class realm " + classRealm + ": "
324 + e.getMessage(), e );
325 }
326 }
327 }
328 }
329
330 private Set<String> populateRealm( ClassRealm classRealm, List<ClassRealmConstituent> constituents )
331 {
332 Set<String> includedIds = new LinkedHashSet<String>();
333
334 if ( logger.isDebugEnabled() )
335 {
336 logger.debug( "Populating class realm " + classRealm.getId() );
337 }
338
339 for ( ClassRealmConstituent constituent : constituents )
340 {
341 File file = constituent.getFile();
342
343 String id = getId( constituent );
344 includedIds.add( id );
345
346 if ( logger.isDebugEnabled() )
347 {
348 logger.debug( " Included: " + id );
349 }
350
351 try
352 {
353 classRealm.addURL( file.toURI().toURL() );
354 }
355 catch ( MalformedURLException e )
356 {
357
358 logger.error( e.getMessage(), e );
359 }
360 }
361
362 return includedIds;
363 }
364
365 private void wireRealm( ClassRealm classRealm, List<String> parentImports, Map<String, ClassLoader> foreignImports )
366 {
367 if ( foreignImports != null && !foreignImports.isEmpty() )
368 {
369 if ( logger.isDebugEnabled() )
370 {
371 logger.debug( "Importing foreign packages into class realm " + classRealm.getId() );
372 }
373
374 for ( Map.Entry<String, ClassLoader> entry : foreignImports.entrySet() )
375 {
376 ClassLoader importedRealm = entry.getValue();
377 String imp = entry.getKey();
378
379 if ( logger.isDebugEnabled() )
380 {
381 logger.debug( " Imported: " + imp + " < " + getId( importedRealm ) );
382 }
383
384 classRealm.importFrom( importedRealm, imp );
385 }
386 }
387
388 if ( parentImports != null && !parentImports.isEmpty() )
389 {
390 if ( logger.isDebugEnabled() )
391 {
392 logger.debug( "Importing parent packages into class realm " + classRealm.getId() );
393 }
394
395 for ( String imp : parentImports )
396 {
397 if ( logger.isDebugEnabled() )
398 {
399 logger.debug( " Imported: " + imp + " < " + getId( classRealm.getParentClassLoader() ) );
400 }
401
402 classRealm.importFromParent( imp );
403 }
404 }
405 }
406
407 private String getId( ClassLoader classLoader )
408 {
409 if ( classLoader instanceof ClassRealm )
410 {
411 return ( (ClassRealm) classLoader ).getId();
412 }
413 return String.valueOf( classLoader );
414 }
415
416 }