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