001: /*
002: * The contents of this file are subject to the Sapient Public License
003: * Version 1.0 (the "License"); you may not use this file except in compliance
004: * with the License. You may obtain a copy of the License at
005: * http://carbon.sf.net/License.html.
006: *
007: * Software distributed under the License is distributed on an "AS IS" basis,
008: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
009: * the specific language governing rights and limitations under the License.
010: *
011: * The Original Code is The Carbon Component Framework.
012: *
013: * The Initial Developer of the Original Code is Sapient Corporation
014: *
015: * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
016: */
017:
018: package org.sape.carbon.core.bootstrap;
019:
020: import org.sape.carbon.core.component.ComponentKeeper;
021: import org.sape.carbon.core.component.startup.StartupService;
022: import org.sape.carbon.core.config.ConfigurationService;
023: import org.sape.carbon.core.exception.ExceptionUtility;
024:
025: import org.apache.commons.logging.Log;
026: import org.apache.commons.logging.LogFactory;
027:
028: import java.util.Properties;
029: import java.util.Iterator;
030: import java.util.Map;
031: import java.util.Set;
032:
033: /**
034: * <p>This class is Carbon's ultimate source for the config service and
035: * <code>ComponentKeeper</code>. If they have not been loaded already,
036: * this class knows how bootstrap the system to load them.</p>
037: *
038: * <p>All public methods are synchronized such that the first thread to acquire
039: * the lock on this class will load the Carbon Core, all other concurrent
040: * threads will wait for the first to finish. The exception to this is the
041: * getState method. This method is synchronized on a different object so that
042: * threads merely interested in the system state do not need to wait for the
043: * system to load.</p>
044: *
045: * <p>This class is final because Carbon is not designed to have this class
046: * be overridden so extending it makes no sense.</p>
047: *
048: * Copyright 2002 Sapient
049: * @since carbon 1.0
050: * @author Douglas Voet, January 2002
051: * @version $Revision: 1.39 $($Author: ghinkl $ / $Date: 2003/10/16 13:46:23 $)
052: * @stereotype singleton
053: */
054: public final class BootStrapper {
055: /** @link dependency */
056: /*#Loader lnkLoader;*/
057:
058: /** The handle to Apache-commons logger */
059: private Log log = LogFactory.getLog(this .getClass());
060:
061: /** The ConfigurationService for the Carbon Core */
062: private ConfigurationService configurationService = null;
063:
064: /** The ComponentKeeper for the Carbon Core */
065: private ComponentKeeper componentKeeper = null;
066:
067: /**
068: * Attribute to hold the state of the Carbon Core (is it loaded or not).
069: */
070: private BootStrapperStateEnum state = BootStrapperStateEnum.NOT_LOADED;
071:
072: /**
073: * Object used to synchronize upon when state is accessed.
074: * This will never change, so it is final.
075: */
076: private final Object stateLock = new Object();
077:
078: /**
079: * The deployement properties cache for this bootstrapper
080: */
081: private final DeploymentProperties deploymentProperties;
082:
083: /**
084: * <p>This is a singleton so we have a private constructor to prevent
085: * impropper instantiation.</p>
086: */
087: private BootStrapper() {
088: //log carbon statistics
089: Package pkg = BootStrapper.class.getPackage();
090:
091: if (pkg != null) {
092: if (log.isInfoEnabled()) {
093: log
094: .info("Loading the Carbon Core, version: "
095: + pkg.getSpecificationTitle() + " "
096: + pkg.getSpecificationVersion()
097: + ", Copyright "
098: + pkg.getSpecificationVendor());
099: }
100: } else {
101: if (log.isInfoEnabled()) {
102: log.info("Loading the Carbon Core, no package "
103: + "information available");
104: }
105: }
106:
107: this .deploymentProperties = new DeploymentProperties();
108: }
109:
110: /**
111: * <p>Gets a reference to the root configuration provider for the
112: * system.</p>
113: *
114: * @return a reference to the configuration service
115: */
116: public synchronized ConfigurationService fetchConfigurationService() {
117: load();
118: return this .configurationService;
119: }
120:
121: /**
122: * <p>Gets a reference to the <code>ComponentKeeper</code>
123: * for the system.</p>
124: *
125: * @return a reference to the component keeper
126: */
127: public synchronized ComponentKeeper fetchComponentKeeper() {
128: load();
129: return this .componentKeeper;
130: }
131:
132: /**
133: * <p>
134: * This method is responsible for making the calls to load of the Carbon
135: * Core. It delegates to a <code>Loader</code> to get references to the
136: * configuration service and the <code>ComponentKeeper</code> for the
137: * system.
138: * </p><p>
139: * It is synchronized so it can only be run by a single thread. It also
140: * contains logic so that it will only run once.
141: * </p>
142: */
143: public synchronized void load() {
144:
145: // only load if we have not loaded already, we can tell this if
146: // the configurationService is null or the
147: // componentKeeper is null
148: if (this .configurationService == null
149: || this .componentKeeper == null) {
150:
151: // This is not the prefered place for this message. Ideally
152: // this should be called with the bootstrapping of the
153: // Xml config system. Currently there is no single class
154: // that is loaded once, all are loaded many times.
155: try {
156: javax.xml.parsers.SAXParserFactory saxParserFactory = javax.xml.parsers.SAXParserFactory
157: .newInstance();
158:
159: javax.xml.parsers.SAXParser saxParser = saxParserFactory
160: .newSAXParser();
161:
162: if (log.isInfoEnabled()) {
163: log.info("System default SAXParser is: "
164: + saxParser.getClass().getName());
165: }
166:
167: } catch (Exception e) {
168: // Eat it.
169: }
170:
171: Loader loader = fetchLoader();
172: // 1) get the ConfigurationService
173: try {
174: this .configurationService = loader
175: .fetchConfigurationService();
176: } catch (BootStrapException bse) {
177: System.out
178: .println("The carbon services framework was unable to boot.");
179: bse.printStackTrace();
180: throw bse;
181: }
182:
183: // 2) get the ComponentKeeper
184: this .componentKeeper = loader
185: .fetchComponentKeeper(configurationService);
186: // 3) start startup components
187: try {
188:
189: if (log.isDebugEnabled()) {
190: log.debug("Starting up components configured in ["
191: + STARTUP_COMPONENT_NAME + "]");
192: }
193: StartupService startupService = (StartupService) componentKeeper
194: .fetchComponent(STARTUP_COMPONENT_NAME);
195: startupService.startComponents();
196: } catch (Exception e) {
197: if (log.isWarnEnabled()) {
198: log.warn("Could not start startup components: "
199: + ExceptionUtility
200: .printStackTracesToString(e));
201: }
202: }
203:
204: // set state to BootStrapperStateEnum.LOADED
205: // synchronized to make sure we write the state back to main memory
206: // and that this step is not reordered by the compiler such that it
207: // appears to happen to other threads before the system is loaded
208: synchronized (this .stateLock) {
209: this .state = BootStrapperStateEnum.LOADED;
210: }
211: }
212: }
213:
214: /**
215: * <p>Gets the state of BootStrapper. This method allows you to
216: * determine whether or no the core is up an running yet.</p>
217: * <p>Note that this method is synchronized (within the method, not in
218: * the method signature) in order to return a
219: * correct result <i>always</i>. If a <i>stale</i> result is acceptable in
220: * exchange for better performance, the synchronization can be removed.</p>
221: *
222: * @return BootStrapperStateEnum.LOADED when the BootStrapper has been
223: * loaded, BootStrapperStateEnum.NOT_LOADED otherwise
224: */
225: public BootStrapperStateEnum getState() {
226: // This needs to be synchronized in order to make sure that we get
227: // the most recent copy of state from main memory
228: synchronized (this .stateLock) {
229: return this .state;
230: }
231: }
232:
233: /**
234: * This method provides access to properties that are specific to this
235: * deployment of Carbon.
236: * The system properties are searched for the requested property
237: * first. If it is not found, the value is returned from a properties
238: * file name by BootStrapper.DEPLOYMENT_CONFIG_FILE_NAME
239: * and located using the <code>ClassLoader.getResource</code>
240: * method. Deployment Properties
241: * should be properties that either change based on deployment or are
242: * required before the <code>ConfigurationService</code> is loaded. Cases
243: * of properties varying based on deployment should be minimal. In this
244: * case, the <code>DeploymentService</code> should be used.
245: * <p>
246: * This method has the same semantics as
247: * java.util.Properties.getProperty(String)
248: *
249: * @see java.lang.ClassLoader#getResource(String)
250: * @see java.util.Properties#getProperty(String)
251: * @see org.sape.carbon.services.deployment.DeploymentService
252: *
253: * @param key the name of the property
254: * @return String the value of the property or null if it does not exist
255: */
256: public String getDeploymentProperty(String key) {
257: return this .deploymentProperties.get(key);
258: }
259:
260: /**
261: * This method sets Deployment properties. If the property exists within
262: * the System properties, it is replaced, otherwise, it is written to the
263: * deployment properties maintained by this class. It does not write the
264: * new value to the properties file. This method affects the smallest
265: * scope possible. It will not write to the system wide properties unless
266: * the property already exists because that could affect other applications
267: * running in the same JVM.
268: * <p>
269: * This method has the same semantics as
270: * java.util.Properties.setProperty(String, String)
271: *
272: * @see java.util.Properties#setProperty(String, String)
273: *
274: * @param key the key to store the value in
275: * @param value the value of the deployment property
276: * @return Object the old value of the deployment property
277: */
278: public Object setDeploymentProperty(String key, String value) {
279: return this .deploymentProperties.set(key, value);
280: }
281:
282: /**
283: * <p>
284: * Factory method for getting a <code>Loader</code> to load the system.
285: * This method defaults to using the <code>DefaultLoader</code> unless
286: * the system property defined by BOOT_LOADER_PROPERTY is set on the
287: * commandline. In that case, the value of the system property must refer
288: * to a class that implements <code>Loader</code>.
289: * </p>
290: *
291: * @return the proper loader to load the system
292: */
293: private Loader fetchLoader() {
294:
295: String loaderClassName = this
296: .getDeploymentProperty(BOOT_LOADER_PROPERTY);
297:
298: if (loaderClassName == null || "".equals(loaderClassName)) {
299: loaderClassName = DEFAULT_LOADER_CLASS_NAME;
300: }
301:
302: try {
303:
304: if (log.isDebugEnabled()) {
305: log.debug("Using boot loader [" + loaderClassName
306: + "] to bootstrap the system");
307: }
308: return (Loader) Class.forName(loaderClassName)
309: .newInstance();
310:
311: } catch (ClassCastException cce) {
312: throw new BootStrapException(this .getClass(),
313: "Specified loader [" + loaderClassName
314: + "] was not of the correct type: ["
315: + Loader.class.getName() + "]", cce);
316: } catch (ClassNotFoundException cnfe) {
317: throw new BootStrapException(this .getClass(),
318: "Could not instantiate Loader: [" + loaderClassName
319: + "]", cnfe);
320: } catch (InstantiationException ie) {
321: throw new BootStrapException(this .getClass(),
322: "Could not instantiate Loader: [" + loaderClassName
323: + "]", ie);
324: } catch (IllegalAccessException iae) {
325: throw new BootStrapException(this .getClass(),
326: "Could not instantiate Loader: [" + loaderClassName
327: + "]", iae);
328: }
329: }
330:
331: /**
332: * The bootloader key definition to override the bootloader
333: */
334: public static final String BOOT_LOADER_PROPERTY = "carbon.bootstrap.Loader";
335: /**
336: * The key to the class name of the default loader
337: */
338: private static final String DEFAULT_LOADER_CLASS_NAME = "org.sape.carbon.core.bootstrap.DefaultLoader";
339: /**
340: * The default name of the startup component service
341: */
342: private static final String STARTUP_COMPONENT_NAME = "/core/StartupService";
343: /**
344: * The single static reference to the boot strapper of this system.
345: */
346: private static final BootStrapper INSTANCE = new BootStrapper();
347:
348: /** @link dependency */
349: /*#StartupService lnkStartupService;*/
350:
351: /**
352: * <p>
353: * Static factory method for getting a reference to the
354: * <code>BootStrapper</code>.
355: * </p>
356: *
357: * @return BootStrapper
358: */
359: public static BootStrapper getInstance() {
360: return BootStrapper.INSTANCE;
361: }
362: }
|