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.util.StringUtils;
49 import org.eclipse.aether.artifact.Artifact;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53
54
55
56
57
58
59
60 @Named
61 @Singleton
62 public class DefaultClassRealmManager
63 implements ClassRealmManager
64 {
65 public static final String API_REALMID = "maven.api";
66
67
68
69
70
71
72
73
74
75 private static final ClassLoader PARENT_CLASSLOADER = ClassWorld.class.getClassLoader();
76
77 private final Logger logger = LoggerFactory.getLogger( getClass() );
78
79 private final ClassWorld world;
80
81 private final ClassRealm containerRealm;
82
83
84 private final List<ClassRealmManagerDelegate> delegates;
85
86 private final ClassRealm mavenApiRealm;
87
88
89
90
91
92 private final Set<String> providedArtifacts;
93
94 @Inject
95 public DefaultClassRealmManager( PlexusContainer container,
96 List<ClassRealmManagerDelegate> delegates,
97 CoreExports exports )
98 {
99 this.world = ( (MutablePlexusContainer) container ).getClassWorld();
100 this.containerRealm = container.getContainerRealm();
101 this.delegates = delegates;
102
103 Map<String, ClassLoader> foreignImports = exports.getExportedPackages();
104
105 this.mavenApiRealm =
106 createRealm( API_REALMID, RealmType.Core, null , null ,
107 foreignImports, null );
108
109 this.providedArtifacts = exports.getExportedArtifacts();
110 }
111
112 private ClassRealm newRealm( String id )
113 {
114 synchronized ( world )
115 {
116 String realmId = id;
117
118 Random random = new Random();
119
120 while ( true )
121 {
122 try
123 {
124 ClassRealm classRealm = world.newRealm( realmId, null );
125
126 if ( logger.isDebugEnabled() )
127 {
128 logger.debug( "Created new class realm " + realmId );
129 }
130
131 return classRealm;
132 }
133 catch ( DuplicateRealmException e )
134 {
135 realmId = id + '-' + random.nextInt();
136 }
137 }
138 }
139 }
140
141 public ClassRealm getMavenApiRealm()
142 {
143 return mavenApiRealm;
144 }
145
146
147
148
149
150
151
152
153
154
155
156
157
158 private ClassRealm createRealm( String baseRealmId, RealmType type, ClassLoader parent, List<String> parentImports,
159 Map<String, ClassLoader> foreignImports, List<Artifact> artifacts )
160 {
161 Set<String> artifactIds = new LinkedHashSet<>();
162
163 List<ClassRealmConstituent> constituents = new ArrayList<>();
164
165 if ( artifacts != null )
166 {
167 for ( Artifact artifact : artifacts )
168 {
169 if ( !isProvidedArtifact( artifact ) )
170 {
171 artifactIds.add( getId( artifact ) );
172 if ( artifact.getFile() != null )
173 {
174 constituents.add( new ArtifactClassRealmConstituent( artifact ) );
175 }
176 }
177 }
178 }
179
180 if ( parentImports != null )
181 {
182 parentImports = new ArrayList<>( parentImports );
183 }
184 else
185 {
186 parentImports = new ArrayList<>();
187 }
188
189 if ( foreignImports != null )
190 {
191 foreignImports = new TreeMap<>( foreignImports );
192 }
193 else
194 {
195 foreignImports = new TreeMap<>();
196 }
197
198 ClassRealm classRealm = newRealm( baseRealmId );
199
200 if ( parent != null )
201 {
202 classRealm.setParentClassLoader( parent );
203 }
204
205 callDelegates( classRealm, type, parent, parentImports, foreignImports, constituents );
206
207 wireRealm( classRealm, parentImports, foreignImports );
208
209 Set<String> includedIds = populateRealm( classRealm, constituents );
210
211 if ( logger.isDebugEnabled() )
212 {
213 artifactIds.removeAll( includedIds );
214
215 for ( String id : artifactIds )
216 {
217 logger.debug( " Excluded: " + id );
218 }
219 }
220
221 return classRealm;
222 }
223
224 public ClassRealm getCoreRealm()
225 {
226 return containerRealm;
227 }
228
229 public ClassRealm createProjectRealm( Model model, List<Artifact> artifacts )
230 {
231 Objects.requireNonNull( model, "model cannot be null" );
232
233 ClassLoader parent = getMavenApiRealm();
234
235 return createRealm( getKey( model ), RealmType.Project, parent, null, null, artifacts );
236 }
237
238 private static String getKey( Model model )
239 {
240 return "project>" + model.getGroupId() + ":" + model.getArtifactId() + ":" + model.getVersion();
241 }
242
243 public ClassRealm createExtensionRealm( Plugin plugin, List<Artifact> artifacts )
244 {
245 Objects.requireNonNull( plugin, "plugin cannot be null" );
246
247 Map<String, ClassLoader> foreignImports =
248 Collections.singletonMap( "", getMavenApiRealm() );
249
250 return createRealm( getKey( plugin, true ), RealmType.Extension, PARENT_CLASSLOADER, null,
251 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 }