0001: /*
0002: * <copyright>
0003: *
0004: * Copyright 2000-2004 BBNT Solutions, LLC
0005: * under sponsorship of the Defense Advanced Research Projects
0006: * Agency (DARPA).
0007: *
0008: * You can redistribute this software and/or modify it under the
0009: * terms of the Cougaar Open Source License as published on the
0010: * Cougaar Open Source Website (www.cougaar.org).
0011: *
0012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
0013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
0014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
0015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
0016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
0022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0023: *
0024: * </copyright>
0025: */
0026: package org.cougaar.core.component;
0027:
0028: import java.util.ArrayList;
0029: import java.util.Collection;
0030: import java.util.Collections;
0031: import java.util.Iterator;
0032: import java.util.List;
0033: import java.util.Map;
0034: import java.beans.beancontext.*; /*make @see reference work*/
0035:
0036: import org.cougaar.util.GenericStateModel;
0037: import org.cougaar.util.GenericStateModelAdapter;
0038: import org.cougaar.util.RarelyModifiedList;
0039: import org.cougaar.util.Mappings;
0040: import org.cougaar.util.Mapping;
0041: import org.cougaar.util.Filters;
0042: import org.cougaar.util.UnaryPredicate;
0043: import org.cougaar.util.log.Logger;
0044: import org.cougaar.util.log.Logging;
0045:
0046: /**
0047: * The standard implementation of a Container.
0048: * <p>
0049: * Although this implementation defines many protected methods,
0050: * it is long overdue for a major refactor, so developers should avoid
0051: * complex subclassing to allow for future ContainerSupport cleanup.
0052: * A simple ContainerSupport subclass, such as this example in
0053: * core:<br>
0054: * <code>org.cougaar.core.wp.resolver.ResolverContainer</code><br>
0055: * will only define the two required abstract methods and allow
0056: * child components to advertise any necessary services.
0057: * <p>
0058: * Future refactor ideas include:<ul>
0059: * <li>Obvious overall code cleanup into smaller classes and
0060: * cleaner code.</li>
0061: * <li>Remove last remnants of component proprity (HIGH/BINDER/etc),
0062: * since the core no longer uses it (bug 2522).</li>
0063: * <li>Replace "*.Binder" and ".BinderFactory" insertion point
0064: * extension and "attachBinderFactory" with a new
0065: * "BinderFactoryService" advertised by this container,
0066: * and fix BinderFactories/Binders to use the new service.</li>
0067: * <li>Make a clearer distinction between binders that instantiate
0068: * components v.s. binders that monitor service access.
0069: * Create a new ComponentFactoryService for the former.</li>
0070: * <li>Extract state model actions (load/start/etc) to a
0071: * binder or StateModelService, to make it easier for binders
0072: * to intercept these calls and allow custom state model
0073: * implementations (e.g. better threading, new states,
0074: * etc).</li>
0075: * <li>With the above changes, limit Binders to ServiceBroker
0076: * interactions and remove "ContainerProxy" and literal
0077: * binder chaining -- to support service-focused binders,
0078: * all we need to do is intercept ServiceBroker calls!</li>
0079: * <li>Plenty more to do..</li>
0080: * </ul>
0081: */
0082: public abstract class ContainerSupport extends GenericStateModelAdapter
0083: implements Container, StateObject {
0084: private static final boolean ENABLE_VIEW_SERVICE = true;
0085:
0086: protected final ComponentFactory componentFactory = specifyComponentFactory();
0087: /** this is the prefix that all subcomponents must have as a prefix **/
0088: protected final String containmentPrefix = specifyContainmentPoint()
0089: + ".";
0090:
0091: protected Object loadState = null;
0092:
0093: private ComponentDescriptions externalComponentDescriptions = null;
0094:
0095: // my service broker
0096: protected ServiceBroker serviceBroker = null;
0097:
0098: // the service broker for all child components
0099: protected ServiceBroker childServiceBroker = null;
0100:
0101: /** The actual set of child BoundComponent loaded.
0102: * @see org.cougaar.core.component.BoundComponent
0103: **/
0104: private final RarelyModifiedList boundComponents = new RarelyModifiedList();
0105:
0106: /** a Sorted Collection of child BinderFactory components.
0107: * Note that we cannot use TreeSet because of the Collection API's
0108: * braindamaged insistance on conflating identity and order, so
0109: * this is a regular arraylist which we keep sorted by hand.
0110: **/
0111: protected final ArrayList binderFactories = new ArrayList();
0112:
0113: /**
0114: * The ComponentDescriptions for loaded binder factories.
0115: **/
0116: protected final ArrayList binderFactoryDescriptions = new ArrayList();
0117:
0118: protected ContainerSupport() {
0119: // child service broker
0120: ServiceBroker csb = specifyChildServiceBroker();
0121: if (csb != null)
0122: setChildServiceBroker(csb);
0123:
0124: // binder factory
0125: BinderFactory bf = createBinderFactory();
0126: if (bf != null && !attachBinderFactory(bf)) {
0127: throw new RuntimeException(
0128: "Failed to load the DefaultBinderFactory");
0129: }
0130: }
0131:
0132: /** Overridable by extending classes to capture the BindingSite. **/
0133: public void setBindingSite(BindingSite bs) {
0134: this .serviceBroker = bs.getServiceBroker();
0135:
0136: // late-binding child service broker
0137: ServiceBroker csb = createChildServiceBroker(bs);
0138: if (csb != null) {
0139: setChildServiceBroker(csb);
0140: }
0141: }
0142:
0143: protected ServiceBroker getServiceBroker() {
0144: return serviceBroker;
0145: }
0146:
0147: /** override to specify a different component factory class.
0148: * Called once during initialization.
0149: **/
0150: protected ComponentFactory specifyComponentFactory() {
0151: return new ComponentFactory() {
0152: };
0153: }
0154:
0155: /** override to specify insertion point of this component,
0156: * the parent insertion point which sub Components must match,
0157: * e.g. "Node.AgentManager.Agent.PluginManager"
0158: * this is called once during initialization.
0159: **/
0160: protected abstract String specifyContainmentPoint();
0161:
0162: /** override to specify a the ServiceBroker object to use for children.
0163: * this is called once during initialization.
0164: * Note that this value might be only part of the process for
0165: * actually finding the services for children and/or peers.
0166: * <p>
0167: * Either this method must be overridden to return a non-null value,
0168: * or the subclass must call setChildServiceBroker exactly once with
0169: * a non-null value.
0170: **/
0171: protected ServiceBroker specifyChildServiceBroker() {
0172: return null;
0173: }
0174:
0175: protected ServiceBroker createChildServiceBroker(BindingSite bs) {
0176: return new DefaultServiceBroker(bs);
0177: }
0178:
0179: protected void destroyChildServiceBroker(ServiceBroker csb) {
0180: if (csb instanceof DefaultServiceBroker) {
0181: ((DefaultServiceBroker) csb).myDestroy();
0182: }
0183: }
0184:
0185: protected final void setChildServiceBroker(ServiceBroker sb) {
0186: if (sb == null) {
0187: throw new IllegalArgumentException(
0188: "Specified ServiceBroker must not be null");
0189: }
0190: if (childServiceBroker != null) {
0191: throw new IllegalArgumentException(
0192: "ServiceBroker already set");
0193: }
0194: childServiceBroker = sb;
0195: }
0196:
0197: /**
0198: * For subclass use, get the service broker shared by the child
0199: * components, which the container subclass can use to directly
0200: * add services.
0201: * <p>
0202: * Subclassing container is messy, and there are only a few
0203: * examples in Cougaar where it is done, and even those should
0204: * probably be refactored away.
0205: * <p>
0206: * Here we return a throw-away ViewedServiceBroker to intercept
0207: * "addService" calls, otherwise the ViewService for the service
0208: * clients will lack the provider "id" and component description.
0209: */
0210: protected ServiceBroker getChildServiceBroker() {
0211: ServiceBroker csb = childServiceBroker;
0212: if (!ENABLE_VIEW_SERVICE) {
0213: return csb;
0214: }
0215: ComponentView cv = getContainerView();
0216: if (cv == null) {
0217: return csb;
0218: }
0219: final int id = cv.getId();
0220: final ComponentDescription cd = cv.getComponentDescription();
0221: return new ViewedServiceBroker(csb, id, cd, null, null);
0222: }
0223:
0224: /** Return (or construct) a serviceBroker instance for
0225: * a particular child component.
0226: * <p>The default is to ignore the child and return the value
0227: * of getChildServiceBroker with no arguments.
0228: * <p>This can be used to build per-child ServiceBroker instances
0229: * as a simpler option than adding an additional high-priority wrapping binder.
0230: * See also getChildContainerProxy.
0231: **/
0232: protected ServiceBroker getChildServiceBroker(Binder b, Object child) {
0233: ServiceBroker csb = childServiceBroker;
0234: if (!ENABLE_VIEW_SERVICE) {
0235: return csb;
0236: }
0237: int id = ViewedServiceBroker.nextId();
0238: ComponentDescription cd = (child instanceof ComponentDescription ? ((ComponentDescription) child)
0239: : null);
0240: ContainerView parentView = getContainerView();
0241: // note that the parentView can be null, which occurs in the root
0242: // container or if a binder blocks access. This is fine.
0243: return new ViewedServiceBroker(csb, id, cd, parentView, b);
0244: }
0245:
0246: // package-private for ContainerBinderSupport access
0247: List getChildViews() {
0248: if (!ENABLE_VIEW_SERVICE) {
0249: return Collections.EMPTY_LIST;
0250: }
0251: int nbf = binderFactoryDescriptions.size();
0252: List ret = new ArrayList(nbf + boundComponents.size());
0253: // ugly hack for binders, since they're not created like
0254: // normal components, but at least we know the compDesc.
0255: // See above RFE for BinderFactoryService!
0256: for (int i = 0; i < nbf; i++) {
0257: Object o = binderFactoryDescriptions.get(i);
0258: if (o instanceof ComponentDescription) {
0259: final ComponentDescription cd = (ComponentDescription) o;
0260: // better to have a throw-away id that none at all?
0261: final int id = ViewedServiceBroker.nextId();
0262: ComponentView cv = new ComponentView() {
0263: public int getId() {
0264: return id;
0265: }
0266:
0267: public long getTimestamp() {
0268: return 0;
0269: }
0270:
0271: public ComponentDescription getComponentDescription() {
0272: return cd;
0273: }
0274:
0275: public ContainerView getParentView() {
0276: return null;
0277: }
0278:
0279: public Map getAdvertisedServices() {
0280: return null;
0281: }
0282:
0283: public Map getObtainedServices() {
0284: return null;
0285: }
0286: };
0287: ret.add(cv);
0288: }
0289: }
0290: for (Iterator it = boundComponents.iterator(); it.hasNext();) {
0291: BoundComponent bc = (BoundComponent) it.next();
0292: ret.add(bc.getComponentView());
0293: }
0294: return Collections.unmodifiableList(ret);
0295: }
0296:
0297: private boolean gotContainerView;
0298: private ContainerView containerView;
0299:
0300: private ContainerView getContainerView() {
0301: if (!ENABLE_VIEW_SERVICE) {
0302: return null;
0303: }
0304: if (!gotContainerView) {
0305: ViewService vs = (ViewService) serviceBroker.getService(
0306: this , ViewService.class, null);
0307: if (vs != null) {
0308: ComponentView pv = vs.getComponentView();
0309: if (pv instanceof ContainerView) {
0310: containerView = (ContainerView) pv;
0311: }
0312: }
0313: gotContainerView = true;
0314: }
0315: return containerView;
0316: }
0317:
0318: protected BinderFactory createBinderFactory() {
0319: return new DefaultBinderFactory();
0320: }
0321:
0322: //
0323: // implement collection
0324: //
0325:
0326: public int size() {
0327: return boundComponents.size();
0328: }
0329:
0330: public boolean isEmpty() {
0331: return boundComponents.isEmpty();
0332: }
0333:
0334: public boolean contains(Object o) {
0335: // note that we don't sync on boundComponents here
0336: if (o instanceof ComponentDescription) {
0337: ComponentDescription cd = (ComponentDescription) o;
0338: String ip = cd.getInsertionPoint();
0339: if (!(ip.startsWith(containmentPrefix))) {
0340: return false;
0341: }
0342: final boolean isDirectChild = (0 >= ip.indexOf('.',
0343: containmentPrefix.length()));
0344:
0345: for (Iterator it = boundComponents.iterator(); it.hasNext();) {
0346: Object oi = it.next();
0347: if (!(oi instanceof BoundComponent)) {
0348: continue;
0349: }
0350: BoundComponent bc = (BoundComponent) oi;
0351: Object bcc = bc.getComponent();
0352: if (!(bcc instanceof ComponentDescription)) {
0353: continue;
0354: }
0355: ComponentDescription bccd = (ComponentDescription) bcc;
0356: if (isDirectChild) {
0357: // at this level in hierarchy
0358: if (cd.equals(bccd)) {
0359: return true;
0360: }
0361: } else {
0362: // child container
0363: Binder bcb = bc.getBinder();
0364: if ((bcb instanceof ContainerBinder)
0365: && (ip.startsWith(bccd.getInsertionPoint()))
0366: && (((ContainerBinder) bcb).contains(cd))) {
0367: return true;
0368: }
0369: }
0370: }
0371: } else if (o instanceof Component) {
0372: // FIXME no good way to find the insertion point!
0373: for (Iterator it = boundComponents.iterator(); it.hasNext();) {
0374: BoundComponent bc = (BoundComponent) it.next();
0375: if (bc.getComponent().equals(o))
0376: return true;
0377: }
0378: }
0379: return false;
0380: }
0381:
0382: /** map BoundComponent to Component (usually ComponentDescription) **/
0383: private static final Mapping map_bc2c = new Mapping() {
0384: public final Object map(Object o) {
0385: return ((BoundComponent) o).getComponent();
0386: }
0387: };
0388:
0389: /** return an iterator of the boundComponents by component **/
0390: public Iterator iterator() {
0391: return componentIterator();
0392: }
0393:
0394: public Iterator componentIterator() {
0395: return Mappings.map(map_bc2c, boundComponents.iterator());
0396: }
0397:
0398: protected final Iterator bcIterator() {
0399: return boundComponents.iterator();
0400: }
0401:
0402: public boolean containsComponent(ComponentDescription cd) {
0403: for (Iterator it = iterator(); it.hasNext();) {
0404: if (cd.equals(it.next())) {
0405: return true;
0406: }
0407: }
0408: return false;
0409: }
0410:
0411: private static final UnaryPredicate pred_isComponentDescription = new UnaryPredicate() {
0412: public final boolean execute(Object o) {
0413: return o instanceof ComponentDescription;
0414: }
0415: };
0416:
0417: /** Get a List of all child components by description.
0418: * @note this creates a new list each call unless it is empty.
0419: * it is better to use the iterator.
0420: */
0421: protected List listComponents() {
0422: return (List) Filters.filter(iterator(),
0423: pred_isComponentDescription);
0424: }
0425:
0426: protected List getBoundComponentList() {
0427: return boundComponents.getUnmodifiableList();
0428: }
0429:
0430: protected Iterator getBoundComponentIterator() {
0431: return boundComponents.iterator();
0432: }
0433:
0434: /** Map BoundComponent to Binder **/
0435: private static final Mapping map_bc2b = new Mapping() {
0436: public final Object map(Object o) {
0437: return ((BoundComponent) o).getBinder();
0438: }
0439: };
0440:
0441: /**
0442: * Get an Iterator of all child Binders.
0443: *
0444: * @see #listBinders
0445: */
0446: protected Iterator binderIterator() {
0447: return Mappings.map(map_bc2b, boundComponents.iterator());
0448: }
0449:
0450: /**
0451: * Get a List of all child Binders.
0452: * @note this creates a new list each call unless it is empty.
0453: * it is better to use the binderIterator.
0454: */
0455: protected List listBinders() {
0456: Iterator it = binderIterator();
0457: if (!it.hasNext()) {
0458: return Collections.EMPTY_LIST;
0459: } else {
0460: List l = new ArrayList();
0461: while (it.hasNext()) {
0462: l.add(it.next());
0463: }
0464: return l;
0465: }
0466: }
0467:
0468: /**
0469: * Add to the container, returning true if the object
0470: * is added, false if it is already contained, and throw an
0471: * exception in all other cases.
0472: */
0473: public boolean add(Object o) {
0474: ComponentDescription cd;
0475: Object cstate;
0476: if (o instanceof ComponentDescription) {
0477: // typical component description
0478: cd = (ComponentDescription) o;
0479: cstate = null;
0480: } else if (o instanceof StateTuple) {
0481: // description plus initial state
0482: StateTuple st = (StateTuple) o;
0483: cd = st.getComponentDescription();
0484: cstate = st.getState();
0485: } else if (o instanceof BinderFactory) {
0486: // unusual case -- prefer to load from cd
0487: return attachBinderFactory((BinderFactory) o);
0488: } else if (o instanceof Component) {
0489: // unusual case -- prefer to load from cd
0490: return addComponent(o, null);
0491: } else {
0492: // not a clue.
0493: throw new IllegalArgumentException(
0494: "Unsupported container element type: "
0495: + ((o != null) ? o.getClass().getName()
0496: : "null"));
0497: }
0498:
0499: // load from a component description
0500: String ip = ((cd != null) ? cd.getInsertionPoint() : null);
0501: if (ip == null) {
0502: // description or insertion point not specified
0503: throw new IllegalArgumentException(
0504: "ComponentDescription must specify an insertion point");
0505: }
0506: if (!(ip.startsWith(containmentPrefix))) {
0507: // wrong insertion point
0508: throw new IncorrectInsertionPointException(
0509: "Insertion point " + ip
0510: + " doesn't match container's "
0511: + containmentPrefix, cd);
0512: }
0513:
0514: // match! - now do we load it here or below - look for any more
0515: // dots beyond the one trailing the prefix...
0516: String tail = ip.substring(containmentPrefix.length());
0517: if ("Binder".equals(tail) || "BinderFactory".equals(tail)) {
0518: // load binder factory, ignore cstate?
0519: return loadBinderFactory(cd);
0520: }
0521:
0522: boolean isDirectChild = (0 >= tail.indexOf('.'));
0523:
0524: if (isDirectChild) { // no more dots
0525: // already loaded?
0526: if (containsComponent(cd))
0527: return false;
0528:
0529: return addComponent(cd, cstate);
0530: } else { // more dots: try inserting in subcomponents
0531: for (Iterator it = boundComponents.iterator(); it.hasNext();) {
0532: BoundComponent bc = (BoundComponent) it.next();
0533:
0534: Binder b = bc.getBinder();
0535: if (b instanceof ContainerBinder) {
0536: Object bcc = bc.getComponent();
0537: if (bcc instanceof ComponentDescription) {
0538: ComponentDescription bccd = (ComponentDescription) bcc;
0539: if (!(ip.startsWith(bccd.getInsertionPoint()))) {
0540: continue;
0541: }
0542: } else {
0543: // non-desc child, but okay
0544: }
0545:
0546: try {
0547: boolean ret = ((ContainerBinder) b).add(o);
0548: // child already contains or added it
0549: return ret;
0550: } catch (IncorrectInsertionPointException ipE) {
0551: // wrong insertion point
0552: }
0553: }
0554: }
0555: }
0556: // not at this level, and no child accepted it
0557: throw new ComponentLoadFailure(
0558: "Component not loaded by this container ("
0559: + containmentPrefix + ") or its children", cd);
0560: }
0561:
0562: public boolean remove(Object o) {
0563: if (!(o instanceof ComponentDescription)) {
0564: // could support Components, but not required at this time
0565: throw new UnsupportedOperationException(
0566: "Removal of non-ComponentDescription not supported");
0567: }
0568:
0569: ComponentDescription cd = (ComponentDescription) o;
0570: String ip = cd.getInsertionPoint();
0571: if ((ip == null) || (!(ip.startsWith(containmentPrefix)))) {
0572: return false;
0573: }
0574: String tail = ip.substring(containmentPrefix.length());
0575:
0576: if ("Binder".equals(tail) || "BinderFactory".equals(tail)) {
0577: throw new UnsupportedOperationException(
0578: "Binder and BinderFactory removal not supported ("
0579: + ip + ")");
0580: }
0581:
0582: boolean isDirectChild = (0 >= tail.indexOf('.'));
0583:
0584: // find the child and remove it
0585: Binder removedBinder = null;
0586: synchronized (boundComponents) {
0587: for (Iterator it = boundComponents.iterator(); it.hasNext();) {
0588: BoundComponent bc = (BoundComponent) it.next();
0589:
0590: Object bcc = bc.getComponent();
0591: if (!(bcc instanceof ComponentDescription)) {
0592: continue;
0593: }
0594: ComponentDescription bccd = (ComponentDescription) bcc;
0595: if (isDirectChild) {
0596: // at this level in hierarchy
0597: if (cd.equals(bccd)) {
0598: removedBinder = bc.getBinder();
0599: boundComponents.remove(bc); // cannot use it.remove() here
0600: break;
0601: }
0602: } else {
0603: // child container
0604: String bctail = bccd.getInsertionPoint().substring(
0605: containmentPrefix.length());
0606:
0607: if (tail.startsWith(bctail)) {
0608: Binder bcb = bc.getBinder();
0609: if ((bcb instanceof ContainerBinder)
0610: && (((ContainerBinder) bcb).remove(cd))) {
0611: // bail out completely - need not remove anything locally
0612: return true;
0613: }
0614: }
0615: }
0616: }
0617: }
0618:
0619: if (removedBinder == null) {
0620: return false;
0621: } // wasn't there to remove
0622:
0623: // unload the removed direct child
0624: try {
0625: switch (removedBinder.getModelState()) {
0626: case GenericStateModel.ACTIVE:
0627: removedBinder.suspend();
0628: // fall-through
0629: case GenericStateModel.IDLE:
0630: removedBinder.stop();
0631: // fall-through
0632: case GenericStateModel.LOADED:
0633: removedBinder.unload();
0634: // fall-through
0635: case GenericStateModel.UNLOADED:
0636: // unloaded
0637: break;
0638: default:
0639: // should never happen
0640: throw new IllegalStateException(
0641: "Illegal binder state: "
0642: + removedBinder.getModelState());
0643: }
0644: } catch (RuntimeException e) {
0645: throw new ComponentRuntimeException(
0646: "Removed component with unclean unload", cd, e);
0647: }
0648:
0649: return true;
0650: }
0651:
0652: /** Add all elements of Collection to the collection, in the order specified **/
0653: public boolean addAll(Collection c) {
0654: boolean allAdded = true;
0655: for (Iterator it = c.iterator(); it.hasNext();) {
0656: Object o = it.next();
0657: boolean succ = add(o);
0658: allAdded = allAdded && (!succ);
0659: }
0660: return allAdded;
0661: }
0662:
0663: // unsupported Collection ops
0664: public void clear() {
0665: throw new UnsupportedOperationException();
0666: }
0667:
0668: public Object[] toArray() {
0669: throw new UnsupportedOperationException();
0670: }
0671:
0672: public Object[] toArray(Object[] ignore) {
0673: throw new UnsupportedOperationException();
0674: }
0675:
0676: public boolean containsAll(Collection c) {
0677: throw new UnsupportedOperationException();
0678: }
0679:
0680: public boolean removeAll(Collection c) {
0681: throw new UnsupportedOperationException();
0682: }
0683:
0684: public boolean retainAll(Collection c) {
0685: throw new UnsupportedOperationException();
0686: }
0687:
0688: /**
0689: * Add a component into our set. We are sure that this is
0690: * the requested level, but might not be certain how much we trust
0691: * it as of yet. In particular, we may need to treat different classes
0692: * of Components differently.
0693: * <p>
0694: * The component (and the binder tree) will be transitioned to
0695: * our container's current model state (e.g. ACTIVE).
0696: *
0697: * @return true on success.
0698: * @throws ComponentLoadFailure when the component can not be loaded.
0699: **/
0700: protected boolean addComponent(Object c, Object cstate) {
0701: Binder b = bindComponent(c);
0702: if (b == null) {
0703: throw new ComponentLoadFailure("No binder found", c);
0704: }
0705: ServiceBroker sb = getChildServiceBroker(b, c);
0706: BindingUtility.setBindingSite(b, getChildContainerProxy(c, sb));
0707: BindingUtility.setServices(b, sb);
0708: if (cstate != null) {
0709: b.setState(cstate);
0710: }
0711: ComponentView cv = (ENABLE_VIEW_SERVICE
0712: && (sb instanceof ViewedServiceBroker) ? ((ViewedServiceBroker) sb)
0713: .getComponentView()
0714: : null);
0715: BoundComponent bc = new BoundComponent(b, c, cv);
0716: boundComponents.add(bc);
0717:
0718: // transition state to match our container's state
0719: int myState = getModelState();
0720: boolean suspend = (myState == GenericStateModel.IDLE);
0721: boolean start = (suspend || myState == GenericStateModel.ACTIVE);
0722: boolean load = (start || myState == GenericStateModel.LOADED);
0723: boolean init = (load || myState != GenericStateModel.UNINITIALIZED);
0724: if (init) {
0725: b.initialize();
0726: if (load) {
0727: b.load();
0728: if (start) {
0729: b.start();
0730: if (suspend) {
0731: b.suspend();
0732: }
0733: }
0734: }
0735: }
0736: return true;
0737: }
0738:
0739: /**
0740: * Find a BinderFactory willing to bind this component, then
0741: * wrap it with BinderFactoryWrappers.
0742: */
0743: protected Binder bindComponent(Object c) {
0744: synchronized (binderFactories) {
0745: ArrayList wrappers = null;
0746:
0747: // find a binder factory that will bind this component
0748: Binder b = null;
0749: for (Iterator i = binderFactories.iterator(); i.hasNext();) {
0750: BinderFactory bf = (BinderFactory) i.next();
0751: if (bf instanceof BinderFactoryWrapper) {
0752: if (wrappers == null) {
0753: wrappers = new ArrayList(1);
0754: }
0755: wrappers.add(bf);
0756: } else {
0757: b = bf.getBinder(c);
0758: if (b != null) {
0759: break;
0760: }
0761: }
0762: }
0763:
0764: // now apply any wrappers, iterating from "last to innermost"
0765: if (wrappers != null) {
0766: int l = wrappers.size();
0767: for (int i = l - 1; i >= 0; i--) {
0768: BinderFactoryWrapper bf = (BinderFactoryWrapper) wrappers
0769: .get(i);
0770: Binder w = bf.getBinder((b == null) ? c : b);
0771: if (w != null) {
0772: b = w;
0773: }
0774: }
0775: }
0776:
0777: return b;
0778: }
0779: }
0780:
0781: /** Called when a componentDescription insertion point ends in .Binder or .BinderFactory
0782: * @throws ComponentFactoryException if the BinderFactory Cannot be loaded.
0783: **/
0784: protected boolean loadBinderFactory(ComponentDescription cd) {
0785: if (checkBinderFactory(cd)) {
0786: Component bfc = componentFactory.createComponent(cd);
0787: if (bfc instanceof BinderFactory) {
0788: binderFactoryDescriptions.add(cd);
0789: return attachBinderFactory((BinderFactory) bfc);
0790: } else {
0791: throw new ComponentLoadFailure("Not a BinderFactory",
0792: cd);
0793: }
0794: } else {
0795: throw new ComponentLoadFailure("Failed BinderFactory test",
0796: cd);
0797: }
0798: }
0799:
0800: /** @return true iff the binderfactory is trusted enought to load **/
0801: protected boolean checkBinderFactory(ComponentDescription cd) {
0802: return true;
0803: }
0804:
0805: /** Activate a binder factory
0806: **/
0807: protected boolean attachBinderFactory(BinderFactory c) {
0808: synchronized (binderFactories) {
0809: binderFactories.add(c);
0810: Collections.sort(binderFactories, BinderFactory.comparator);
0811: }
0812: // FIXME this should be a ViewedServiceBroker!
0813: ServiceBroker sb = childServiceBroker;
0814: return BindingUtility.activate(c,
0815: getChildContainerProxy(c, sb), sb);
0816: }
0817:
0818: /** Specifies an object to use as the "parent" proxy object
0819: * for a particular object (generally an <em>unbound</em> component).
0820: * This will be either be the Container itself (this) or a
0821: * simple proxy for the container so that BinderFactory instances
0822: * cannot downcast the object to get additional privileges.
0823: **/
0824: protected ContainerAPI getChildContainerProxy(Object o,
0825: ServiceBroker sb) {
0826: return new DefaultProxy(sb);
0827: }
0828:
0829: /** Matching setter for getExternalComponentDescriptions **/
0830: protected final void setExternalComponentDescriptions(
0831: ComponentDescriptions cds) {
0832: externalComponentDescriptions = cds;
0833: }
0834:
0835: public void setState(Object loadState) {
0836: this .loadState = loadState;
0837: }
0838:
0839: public Object getState() {
0840: return captureState();
0841: }
0842:
0843: protected ComponentDescriptions captureState() {
0844: synchronized (boundComponents) {
0845: List l = new ArrayList(boundComponents.size()
0846: + binderFactoryDescriptions.size());
0847: l.addAll(binderFactoryDescriptions);
0848: for (Iterator it = bcIterator(); it.hasNext();) {
0849: BoundComponent bc = (BoundComponent) it.next();
0850: Object comp = bc.getComponent();
0851: if (comp instanceof ComponentDescription) {
0852: ComponentDescription cd = (ComponentDescription) comp;
0853: Binder b = bc.getBinder();
0854: Object state = b.getState();
0855: StateTuple ti = new StateTuple(cd, state);
0856: l.add(ti);
0857: } else {
0858: // error?
0859: }
0860: }
0861: return new ComponentDescriptions(l);
0862: }
0863: }
0864:
0865: protected abstract ComponentDescriptions findInitialComponentDescriptions();
0866:
0867: /** return a ComponentDescriptions object which contains a set of ComponentDescriptions
0868: * defined externally to be loaded into this Container.
0869: * This will return the value computed by findExternalComponentDescriptions()
0870: * Note that this value is not filled until ContainerSupport.load() has been called.
0871: * @see #findExternalComponentDescriptions
0872: * @return null if no component descriptions specified.
0873: */
0874: protected final ComponentDescriptions getExternalComponentDescriptions() {
0875: return externalComponentDescriptions;
0876: }
0877:
0878: /** Extending classes may override this method to define a set of externally-specified
0879: * Component to load. Will be called during load() immediately after super.load().
0880: * load will set the value returned by getExternalComponentDescriptions(), so that
0881: * loadInternalSubcomponents, etc can retrieve the values.
0882: * @return null if none.
0883: */
0884: protected ComponentDescriptions findExternalComponentDescriptions() {
0885: if (loadState instanceof ComponentDescriptions) {
0886: ComponentDescriptions descs = (ComponentDescriptions) loadState;
0887: loadState = null;
0888: return descs;
0889: } else {
0890: // ask the container
0891: return findInitialComponentDescriptions();
0892: }
0893: }
0894:
0895: public void initialize() {
0896: super .initialize();
0897: // not expecting any child components this early
0898: for (Iterator childBinders = binderIterator(); childBinders
0899: .hasNext();) {
0900: Binder b = (Binder) childBinders.next();
0901: b.initialize();
0902: }
0903: }
0904:
0905: /**
0906: * Called by super.load() to transit the state, sets the value of
0907: * getExternalComponentDescriptions()
0908: * by calling
0909: * findExternalComponentDescriptions(),
0910: * then invokes each of
0911: * <ul>
0912: * <li>loadHighPriorityComponents()</li>
0913: * <li>loadInternalPriorityComponents()</li>
0914: * <li>loadBinderPriorityComponents()</li>
0915: * <li>loadComponentPriorityComponents()</li>
0916: * <li>loadLowPriorityComponents()</li>
0917: * </ul>
0918: * in order.
0919: */
0920: public void load() {
0921: super .load();
0922: setExternalComponentDescriptions(findExternalComponentDescriptions());
0923: loadHighPriorityComponents();
0924: loadInternalPriorityComponents();
0925: loadBinderPriorityComponents();
0926: loadComponentPriorityComponents();
0927: loadLowPriorityComponents();
0928: }
0929:
0930: public void start() {
0931: super .start();
0932: for (Iterator childBinders = binderIterator(); childBinders
0933: .hasNext();) {
0934: Binder b = (Binder) childBinders.next();
0935: b.start();
0936: }
0937: }
0938:
0939: public void suspend() {
0940: super .suspend();
0941: List childBinders = listBinders();
0942: for (int i = childBinders.size() - 1; i >= 0; i--) {
0943: Binder b = (Binder) childBinders.get(i);
0944: b.suspend();
0945: }
0946: }
0947:
0948: public void resume() {
0949: super .resume();
0950: for (Iterator childBinders = binderIterator(); childBinders
0951: .hasNext();) {
0952: Binder b = (Binder) childBinders.next();
0953: b.resume();
0954: }
0955: }
0956:
0957: public void stop() {
0958: super .stop();
0959: List childBinders = listBinders();
0960: for (int i = childBinders.size() - 1; i >= 0; i--) {
0961: Binder b = (Binder) childBinders.get(i);
0962: b.stop();
0963: }
0964: }
0965:
0966: public void halt() {
0967: suspend();
0968: stop();
0969: }
0970:
0971: public void unload() {
0972: super .unload();
0973:
0974: List childBinders = listBinders();
0975: for (int i = childBinders.size() - 1; i >= 0; i--) {
0976: Binder b = (Binder) childBinders.get(i);
0977: b.unload();
0978: }
0979: boundComponents.clear();
0980:
0981: if (childServiceBroker != null) {
0982: destroyChildServiceBroker(childServiceBroker);
0983: childServiceBroker = null;
0984: }
0985: }
0986:
0987: /** Should check the ComponentDescription to see if it should
0988: * be loaded into the Container. Implementations may use any rules
0989: * they'd like. The default implementation returns true IFF the
0990: * insertion point of the ComponentDescription is a direct child
0991: * of the ContainmentPoint. The basic component model only calls
0992: * this method during the default load() methods - e.g. only
0993: * to load components specified by getExternalComponentDescriptions().
0994: *
0995: * @param o either a StateTuple or ComponentDescription
0996: * @return true iff the specified ComponentDescription should be loaded.
0997: **/
0998: protected boolean isSubComponentLoadable(Object o) {
0999: ComponentDescription cd = ((o instanceof StateTuple) ? (((StateTuple) o)
1000: .getComponentDescription())
1001: : ((ComponentDescription) o));
1002: return isInsertionPointLoadable(cd.getInsertionPoint());
1003: }
1004:
1005: /** validate a child component's InsertionPoint. Simpler but
1006: * less flexible mechanism to isSubComponentLoadable.
1007: * <p>The default behavior is to return true IFF the insertion point
1008: * is a direct child of the container's containement prefix.
1009: **/
1010: protected boolean isInsertionPointLoadable(String ip) {
1011: String cpr = containmentPrefix;
1012: int cprl = cpr.length();
1013: boolean r = (ip.startsWith(cpr) && // starts with the containment point+'.'
1014: ip.indexOf(".", cprl + 1) == -1 // no more '.'
1015: );
1016: return r;
1017: }
1018:
1019: /** Hook for loading all the high priority subcomponents. The default implementation
1020: * loads all high priority components which match getSubComponentInsertionPoints().
1021: **/
1022: protected void loadHighPriorityComponents() {
1023: loadSubComponentsByPriority(ComponentDescription.PRIORITY_HIGH);
1024: }
1025:
1026: /** Hook for loading all the internal priority subcomponents. The default implementation
1027: * loads all internal priority components which match getSubComponentInsertionPoints().
1028: **/
1029: protected void loadInternalPriorityComponents() {
1030: loadSubComponentsByPriority(ComponentDescription.PRIORITY_INTERNAL);
1031: }
1032:
1033: /** Hook for loading all the binder priority subcomponents. The default implementation
1034: * loads all binder priority components which match getSubComponentInsertionPoints().
1035: **/
1036: protected void loadBinderPriorityComponents() {
1037: loadSubComponentsByPriority(ComponentDescription.PRIORITY_BINDER);
1038: }
1039:
1040: /** Hook for loading all the component priority subcomponents. The default implementation
1041: * loads all component priority components which match getSubComponentInsertionPoints().
1042: **/
1043: protected void loadComponentPriorityComponents() {
1044: loadSubComponentsByPriority(ComponentDescription.PRIORITY_COMPONENT);
1045: }
1046:
1047: /** Hook for loading all the low priority subcomponents. The default implementation
1048: * loads all low priority components which match getSubComponentInsertionPoints().
1049: **/
1050: protected void loadLowPriorityComponents() {
1051: loadSubComponentsByPriority(ComponentDescription.PRIORITY_LOW);
1052: }
1053:
1054: /** Utility method used buy the Load*PriorityComponents methods.
1055: * adds components as specified by getExternalComponentDescriptions
1056: * of the specified priority.
1057: **/
1058: protected void loadSubComponentsByPriority(int priority) {
1059: ComponentDescriptions cds = getExternalComponentDescriptions();
1060: if (cds == null)
1061: return;
1062:
1063: List pcd = cds.selectComponentDescriptions(priority);
1064: if (pcd == null)
1065: return;
1066:
1067: for (Iterator it = pcd.iterator(); it.hasNext();) {
1068: Object o = it.next();
1069: if (isSubComponentLoadable(o)) {
1070: try {
1071: add(o);
1072: } catch (RuntimeException re) {
1073: Logger l = Logging
1074: .getLogger(ContainerSupport.class);
1075: l.error("Skipping load of " + o + " into " + this ,
1076: re);
1077: }
1078: }
1079: }
1080: }
1081:
1082: //
1083: // support classes
1084: //
1085:
1086: private static class DefaultBinderFactory extends
1087: BinderFactorySupport {
1088: public Binder getBinder(Object child) {
1089: return new DefaultBinder(this , child);
1090: }
1091:
1092: private static class DefaultBinder extends
1093: ContainerBinderSupport implements BindingSite {
1094: public DefaultBinder(BinderFactory bf, Object child) {
1095: super (bf, child);
1096: }
1097:
1098: protected BindingSite getBinderProxy() {
1099: return this ;
1100: }
1101: }
1102: }
1103:
1104: private static class DefaultServiceBroker extends
1105: PropagatingServiceBroker {
1106: public DefaultServiceBroker(BindingSite bs) {
1107: super (bs);
1108: }
1109:
1110: private void myDestroy() {
1111: super .destroy();
1112: }
1113: }
1114:
1115: private class DefaultProxy implements ContainerAPI {
1116: private final ServiceBroker sb;
1117:
1118: public DefaultProxy(ServiceBroker sb) {
1119: this .sb = sb;
1120: }
1121:
1122: public ServiceBroker getServiceBroker() {
1123: return sb;
1124: }
1125:
1126: public boolean remove(Object childComponent) {
1127: return ContainerSupport.this .remove(childComponent);
1128: }
1129:
1130: public void requestStop() {
1131: }
1132: }
1133: }
|