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 }