001: /* ====================================================================
002: * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
003: *
004: * Copyright (c) 1995-2003 Jcorporate Ltd. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution,
019: * if any, must include the following acknowledgment:
020: * "This product includes software developed by Jcorporate Ltd.
021: * (http://www.jcorporate.com/)."
022: * Alternately, this acknowledgment may appear in the software itself,
023: * if and wherever such third-party acknowledgments normally appear.
024: *
025: * 4. "Jcorporate" and product names such as "Expresso" must
026: * not be used to endorse or promote products derived from this
027: * software without prior written permission. For written permission,
028: * please contact info@jcorporate.com.
029: *
030: * 5. Products derived from this software may not be called "Expresso",
031: * or other Jcorporate product names; nor may "Expresso" or other
032: * Jcorporate product names appear in their name, without prior
033: * written permission of Jcorporate Ltd.
034: *
035: * 6. No product derived from this software may compete in the same
036: * market space, i.e. framework, without prior written permission
037: * of Jcorporate Ltd. For written permission, please contact
038: * partners@jcorporate.com.
039: *
040: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
041: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
042: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
043: * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
044: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
045: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
046: * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
047: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
048: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
049: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
050: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
051: * SUCH DAMAGE.
052: * ====================================================================
053: *
054: * This software consists of voluntary contributions made by many
055: * individuals on behalf of the Jcorporate Ltd. Contributions back
056: * to the project(s) are encouraged when you make modifications.
057: * Please send them to support@jcorporate.com. For more information
058: * on Jcorporate Ltd. and its products, please see
059: * <http://www.jcorporate.com/>.
060: *
061: * Portions of this software are based upon other open source
062: * products and are subject to their respective licenses.
063: */
064: package com.jcorporate.expresso.kernel;
065:
066: import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;
067: import EDU.oswego.cs.dl.util.concurrent.ReadWriteLock;
068: import EDU.oswego.cs.dl.util.concurrent.WriterPreferenceReadWriteLock;
069: import com.jcorporate.expresso.kernel.exception.InstallationException;
070: import com.jcorporate.expresso.kernel.internal.ContainerImplBase;
071: import org.apache.log4j.Logger;
072:
073: import java.util.ArrayList;
074: import java.util.Collections;
075: import java.util.HashMap;
076: import java.util.Iterator;
077: import java.util.List;
078: import java.util.Map;
079:
080: /**
081: * This class is the default Container Implementation used by the Expresso
082: * Framework. You can change this by modifying the SystemFactory.
083: * <p>It provides basic container usability such as component location,
084: * component management, component listeners, etc </p>
085: *
086: * @author Michael Rimov
087: */
088: public class DefaultContainerImpl extends ContainerImplBase {
089:
090: /**
091: * Read Write lock on the container
092: */
093: private ReadWriteLock containerLock = new WriterPreferenceReadWriteLock();
094:
095: /**
096: * The parent container of this component.
097: */
098: private ComponentContainer parent = null;
099:
100: /**
101: * A map of child components keyed by the component name. (java.lang.String)
102: */
103: private Map childComponents = new ConcurrentReaderHashMap();
104:
105: /**
106: * List of all listeners... this map must be locked before using.
107: */
108: private List containerListeners = new ArrayList();
109:
110: /**
111: * Logger instance
112: */
113: private static final Logger log = Logger
114: .getLogger(DefaultContainerImpl.class);
115:
116: /**
117: * Basic constructor.
118: */
119: public DefaultContainerImpl() {
120: super ();
121: }
122:
123: /**
124: * Removes a component from this container.
125: *
126: * @param componentName The name of the component to remove.
127: */
128: public void removeComponent(String componentName) {
129: try {
130: containerLock.writeLock().acquire();
131: } catch (InterruptedException ex) {
132: log
133: .error(
134: "Interrupted waiting for write lock. Aborting method",
135: ex);
136: return;
137: }
138:
139: try {
140: childComponents.remove(componentName);
141: } finally {
142: containerLock.writeLock().release();
143: }
144: }
145:
146: /**
147: * Retrieves a list of instances of all contained ExpressoComponents. Use
148: * this for iterating through the components of a current 'context'. Do not
149: * attempt to modify the map given. Either add or remove a component through
150: * the addComponent or removeComponent methods.
151: *
152: * @return Read only map of the components.
153: */
154: public Map getChildComponents() {
155: try {
156: containerLock.readLock().acquire();
157: } catch (InterruptedException ex) {
158: log
159: .error(
160: "Interrupted waiting for read lock. Aborting method",
161: ex);
162: return new HashMap();
163: }
164: try {
165: return Collections.unmodifiableMap(childComponents);
166: } finally {
167: containerLock.readLock().release();
168: }
169: }
170:
171: /**
172: * Locates an Expresso Service for use by a client.
173: *
174: * @param componentName the name of the service to locate.
175: * @return ExpressoService.
176: * @throws IllegalArgumentException if the service cannot be found.
177: * @throws IllegalStateException if the service exists, but is not in a
178: * 'runnable' state due to some configuration error or other unforeseen
179: * issue.
180: */
181: public ExpressoComponent locateComponent(String componentName) {
182: try {
183: containerLock.readLock().acquire();
184: } catch (InterruptedException ex) {
185: log
186: .error(
187: "Interrupted waiting for read lock. Aborting method",
188: ex);
189: return null;
190: }
191:
192: try {
193: ExpressoComponent returnValue = (ExpressoComponent) childComponents
194: .get(componentName);
195: if (returnValue == null && _getParentContainer() != null) {
196: returnValue = _getParentContainer().locateComponent(
197: componentName);
198: }
199:
200: return returnValue;
201: } finally {
202: containerLock.readLock().release();
203: }
204: }
205:
206: /**
207: * Install a component into the system. If newComponent implements <code>
208: * installable</code> then it shall be installed. After that, the component
209: * is added.
210: *
211: * @param newComponent An instance of the component to install.
212: * @param installOptions any installation options [optional]
213: * @param installLog a Logger-like interface to a component tha records the process
214: * of the installation including any errors, etc.
215: */
216: public void installComponent(ExpressoComponent newComponent,
217: InstallationOptions installOptions, InstallLog installLog) {
218: if (log.isInfoEnabled()) {
219: log.info("Installing component: "
220: + newComponent.getMetaData().getName() + " class: "
221: + newComponent.getClass().getName());
222: }
223: try {
224: containerLock.writeLock().acquire();
225: } catch (InterruptedException ex) {
226: log
227: .error(
228: "Interrupted waiting for write lock. Aborting method",
229: ex);
230: installLog
231: .error(
232: "Error getting container lock, Aborting Installation",
233: ex);
234: return;
235: }
236:
237: try {
238: this .addComponent(newComponent);
239:
240: try {
241: if (newComponent instanceof Installable) {
242: Installable i = (Installable) newComponent;
243: i.install(installOptions, installLog);
244: }
245:
246: if (log.isInfoEnabled()) {
247: log.info("Successfully installed component: "
248: + newComponent.getMetaData().getName());
249: }
250: } catch (InstallationException ex) {
251: log.error("Error installing component: "
252: + newComponent.getClass().getName());
253: this .removeComponent(newComponent.getMetaData()
254: .getName());
255: }
256: } finally {
257: containerLock.writeLock().release();
258: }
259: }
260:
261: /**
262: * Uninstalls the component. If the component implements <code>
263: * installable</code> then it shall be uninstalled. After that, it shall
264: * be removed.
265: *
266: * @param componentName the name of the component to uninstall
267: * @param installLog a Logger-like interface to a component tha records the process
268: * of the installation including any errors, etc.
269: * @param installOptions any installation options to use.
270: */
271: public void uninstallComponent(String componentName,
272: InstallationOptions installOptions, InstallLog installLog) {
273: if (log.isInfoEnabled()) {
274: log.info("Installing component: " + componentName);
275: }
276:
277: try {
278: containerLock.writeLock().acquire();
279: } catch (InterruptedException ex) {
280: log
281: .error(
282: "Interrupted waiting for write lock. Aborting method",
283: ex);
284: installLog
285: .error(
286: "Error getting container lock, Aborting Installation",
287: ex);
288: return;
289: }
290:
291: ExpressoComponent removeComponent = null;
292: try {
293:
294: removeComponent = (ExpressoComponent) childComponents
295: .get(componentName);
296: if (removeComponent == null) {
297: throw new IllegalArgumentException("Component: '"
298: + componentName
299: + "' does not exist in this container");
300: }
301:
302: if (removeComponent instanceof Installable) {
303: Installable i = (Installable) removeComponent;
304: i.uninstall(installOptions, installLog);
305: }
306:
307: if (log.isInfoEnabled()) {
308: log.info("Successfully installed component: "
309: + removeComponent.getMetaData().getName());
310: }
311: } catch (InstallationException ex) {
312:
313: log.error("Error installing component: " + componentName);
314: this .removeComponent(removeComponent.getMetaData()
315: .getName());
316: } finally {
317: containerLock.writeLock().release();
318: }
319:
320: }
321:
322: /**
323: * Query the container to see if a particular service name is installed
324: * in the system
325: *
326: * @param componentName the name of the component to query for.
327: * @return true if the service is installed and running.
328: */
329: public boolean isComponentExists(String componentName) {
330: try {
331: containerLock.readLock().acquire();
332: } catch (InterruptedException ex) {
333: log
334: .error(
335: "Interrupted waiting for read lock. Aborting method",
336: ex);
337: return false;
338: }
339: try {
340: return _isComponentExists(componentName);
341: } finally {
342: containerLock.readLock().release();
343: }
344:
345: }
346:
347: /**
348: * Version of isComponentExists without the locking. Prevents deadlocks
349: * for internal use.
350: *
351: * @param componentName the name of the component
352: * @return true if the component exists
353: */
354: protected boolean _isComponentExists(String componentName) {
355: boolean returnValue = childComponents
356: .containsKey(componentName);
357: if (returnValue == false && _getParentContainer() != null) {
358: returnValue = _getParentContainer().isComponentExists(
359: componentName);
360: }
361: return returnValue;
362: }
363:
364: /**
365: * To register the component for control by the Component Manager. This will
366: * in essense transfer the control of ther service to the Component Manager.
367: * This will often be called by the Configuration Bootstrap system.
368: *
369: * @param newComponent the component to install
370: */
371: public void addComponent(ExpressoComponent newComponent) {
372: if (newComponent == null) {
373: throw new IllegalArgumentException(
374: "newComponent must not be null");
375: }
376:
377: try {
378: containerLock.writeLock().acquire();
379: } catch (InterruptedException ex) {
380: log
381: .error(
382: "Interrupted waiting for read lock. Aborting method",
383: ex);
384: return;
385: }
386:
387: try {
388:
389: if (this ._isComponentExists(newComponent.getMetaData()
390: .getName())) {
391: throw new IllegalArgumentException(
392: "Error adding component: "
393: + newComponent.getClass().getName()
394: + " of name "
395: + newComponent.getMetaData().getName()
396: + " already exists in the container.");
397: }
398:
399: childComponents.put(newComponent.getMetaData().getName(),
400: newComponent);
401: } finally {
402: containerLock.writeLock().release();
403: }
404: }
405:
406: /**
407: * Return the parent container
408: *
409: * @return ContainerImpl interface
410: */
411: public ComponentContainer getParentContainer() {
412: try {
413: containerLock.readLock().acquire();
414: } catch (InterruptedException ex) {
415: log
416: .error(
417: "Interrupted waiting for read lock. Aborting method",
418: ex);
419: return null;
420: }
421:
422: try {
423: return parent;
424: } finally {
425: containerLock.readLock().release();
426: }
427: }
428:
429: /**
430: * getParentContainer without the locking
431: *
432: * @return ComponentContainer instance
433: */
434: protected ComponentContainer _getParentContainer() {
435: return parent;
436: }
437:
438: /**
439: * Set the parent container of this container
440: *
441: * @param newParent the new Parent Container
442: */
443: public void setParentContainer(ComponentContainer newParent) {
444: try {
445: containerLock.writeLock().acquire();
446: } catch (InterruptedException ex) {
447: log
448: .error(
449: "Interrupted waiting for read lock. Aborting method",
450: ex);
451: return;
452: }
453:
454: try {
455: parent = newParent;
456: } finally {
457: containerLock.writeLock().release();
458: }
459: }
460:
461: /**
462: * Global Container Destruction. Iterate depth first through all the subcompoennts,
463: * possibly calling stop, and possibly calling destroy.
464: */
465: public void destroyContainer() {
466: try {
467: containerLock.writeLock().acquire();
468: } catch (InterruptedException ex) {
469: log
470: .error(
471: "Interrupted waiting for read lock. Aborting method",
472: ex);
473: return;
474: }
475:
476: try {
477: if (log.isInfoEnabled()) {
478: log.info("Destroying Container "
479: + this .getClass().getName());
480: }
481:
482: for (Iterator i = childComponents.values().iterator(); i
483: .hasNext();) {
484: Object nextObject = i.next();
485: String componentName = nextObject.getClass().getName();
486: if (nextObject instanceof Startable) {
487: if (log.isInfoEnabled()) {
488: log.info("Stopping : " + componentName);
489: }
490:
491: try {
492: ((Startable) nextObject).stop();
493: } catch (Throwable ex) {
494: log.error("Error stopping component: "
495: + nextObject.getClass().getName(), ex);
496: }
497: }
498:
499: if (nextObject instanceof Containable) {
500: try {
501: ((Containable) nextObject)
502: .getContainerImplementation()
503: .destroyContainer();
504: } catch (Throwable ex) {
505: log.error("Error destroying container: "
506: + nextObject.getClass().getName(), ex);
507: }
508: }
509:
510: if (nextObject instanceof ComponentLifecycle) {
511: if (log.isInfoEnabled()) {
512: log.info("Destroying : " + componentName);
513: try {
514: ((ComponentLifecycle) nextObject).destroy();
515: } catch (Throwable ex) {
516: log.error("Error destroying component: "
517: + nextObject.getClass().getName(),
518: ex);
519: }
520: }
521:
522: }
523: }
524:
525: childComponents.clear();
526: } catch (Throwable t) {
527: log.error("Error destroying component", t);
528: } finally {
529: containerLock.writeLock().release();
530: }
531: }
532:
533: /**
534: * Just in case..... if all references to the root container have been
535: * released and GC is running, then make sure we 'cleanly' destroy all
536: * nested components. Of course, since finalize() doesn't necessarily
537: * get called, this isn't a guaranteed fixit.. but at least it's a shot
538: * in the right direction
539: */
540: protected void finalize() throws java.lang.Throwable {
541: if (childComponents != null && !childComponents.isEmpty()) {
542: if (log != null) {
543: log.warn("Container getting GC'ed and subcomponents "
544: + "have not been destroyed.");
545: }
546: destroyContainer();
547: }
548:
549: super.finalize();
550: }
551:
552: }
|