001 /*******************************************************************************
002 * Copyright (C) PicoContainer Organization. All rights reserved.
003 * ---------------------------------------------------------------------------
004 * The software in this package is published under the terms of the BSD style
005 * license a copy of which has been included with this distribution in the
006 * LICENSE.txt file.
007 ******************************************************************************/
008 package org.picocontainer.classname;
009
010 import org.picocontainer.*;
011 import org.picocontainer.security.CustomPermissionsURLClassLoader;
012 import org.picocontainer.lifecycle.LifecycleState;
013 import org.picocontainer.classname.ClassPathElement;
014 import org.picocontainer.classname.ClassLoadingPicoContainer;
015 import org.picocontainer.behaviors.Caching;
016 import org.picocontainer.containers.AbstractDelegatingMutablePicoContainer;
017
018 import java.lang.annotation.Annotation;
019 import java.lang.reflect.Type;
020 import java.net.URL;
021 import java.security.AccessController;
022 import java.security.PrivilegedAction;
023 import java.security.Permissions;
024 import java.util.ArrayList;
025 import java.util.Collection;
026 import java.util.HashMap;
027 import java.util.Iterator;
028 import java.util.List;
029 import java.util.Map;
030 import java.util.Properties;
031
032 /**
033 * Default implementation of ClassLoadingPicoContainer.
034 *
035 * @author Paul Hammant
036 * @author Mauro Talevi
037 * @author Michael Rimov
038 */
039 @SuppressWarnings("serial")
040 public class DefaultClassLoadingPicoContainer extends AbstractDelegatingMutablePicoContainer implements
041 ClassLoadingPicoContainer, ComponentMonitorStrategy {
042
043 /**
044 * Converting Map to allow for primitives to be boxed to Object types.
045 */
046 private static final transient Map<String, String> primitiveNameToBoxedName = new HashMap<String, String>();
047
048 static {
049 primitiveNameToBoxedName.put("int", Integer.class.getName());
050 primitiveNameToBoxedName.put("byte", Byte.class.getName());
051 primitiveNameToBoxedName.put("short", Short.class.getName());
052 primitiveNameToBoxedName.put("long", Long.class.getName());
053 primitiveNameToBoxedName.put("float", Float.class.getName());
054 primitiveNameToBoxedName.put("double", Double.class.getName());
055 primitiveNameToBoxedName.put("boolean", Boolean.class.getName());
056 }
057
058 private final transient List<ClassPathElement> classPathElements = new ArrayList<ClassPathElement>();
059 private final transient ClassLoader parentClassLoader;
060
061 private transient ClassLoader componentClassLoader;
062 private transient boolean componentClassLoaderLocked;
063
064 protected final Map<String, PicoContainer> namedChildContainers = new HashMap<String, PicoContainer>();
065
066 public DefaultClassLoadingPicoContainer(ClassLoader classLoader, ComponentFactory componentFactory, PicoContainer parent) {
067 super(new DefaultPicoContainer(componentFactory, parent));
068 parentClassLoader = classLoader;
069 }
070
071 public DefaultClassLoadingPicoContainer(ClassLoader classLoader, MutablePicoContainer delegate) {
072 super(delegate);
073 parentClassLoader = classLoader;
074
075 }
076
077 public DefaultClassLoadingPicoContainer(ClassLoader classLoader, PicoContainer parent, ComponentMonitor componentMonitor) {
078 super(new DefaultPicoContainer(new Caching(), parent));
079 parentClassLoader = classLoader;
080 ((ComponentMonitorStrategy) getDelegate()).changeMonitor(componentMonitor);
081 }
082
083 public DefaultClassLoadingPicoContainer(ComponentFactory componentFactory) {
084 super(new DefaultPicoContainer(componentFactory, null));
085 parentClassLoader = DefaultClassLoadingPicoContainer.class.getClassLoader();
086 }
087
088
089 public DefaultClassLoadingPicoContainer(PicoContainer parent) {
090 super(new DefaultPicoContainer(parent));
091 parentClassLoader = DefaultClassLoadingPicoContainer.class.getClassLoader();
092 }
093
094 public DefaultClassLoadingPicoContainer(MutablePicoContainer delegate) {
095 super(delegate);
096 parentClassLoader = DefaultClassLoadingPicoContainer.class.getClassLoader();
097 }
098
099 public DefaultClassLoadingPicoContainer(ClassLoader classLoader) {
100 super(new DefaultPicoContainer());
101 parentClassLoader = classLoader;
102 }
103
104 public DefaultClassLoadingPicoContainer() {
105 super(new DefaultPicoContainer());
106 parentClassLoader = DefaultClassLoadingPicoContainer.class.getClassLoader();
107 }
108
109 public DefaultClassLoadingPicoContainer(ComponentFactory componentFactory, LifecycleStrategy lifecycleStrategy,
110 PicoContainer parent, ClassLoader cl, ComponentMonitor componentMonitor) {
111
112 super(new DefaultPicoContainer(componentFactory, lifecycleStrategy, parent, componentMonitor));
113 parentClassLoader = (cl != null) ? cl : DefaultClassLoadingPicoContainer.class.getClassLoader();
114 }
115
116 protected DefaultClassLoadingPicoContainer createChildContainer() {
117 MutablePicoContainer child = getDelegate().makeChildContainer();
118 DefaultClassLoadingPicoContainer container = new DefaultClassLoadingPicoContainer(getComponentClassLoader(), child);
119 container.changeMonitor(currentMonitor());
120 return container;
121 }
122
123 /**
124 * Propagates the monitor change down the delegate chain if a delegate that implements ComponentMonitorStrategy
125 * exists. Because of the ComponentMonitorStrategy API, not all delegates can have their API changed. If
126 * a delegate implementing ComponentMonitorStrategy cannot be found, an exception is thrown.
127 * @throws IllegalStateException if no delegate can be found that implements ComponentMonitorStrategy.
128 * @param monitor the monitor to swap.
129 */
130 public void changeMonitor(ComponentMonitor monitor) {
131
132 MutablePicoContainer picoDelegate = getDelegate();
133 while (picoDelegate != null) {
134 if (picoDelegate instanceof ComponentMonitorStrategy) {
135 ((ComponentMonitorStrategy)picoDelegate).changeMonitor(monitor);
136 return;
137 }
138
139 if (picoDelegate instanceof AbstractDelegatingMutablePicoContainer) {
140 picoDelegate = ((AbstractDelegatingMutablePicoContainer)picoDelegate).getDelegate();
141 } else {
142 break;
143 }
144 }
145
146 throw new IllegalStateException("Could not find delegate picocontainer that implemented ComponentMonitorStrategy");
147
148
149 }
150
151 public ComponentMonitor currentMonitor() {
152 MutablePicoContainer picoDelegate = getDelegate();
153 while (picoDelegate != null) {
154 if (picoDelegate instanceof ComponentMonitorStrategy) {
155 return ((ComponentMonitorStrategy)picoDelegate).currentMonitor();
156 }
157
158 if (picoDelegate instanceof AbstractDelegatingMutablePicoContainer) {
159 picoDelegate = ((AbstractDelegatingMutablePicoContainer)picoDelegate).getDelegate();
160 } else {
161 break;
162 }
163 }
164
165 throw new IllegalStateException("Could not find delegate picocontainer that implemented ComponentMonitorStrategy");
166 }
167
168 public final Object getComponent(Object componentKeyOrType) throws PicoException {
169
170 if (componentKeyOrType instanceof ClassName) {
171 componentKeyOrType = loadClass(componentKeyOrType.toString());
172 }
173
174 Object instance = getDelegate().getComponent(componentKeyOrType);
175
176 if (instance != null) {
177 return instance;
178 }
179
180 ComponentAdapter<?> componentAdapter = null;
181 if (componentKeyOrType.toString().startsWith("*")) {
182 String candidateClassName = componentKeyOrType.toString().substring(1);
183 Collection<ComponentAdapter<?>> cas = getComponentAdapters();
184 for (ComponentAdapter<?> ca : cas) {
185 Object key = ca.getComponentKey();
186 if (key instanceof Class && candidateClassName.equals(((Class<?>) key).getName())) {
187 componentAdapter = ca;
188 break;
189 }
190 }
191 }
192 if (componentAdapter != null) {
193 return componentAdapter.getComponentInstance(this, ComponentAdapter.NOTHING.class);
194 } else {
195 return getComponentInstanceFromChildren(componentKeyOrType);
196 }
197 }
198
199 private Object getComponentInstanceFromChildren(Object componentKey) {
200 String componentKeyPath = componentKey.toString();
201 int ix = componentKeyPath.indexOf('/');
202 if (ix != -1) {
203 String firstElement = componentKeyPath.substring(0, ix);
204 String remainder = componentKeyPath.substring(ix + 1, componentKeyPath.length());
205 Object o = getNamedContainers().get(firstElement);
206 if (o != null) {
207 MutablePicoContainer child = (MutablePicoContainer) o;
208 return child.getComponent(remainder);
209 }
210 }
211 return null;
212 }
213
214 public final MutablePicoContainer makeChildContainer() {
215 return makeChildContainer("containers" + namedChildContainers.size());
216 }
217
218 /**
219 * Makes a child container with the same basic characteristics of
220 * <tt>this</tt> object (ComponentFactory, PicoContainer type, Behavior,
221 * etc)
222 *
223 * @param name the name of the child container
224 * @return The child MutablePicoContainer
225 */
226 public ClassLoadingPicoContainer makeChildContainer(String name) {
227 DefaultClassLoadingPicoContainer child = createChildContainer();
228 MutablePicoContainer parentDelegate = getDelegate();
229 parentDelegate.removeChildContainer(child.getDelegate());
230 parentDelegate.addChildContainer(child);
231 namedChildContainers.put(name, child);
232 return child;
233 }
234
235 public boolean removeChildContainer(PicoContainer child) {
236 boolean result = getDelegate().removeChildContainer(child);
237 Iterator<Map.Entry<String, PicoContainer>> children = namedChildContainers.entrySet().iterator();
238 while (children.hasNext()) {
239 Map.Entry<String, PicoContainer> e = children.next();
240 PicoContainer pc = e.getValue();
241 if (pc == child) {
242 children.remove();
243 }
244 }
245 return result;
246 }
247
248 protected final Map<String, PicoContainer> getNamedContainers() {
249 return namedChildContainers;
250 }
251
252 public ClassPathElement addClassLoaderURL(URL url) {
253 if (componentClassLoaderLocked) {
254 throw new IllegalStateException("ClassLoader URLs cannot be added once this instance is locked");
255 }
256
257 ClassPathElement classPathElement = new ClassPathElement(url);
258 classPathElements.add(classPathElement);
259 return classPathElement;
260 }
261
262 public MutablePicoContainer addComponent(Object implOrInstance) {
263 if (implOrInstance instanceof ClassName) {
264 super.addComponent(loadClass(implOrInstance.toString()));
265 } else {
266 super.addComponent(implOrInstance);
267 }
268 return this;
269 }
270
271 public MutablePicoContainer addComponent(Object key, Object componentImplementationOrInstance,
272 Parameter... parameters) {
273 super.addComponent(classNameToClassIfApplicable(key),
274 classNameToClassIfApplicable(componentImplementationOrInstance), parameters);
275 return this;
276 }
277
278 private Object classNameToClassIfApplicable(Object key) {
279 if (key instanceof ClassName) {
280 key = loadClass(key.toString());
281 }
282 return key;
283 }
284
285 public MutablePicoContainer addAdapter(ComponentAdapter<?> componentAdapter) throws PicoCompositionException {
286 super.addAdapter(componentAdapter);
287 return this;
288 }
289
290 public ClassLoader getComponentClassLoader() {
291 if (componentClassLoader == null) {
292 componentClassLoaderLocked = true;
293 componentClassLoader = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
294 public ClassLoader run() {
295 return new CustomPermissionsURLClassLoader(getURLs(classPathElements), makePermissions(),
296 parentClassLoader);
297 }
298 });
299 }
300 return componentClassLoader;
301 }
302
303 public MutablePicoContainer addChildContainer(PicoContainer child) {
304 getDelegate().addChildContainer(child);
305 namedChildContainers.put("containers" + namedChildContainers.size(), child);
306 return this;
307 }
308
309 public ClassLoadingPicoContainer addChildContainer(String name, PicoContainer child) {
310
311 super.addChildContainer(child);
312
313 namedChildContainers.put(name, child);
314 return this;
315 }
316
317 private Class<?> loadClass(final String className) {
318 ClassLoader classLoader = getComponentClassLoader();
319 // this is deliberately not a doPrivileged operation.
320 String cn = getClassName(className);
321 try {
322 return classLoader.loadClass(cn);
323 } catch (ClassNotFoundException e) {
324 throw new PicoClassNotFoundException(cn, e);
325 }
326 }
327
328 private Map<URL, Permissions> makePermissions() {
329 Map<URL, Permissions> permissionsMap = new HashMap<URL, Permissions>();
330 for (ClassPathElement cpe : classPathElements) {
331 Permissions permissionCollection = cpe.getPermissionCollection();
332 permissionsMap.put(cpe.getUrl(), permissionCollection);
333 }
334 return permissionsMap;
335 }
336
337 private URL[] getURLs(List<ClassPathElement> classPathElemelements) {
338 final URL[] urls = new URL[classPathElemelements.size()];
339 for (int i = 0; i < urls.length; i++) {
340 urls[i] = (classPathElemelements.get(i)).getUrl();
341 }
342 return urls;
343 }
344
345 private static String getClassName(String primitiveOrClass) {
346 String fromMap = primitiveNameToBoxedName.get(primitiveOrClass);
347 return fromMap != null ? fromMap : primitiveOrClass;
348 }
349
350 public ComponentAdapter<?> getComponentAdapter(Object componentKey) {
351 Object componentKey2 = componentKey;
352 if (componentKey instanceof ClassName) {
353 componentKey2 = loadClass(componentKey.toString());
354 }
355 return super.getComponentAdapter(componentKey2);
356 }
357
358 public MutablePicoContainer change(Properties... properties) {
359 super.change(properties);
360 return this;
361 }
362
363 public MutablePicoContainer as(Properties... properties) {
364 return new AsPropertiesPicoContainer(properties);
365 }
366
367 private class AsPropertiesPicoContainer implements ClassLoadingPicoContainer {
368 private MutablePicoContainer delegate;
369
370 public AsPropertiesPicoContainer(Properties... props) {
371 delegate = DefaultClassLoadingPicoContainer.this.getDelegate().as(props);
372 }
373
374 public ClassPathElement addClassLoaderURL(URL url) {
375 return DefaultClassLoadingPicoContainer.this.addClassLoaderURL(url);
376 }
377
378 public ClassLoader getComponentClassLoader() {
379 return DefaultClassLoadingPicoContainer.this.getComponentClassLoader();
380 }
381
382 public ClassLoadingPicoContainer makeChildContainer(String name) {
383 return DefaultClassLoadingPicoContainer.this.makeChildContainer(name);
384 }
385
386 public ClassLoadingPicoContainer addChildContainer(String name, PicoContainer child) {
387 return (ClassLoadingPicoContainer) DefaultClassLoadingPicoContainer.this.addChildContainer(child);
388 }
389
390 public MutablePicoContainer addComponent(Object componentKey, Object componentImplementationOrInstance,
391 Parameter... parameters) {
392 delegate.addComponent(classNameToClassIfApplicable(componentKey),
393 classNameToClassIfApplicable(componentImplementationOrInstance), parameters);
394 return DefaultClassLoadingPicoContainer.this;
395 }
396
397 public MutablePicoContainer addComponent(Object implOrInstance) {
398 delegate.addComponent(classNameToClassIfApplicable(implOrInstance));
399 return DefaultClassLoadingPicoContainer.this;
400 }
401
402 public MutablePicoContainer addConfig(String name, Object val) {
403 delegate.addConfig(name, val);
404 return DefaultClassLoadingPicoContainer.this;
405 }
406
407 public MutablePicoContainer addAdapter(ComponentAdapter<?> componentAdapter) {
408 delegate.addAdapter(componentAdapter);
409 return DefaultClassLoadingPicoContainer.this;
410 }
411
412 public ComponentAdapter removeComponent(Object componentKey) {
413 return delegate.removeComponent(componentKey);
414 }
415
416 public ComponentAdapter removeComponentByInstance(Object componentInstance) {
417 return delegate.removeComponentByInstance(componentInstance);
418 }
419
420 public MutablePicoContainer makeChildContainer() {
421 return DefaultClassLoadingPicoContainer.this.makeChildContainer();
422 }
423
424 public MutablePicoContainer addChildContainer(PicoContainer child) {
425 return DefaultClassLoadingPicoContainer.this.addChildContainer(child);
426 }
427
428 public boolean removeChildContainer(PicoContainer child) {
429 return DefaultClassLoadingPicoContainer.this.removeChildContainer(child);
430 }
431
432 public MutablePicoContainer change(Properties... properties) {
433 return DefaultClassLoadingPicoContainer.this.change(properties);
434 }
435
436 public MutablePicoContainer as(Properties... properties) {
437 return new AsPropertiesPicoContainer(properties);
438 }
439
440 public Object getComponent(Object componentKeyOrType) {
441 return DefaultClassLoadingPicoContainer.this.getComponent(componentKeyOrType);
442 }
443
444 public Object getComponent(Object componentKeyOrType, Type into) {
445 return DefaultClassLoadingPicoContainer.this.getComponent(componentKeyOrType, into);
446 }
447
448 public <T> T getComponent(Class<T> componentType) {
449 return DefaultClassLoadingPicoContainer.this.getComponent(componentType);
450 }
451
452 public <T> T getComponent(Class<T> componentType, Class<? extends Annotation> binding) {
453 return DefaultClassLoadingPicoContainer.this.getComponent(componentType, binding);
454 }
455
456 public List<Object> getComponents() {
457 return DefaultClassLoadingPicoContainer.this.getComponents();
458 }
459
460 public PicoContainer getParent() {
461 return DefaultClassLoadingPicoContainer.this.getParent();
462 }
463
464 public ComponentAdapter<?> getComponentAdapter(Object componentKey) {
465 return DefaultClassLoadingPicoContainer.this.getComponentAdapter(componentKey);
466 }
467
468 public <T> ComponentAdapter<T> getComponentAdapter(Class<T> componentType, NameBinding componentNameBinding) {
469 return DefaultClassLoadingPicoContainer.this.getComponentAdapter(componentType, componentNameBinding);
470 }
471
472 public <T> ComponentAdapter<T> getComponentAdapter(Class<T> componentType, Class<? extends Annotation> binding) {
473 return DefaultClassLoadingPicoContainer.this.getComponentAdapter(componentType, binding);
474 }
475
476 public Collection<ComponentAdapter<?>> getComponentAdapters() {
477 return DefaultClassLoadingPicoContainer.this.getComponentAdapters();
478 }
479
480 public <T> List<ComponentAdapter<T>> getComponentAdapters(Class<T> componentType) {
481 return DefaultClassLoadingPicoContainer.this.getComponentAdapters(componentType);
482 }
483
484 public <T> List<ComponentAdapter<T>> getComponentAdapters(Class<T> componentType,
485 Class<? extends Annotation> binding) {
486 return DefaultClassLoadingPicoContainer.this.getComponentAdapters(componentType, binding);
487 }
488
489 public <T> List<T> getComponents(Class<T> componentType) {
490 return DefaultClassLoadingPicoContainer.this.getComponents(componentType);
491 }
492
493 public void accept(PicoVisitor visitor) {
494 DefaultClassLoadingPicoContainer.this.accept(visitor);
495 }
496
497 public void start() {
498 //This implementation does nothing on lifecycle triggers.
499 }
500
501 public void stop() {
502 //This implementation does nothing on lifecycle triggers.
503 }
504
505 public void dispose() {
506 //This implementation does nothing on lifecycle triggers.
507 }
508
509 public void setName(String name) {
510 DefaultClassLoadingPicoContainer.this.setName(name);
511 }
512
513 public void setLifecycleState(LifecycleState lifecycleState) {
514 DefaultClassLoadingPicoContainer.this.setLifecycleState(lifecycleState);
515 }
516
517 public Converters getConverter() {
518 return DefaultClassLoadingPicoContainer.this.getConverters();
519 }
520 }
521
522 }