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.ComponentAdapter;
011
012 import org.picocontainer.*;
013 import org.picocontainer.security.CustomPermissionsURLClassLoader;
014 import org.picocontainer.lifecycle.LifecycleState;
015 import org.picocontainer.behaviors.Caching;
016 import org.picocontainer.containers.AbstractDelegatingMutablePicoContainer;
017
018 import java.io.File;
019 import java.io.IOException;
020 import java.lang.annotation.Annotation;
021 import java.lang.reflect.Type;
022 import java.net.URL;
023 import java.security.AccessController;
024 import java.security.CodeSource;
025 import java.security.PrivilegedAction;
026 import java.security.Permissions;
027 import java.util.ArrayList;
028 import java.util.Collection;
029 import java.util.Enumeration;
030 import java.util.HashMap;
031 import java.util.Iterator;
032 import java.util.List;
033 import java.util.Map;
034 import java.util.Properties;
035 import java.util.regex.Pattern;
036 import java.util.zip.ZipEntry;
037 import java.util.zip.ZipFile;
038
039 /**
040 * Default implementation of ClassLoadingPicoContainer.
041 *
042 * @author Paul Hammant
043 * @author Mauro Talevi
044 * @author Michael Rimov
045 */
046 @SuppressWarnings("serial")
047 public class DefaultClassLoadingPicoContainer extends AbstractDelegatingMutablePicoContainer implements
048 ClassLoadingPicoContainer, ComponentMonitorStrategy {
049
050 /**
051 * Converting Map to allow for primitives to be boxed to Object types.
052 */
053 private static final transient Map<String, String> primitiveNameToBoxedName = new HashMap<String, String>();
054
055 static {
056 primitiveNameToBoxedName.put("int", Integer.class.getName());
057 primitiveNameToBoxedName.put("byte", Byte.class.getName());
058 primitiveNameToBoxedName.put("short", Short.class.getName());
059 primitiveNameToBoxedName.put("long", Long.class.getName());
060 primitiveNameToBoxedName.put("float", Float.class.getName());
061 primitiveNameToBoxedName.put("double", Double.class.getName());
062 primitiveNameToBoxedName.put("boolean", Boolean.class.getName());
063 }
064
065 private final transient List<ClassPathElement> classPathElements = new ArrayList<ClassPathElement>();
066 private final transient ClassLoader parentClassLoader;
067
068 private transient ClassLoader componentClassLoader;
069 private transient boolean componentClassLoaderLocked;
070
071 protected final Map<String, PicoContainer> namedChildContainers = new HashMap<String, PicoContainer>();
072
073 public DefaultClassLoadingPicoContainer(ClassLoader classLoader, ComponentFactory componentFactory, PicoContainer parent) {
074 super(new DefaultPicoContainer(componentFactory, parent));
075 parentClassLoader = classLoader;
076 }
077
078 public DefaultClassLoadingPicoContainer(ClassLoader classLoader, MutablePicoContainer delegate) {
079 super(delegate);
080 parentClassLoader = classLoader;
081
082 }
083
084 public DefaultClassLoadingPicoContainer(ClassLoader classLoader, PicoContainer parent, ComponentMonitor componentMonitor) {
085 super(new DefaultPicoContainer(new Caching(), parent));
086 parentClassLoader = classLoader;
087 ((ComponentMonitorStrategy) getDelegate()).changeMonitor(componentMonitor);
088 }
089
090 public DefaultClassLoadingPicoContainer(ComponentFactory componentFactory) {
091 super(new DefaultPicoContainer(componentFactory, null));
092 parentClassLoader = DefaultClassLoadingPicoContainer.class.getClassLoader();
093 }
094
095
096 public DefaultClassLoadingPicoContainer(PicoContainer parent) {
097 super(new DefaultPicoContainer(parent));
098 parentClassLoader = DefaultClassLoadingPicoContainer.class.getClassLoader();
099 }
100
101 public DefaultClassLoadingPicoContainer(MutablePicoContainer delegate) {
102 super(delegate);
103 parentClassLoader = DefaultClassLoadingPicoContainer.class.getClassLoader();
104 }
105
106 public DefaultClassLoadingPicoContainer(ClassLoader classLoader) {
107 super(new DefaultPicoContainer());
108 parentClassLoader = classLoader;
109 }
110
111 public DefaultClassLoadingPicoContainer() {
112 super(new DefaultPicoContainer());
113 parentClassLoader = DefaultClassLoadingPicoContainer.class.getClassLoader();
114 }
115
116 public DefaultClassLoadingPicoContainer(ComponentFactory componentFactory, LifecycleStrategy lifecycleStrategy,
117 PicoContainer parent, ClassLoader cl, ComponentMonitor componentMonitor) {
118
119 super(new DefaultPicoContainer(componentFactory, lifecycleStrategy, parent, componentMonitor));
120 parentClassLoader = (cl != null) ? cl : DefaultClassLoadingPicoContainer.class.getClassLoader();
121 }
122
123 protected DefaultClassLoadingPicoContainer createChildContainer() {
124 MutablePicoContainer child = getDelegate().makeChildContainer();
125 DefaultClassLoadingPicoContainer container = new DefaultClassLoadingPicoContainer(getComponentClassLoader(), child);
126 container.changeMonitor(currentMonitor());
127 return container;
128 }
129
130 /**
131 * Propagates the monitor change down the delegate chain if a delegate that implements ComponentMonitorStrategy
132 * exists. Because of the ComponentMonitorStrategy API, not all delegates can have their API changed. If
133 * a delegate implementing ComponentMonitorStrategy cannot be found, an exception is thrown.
134 * @throws IllegalStateException if no delegate can be found that implements ComponentMonitorStrategy.
135 * @param monitor the monitor to swap.
136 */
137 public void changeMonitor(ComponentMonitor monitor) {
138
139 MutablePicoContainer picoDelegate = getDelegate();
140 while (picoDelegate != null) {
141 if (picoDelegate instanceof ComponentMonitorStrategy) {
142 ((ComponentMonitorStrategy)picoDelegate).changeMonitor(monitor);
143 return;
144 }
145
146 if (picoDelegate instanceof AbstractDelegatingMutablePicoContainer) {
147 picoDelegate = ((AbstractDelegatingMutablePicoContainer)picoDelegate).getDelegate();
148 } else {
149 break;
150 }
151 }
152
153 throw new IllegalStateException("Could not find delegate picocontainer that implemented ComponentMonitorStrategy");
154
155
156 }
157
158 public ComponentMonitor currentMonitor() {
159 MutablePicoContainer picoDelegate = getDelegate();
160 while (picoDelegate != null) {
161 if (picoDelegate instanceof ComponentMonitorStrategy) {
162 return ((ComponentMonitorStrategy)picoDelegate).currentMonitor();
163 }
164
165 if (picoDelegate instanceof AbstractDelegatingMutablePicoContainer) {
166 picoDelegate = ((AbstractDelegatingMutablePicoContainer)picoDelegate).getDelegate();
167 } else {
168 break;
169 }
170 }
171
172 throw new IllegalStateException("Could not find delegate picocontainer that implemented ComponentMonitorStrategy");
173 }
174
175 public final Object getComponent(Object componentKeyOrType) throws PicoException {
176
177 if (componentKeyOrType instanceof ClassName) {
178 componentKeyOrType = loadClass((ClassName) componentKeyOrType);
179 }
180
181 Object instance = getDelegate().getComponent(componentKeyOrType);
182
183 if (instance != null) {
184 return instance;
185 }
186
187 ComponentAdapter<?> componentAdapter = null;
188 if (componentKeyOrType.toString().startsWith("*")) {
189 String candidateClassName = componentKeyOrType.toString().substring(1);
190 Collection<ComponentAdapter<?>> cas = getComponentAdapters();
191 for (ComponentAdapter<?> ca : cas) {
192 Object key = ca.getComponentKey();
193 if (key instanceof Class && candidateClassName.equals(((Class<?>) key).getName())) {
194 componentAdapter = ca;
195 break;
196 }
197 }
198 }
199 if (componentAdapter != null) {
200 return componentAdapter.getComponentInstance(this, ComponentAdapter.NOTHING.class);
201 } else {
202 return getComponentInstanceFromChildren(componentKeyOrType);
203 }
204 }
205
206 private Object getComponentInstanceFromChildren(Object componentKey) {
207 String componentKeyPath = componentKey.toString();
208 int ix = componentKeyPath.indexOf('/');
209 if (ix != -1) {
210 String firstElement = componentKeyPath.substring(0, ix);
211 String remainder = componentKeyPath.substring(ix + 1, componentKeyPath.length());
212 Object o = getNamedContainers().get(firstElement);
213 if (o != null) {
214 MutablePicoContainer child = (MutablePicoContainer) o;
215 return child.getComponent(remainder);
216 }
217 }
218 return null;
219 }
220
221 public final MutablePicoContainer makeChildContainer() {
222 return makeChildContainer("containers" + namedChildContainers.size());
223 }
224
225 /**
226 * Makes a child container with the same basic characteristics of
227 * <tt>this</tt> object (ComponentFactory, PicoContainer type, Behavior,
228 * etc)
229 *
230 * @param name the name of the child container
231 * @return The child MutablePicoContainer
232 */
233 public ClassLoadingPicoContainer makeChildContainer(String name) {
234 DefaultClassLoadingPicoContainer child = createChildContainer();
235 MutablePicoContainer parentDelegate = getDelegate();
236 parentDelegate.removeChildContainer(child.getDelegate());
237 parentDelegate.addChildContainer(child);
238 namedChildContainers.put(name, child);
239 return child;
240 }
241
242 public boolean removeChildContainer(PicoContainer child) {
243 boolean result = getDelegate().removeChildContainer(child);
244 Iterator<Map.Entry<String, PicoContainer>> children = namedChildContainers.entrySet().iterator();
245 while (children.hasNext()) {
246 Map.Entry<String, PicoContainer> e = children.next();
247 PicoContainer pc = e.getValue();
248 if (pc == child) {
249 children.remove();
250 }
251 }
252 return result;
253 }
254
255 protected final Map<String, PicoContainer> getNamedContainers() {
256 return namedChildContainers;
257 }
258
259 public ClassPathElement addClassLoaderURL(URL url) {
260 if (componentClassLoaderLocked) {
261 throw new IllegalStateException("ClassLoader URLs cannot be added once this instance is locked");
262 }
263
264 ClassPathElement classPathElement = new ClassPathElement(url);
265 classPathElements.add(classPathElement);
266 return classPathElement;
267 }
268
269 public MutablePicoContainer addComponent(Object implOrInstance) {
270 if (implOrInstance instanceof ClassName) {
271 super.addComponent(loadClass((ClassName) implOrInstance));
272 } else {
273 super.addComponent(implOrInstance);
274 }
275 return this;
276 }
277
278 public MutablePicoContainer addComponent(Object key, Object componentImplementationOrInstance,
279 Parameter... parameters) {
280 super.addComponent(classNameToClassIfApplicable(key),
281 classNameToClassIfApplicable(componentImplementationOrInstance), parameters);
282 return this;
283 }
284
285 private Object classNameToClassIfApplicable(Object key) {
286 if (key instanceof ClassName) {
287 key = loadClass((ClassName) key);
288 }
289 return key;
290 }
291
292 public MutablePicoContainer addAdapter(ComponentAdapter<?> componentAdapter) throws PicoCompositionException {
293 super.addAdapter(componentAdapter);
294 return this;
295 }
296
297 public ClassLoader getComponentClassLoader() {
298 if (componentClassLoader == null) {
299 componentClassLoaderLocked = true;
300 componentClassLoader = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
301 public ClassLoader run() {
302 return new CustomPermissionsURLClassLoader(getURLs(classPathElements), makePermissions(),
303 parentClassLoader);
304 }
305 });
306 }
307 return componentClassLoader;
308 }
309
310 public MutablePicoContainer addChildContainer(PicoContainer child) {
311 getDelegate().addChildContainer(child);
312 namedChildContainers.put("containers" + namedChildContainers.size(), child);
313 return this;
314 }
315
316 public ClassLoadingPicoContainer addChildContainer(String name, PicoContainer child) {
317
318 super.addChildContainer(child);
319
320 namedChildContainers.put(name, child);
321 return this;
322 }
323
324 private Class<?> loadClass(final ClassName className) {
325 ClassLoader classLoader = getComponentClassLoader();
326 // this is deliberately not a doPrivileged operation.
327 String cn = getClassName(className.toString());
328 try {
329 return classLoader.loadClass(cn);
330 } catch (ClassNotFoundException e) {
331 throw new PicoClassNotFoundException(cn, e);
332 }
333 }
334
335 private Map<URL, Permissions> makePermissions() {
336 Map<URL, Permissions> permissionsMap = new HashMap<URL, Permissions>();
337 for (ClassPathElement cpe : classPathElements) {
338 Permissions permissionCollection = cpe.getPermissionCollection();
339 permissionsMap.put(cpe.getUrl(), permissionCollection);
340 }
341 return permissionsMap;
342 }
343
344 private URL[] getURLs(List<ClassPathElement> classPathElemelements) {
345 final URL[] urls = new URL[classPathElemelements.size()];
346 for (int i = 0; i < urls.length; i++) {
347 urls[i] = (classPathElemelements.get(i)).getUrl();
348 }
349 return urls;
350 }
351
352 private static String getClassName(String primitiveOrClass) {
353 String fromMap = primitiveNameToBoxedName.get(primitiveOrClass);
354 return fromMap != null ? fromMap : primitiveOrClass;
355 }
356
357 public ComponentAdapter<?> getComponentAdapter(Object componentKey) {
358 Object componentKey2 = componentKey;
359 if (componentKey instanceof ClassName) {
360 componentKey2 = loadClass((ClassName) componentKey);
361 }
362 return super.getComponentAdapter(componentKey2);
363 }
364
365 public MutablePicoContainer change(Properties... properties) {
366 super.change(properties);
367 return this;
368 }
369
370 public MutablePicoContainer as(Properties... properties) {
371 return new AsPropertiesPicoContainer(properties);
372 }
373
374 private class AsPropertiesPicoContainer implements ClassLoadingPicoContainer {
375 private MutablePicoContainer delegate;
376
377 public AsPropertiesPicoContainer(Properties... props) {
378 delegate = DefaultClassLoadingPicoContainer.this.getDelegate().as(props);
379 }
380
381 public ClassPathElement addClassLoaderURL(URL url) {
382 return DefaultClassLoadingPicoContainer.this.addClassLoaderURL(url);
383 }
384
385 public ClassLoader getComponentClassLoader() {
386 return DefaultClassLoadingPicoContainer.this.getComponentClassLoader();
387 }
388
389 public ClassLoadingPicoContainer makeChildContainer(String name) {
390 return DefaultClassLoadingPicoContainer.this.makeChildContainer(name);
391 }
392
393 public ClassLoadingPicoContainer addChildContainer(String name, PicoContainer child) {
394 return (ClassLoadingPicoContainer) DefaultClassLoadingPicoContainer.this.addChildContainer(child);
395 }
396
397 public MutablePicoContainer addComponent(Object componentKey, Object componentImplementationOrInstance,
398 Parameter... parameters) {
399 delegate.addComponent(classNameToClassIfApplicable(componentKey),
400 classNameToClassIfApplicable(componentImplementationOrInstance), parameters);
401 return DefaultClassLoadingPicoContainer.this;
402 }
403
404 public MutablePicoContainer addComponent(Object implOrInstance) {
405 delegate.addComponent(classNameToClassIfApplicable(implOrInstance));
406 return DefaultClassLoadingPicoContainer.this;
407 }
408
409 public MutablePicoContainer addConfig(String name, Object val) {
410 delegate.addConfig(name, val);
411 return DefaultClassLoadingPicoContainer.this;
412 }
413
414 public MutablePicoContainer addAdapter(ComponentAdapter<?> componentAdapter) {
415 delegate.addAdapter(componentAdapter);
416 return DefaultClassLoadingPicoContainer.this;
417 }
418
419 public ComponentAdapter removeComponent(Object componentKey) {
420 return delegate.removeComponent(componentKey);
421 }
422
423 public ComponentAdapter removeComponentByInstance(Object componentInstance) {
424 return delegate.removeComponentByInstance(componentInstance);
425 }
426
427 public MutablePicoContainer makeChildContainer() {
428 return DefaultClassLoadingPicoContainer.this.makeChildContainer();
429 }
430
431 public MutablePicoContainer addChildContainer(PicoContainer child) {
432 return DefaultClassLoadingPicoContainer.this.addChildContainer(child);
433 }
434
435 public boolean removeChildContainer(PicoContainer child) {
436 return DefaultClassLoadingPicoContainer.this.removeChildContainer(child);
437 }
438
439 public MutablePicoContainer change(Properties... properties) {
440 return DefaultClassLoadingPicoContainer.this.change(properties);
441 }
442
443 public MutablePicoContainer as(Properties... properties) {
444 return new AsPropertiesPicoContainer(properties);
445 }
446
447 public Object getComponent(Object componentKeyOrType) {
448 return DefaultClassLoadingPicoContainer.this.getComponent(componentKeyOrType);
449 }
450
451 public Object getComponent(Object componentKeyOrType, Type into) {
452 return DefaultClassLoadingPicoContainer.this.getComponent(componentKeyOrType, into);
453 }
454
455 public <T> T getComponent(Class<T> componentType) {
456 return DefaultClassLoadingPicoContainer.this.getComponent(componentType);
457 }
458
459 public <T> T getComponent(Class<T> componentType, Class<? extends Annotation> binding) {
460 return DefaultClassLoadingPicoContainer.this.getComponent(componentType, binding);
461 }
462
463 public List<Object> getComponents() {
464 return DefaultClassLoadingPicoContainer.this.getComponents();
465 }
466
467 public PicoContainer getParent() {
468 return DefaultClassLoadingPicoContainer.this.getParent();
469 }
470
471 public ComponentAdapter<?> getComponentAdapter(Object componentKey) {
472 return DefaultClassLoadingPicoContainer.this.getComponentAdapter(componentKey);
473 }
474
475 public <T> ComponentAdapter<T> getComponentAdapter(Class<T> componentType, NameBinding componentNameBinding) {
476 return DefaultClassLoadingPicoContainer.this.getComponentAdapter(componentType, componentNameBinding);
477 }
478
479 public <T> ComponentAdapter<T> getComponentAdapter(Class<T> componentType, Class<? extends Annotation> binding) {
480 return DefaultClassLoadingPicoContainer.this.getComponentAdapter(componentType, binding);
481 }
482
483 public Collection<ComponentAdapter<?>> getComponentAdapters() {
484 return DefaultClassLoadingPicoContainer.this.getComponentAdapters();
485 }
486
487 public <T> List<ComponentAdapter<T>> getComponentAdapters(Class<T> componentType) {
488 return DefaultClassLoadingPicoContainer.this.getComponentAdapters(componentType);
489 }
490
491 public <T> List<ComponentAdapter<T>> getComponentAdapters(Class<T> componentType,
492 Class<? extends Annotation> binding) {
493 return DefaultClassLoadingPicoContainer.this.getComponentAdapters(componentType, binding);
494 }
495
496 public <T> List<T> getComponents(Class<T> componentType) {
497 return DefaultClassLoadingPicoContainer.this.getComponents(componentType);
498 }
499
500 public void accept(PicoVisitor visitor) {
501 DefaultClassLoadingPicoContainer.this.accept(visitor);
502 }
503
504 public void start() {
505 //This implementation does nothing on lifecycle triggers.
506 }
507
508 public void stop() {
509 //This implementation does nothing on lifecycle triggers.
510 }
511
512 public void dispose() {
513 //This implementation does nothing on lifecycle triggers.
514 }
515
516 public void setName(String name) {
517 DefaultClassLoadingPicoContainer.this.setName(name);
518 }
519
520 public void setLifecycleState(LifecycleState lifecycleState) {
521 DefaultClassLoadingPicoContainer.this.setLifecycleState(lifecycleState);
522 }
523
524 public Converters getConverter() {
525 return DefaultClassLoadingPicoContainer.this.getConverters();
526 }
527
528 /**
529 * {@inheritDoc}
530 * @see org.picocontainer.MutablePicoContainer#getLifecycleState()
531 */
532 public LifecycleState getLifecycleState() {
533 return DefaultClassLoadingPicoContainer.this.getLifecycleState();
534 }
535
536 /**
537 * {@inheritDoc}
538 * @see org.picocontainer.MutablePicoContainer#getName()
539 */
540 public String getName() {
541 return DefaultClassLoadingPicoContainer.this.getName();
542 }
543
544 }
545
546 public int visit(ClassName thisClassesPackage, String regex, boolean recursive, ClassVisitor classNameVisitor) {
547 Class clazz = loadClass(thisClassesPackage);
548 /* File Seperator of '\\' can cause bogus results in Windows -- So we keep it to forward slash since Windows
549 * can handle it.
550 * -MR
551 */
552 String pkgName = clazz.getPackage().getName().replace(".", "/");
553 CodeSource codeSource = clazz.getProtectionDomain().getCodeSource();
554 if(codeSource == null) {
555 throw new PicoCompositionException("no codesource for " + thisClassesPackage);
556 }
557 String codeSourceRoot = codeSource.getLocation().getFile();
558 String fileName = codeSourceRoot + File.separator + pkgName;
559 File file = new File(fileName);
560 Pattern compiledPattern = Pattern.compile(regex);
561 if (file.exists()) {
562 if (file.isFile()) {
563 file = file.getParentFile();
564 }
565 return visit(file, pkgName, compiledPattern, recursive, classNameVisitor);
566 } else {
567 return visit(pkgName, codeSourceRoot, compiledPattern, recursive, classNameVisitor);
568 }
569 }
570
571 public int visit(String pkgName, String codeSourceRoot, Pattern compiledPattern, boolean recursive, ClassVisitor classNameVisitor) {
572 int found = 0;
573 try {
574 ZipFile zip = new ZipFile(new File(codeSourceRoot));
575 for (Enumeration e = zip.entries(); e.hasMoreElements();) {
576 ZipEntry entry = (ZipEntry) e.nextElement();
577 String entryName = entry.getName();
578 if (entryName.startsWith(pkgName) && entryName.endsWith(".class")) {
579 String name = entryName.substring(pkgName.length()+1);
580 if (name.endsWith("XStream.class")) {
581 System.out.println();
582 }
583 int length = name.split("/").length;
584 if (length == 1 || recursive) {
585 found = visit(pkgName, compiledPattern, classNameVisitor, found, entryName.replace("/","."), null);
586 }
587 }
588 }
589 } catch (IOException e) {
590 e.printStackTrace();
591 }
592 return found;
593 }
594
595 public int visit(File pkgDir, String pkgName, Pattern pattern, boolean recursive, ClassVisitor classNameVisitor) {
596 int found = 0;
597 File files[] = pkgDir.listFiles();
598 if(files != null) {
599 for (File file : files) {
600 if (file.isDirectory()) {
601 if (recursive) {
602 found = found + visit(file, pkgName, pattern, recursive, classNameVisitor);
603 }
604 } else {
605 found = visit(pkgName, pattern, classNameVisitor, found, file.getName(),
606 file.getAbsolutePath().replace(File.separatorChar, '/') );
607 }
608 }
609 }
610 return found;
611 }
612
613 private int visit(String pkgName, Pattern pattern, ClassVisitor classNameVisitor, int foundSoFar, String fileName, String absolutePath) {
614 boolean matches = pattern.matcher(fileName).matches();
615 if (matches) {
616 if (absolutePath != null) {
617 String fqn = absolutePath.substring(absolutePath.indexOf(pkgName));
618 fileName = fqn.substring(0, fqn.indexOf(".class")).replace('/', '.');;
619 } else {
620 fileName = fileName.substring(0, fileName.indexOf(".class"));
621 }
622 classNameVisitor.classFound(loadClass(new ClassName(fileName)));
623 foundSoFar++;
624 }
625 return foundSoFar;
626 }
627
628 public interface ClassVisitor {
629 void classFound(Class clazz);
630 }
631
632 }