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 ******************************************************************************/ 008package org.picocontainer.classname; 009 010import org.picocontainer.ComponentAdapter; 011 012import org.picocontainer.*; 013import org.picocontainer.security.CustomPermissionsURLClassLoader; 014import org.picocontainer.lifecycle.LifecycleState; 015import org.picocontainer.behaviors.Caching; 016import org.picocontainer.containers.AbstractDelegatingMutablePicoContainer; 017 018import java.io.File; 019import java.io.IOException; 020import java.lang.annotation.Annotation; 021import java.lang.reflect.Type; 022import java.net.URL; 023import java.security.AccessController; 024import java.security.CodeSource; 025import java.security.PrivilegedAction; 026import java.security.Permissions; 027import java.util.ArrayList; 028import java.util.Collection; 029import java.util.Enumeration; 030import java.util.HashMap; 031import java.util.Iterator; 032import java.util.List; 033import java.util.Map; 034import java.util.Properties; 035import java.util.regex.Pattern; 036import java.util.zip.ZipEntry; 037import 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") 047public 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}