001: package org.apache.turbine.services;
002:
003: /*
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: import java.util.Hashtable;
023: import java.util.Stack;
024:
025: import org.apache.commons.logging.Log;
026: import org.apache.commons.logging.LogFactory;
027:
028: /**
029: * A generic implementation of <code>InitableBroker</code>.
030: * Functionality provided by the broker includes:
031: *
032: * <ul>
033: *
034: * <li>Maintaining single instance of each <code>Initable</code> in
035: * the system.</li>
036: *
037: * <li>Early initialization of <code>Initables</code> during system
038: * startup.</li>
039: *
040: * <li>Late initialization of <code>Initables</code> before they are
041: * used.</li>
042: *
043: * <li>Providing instances of <code>Initables</code> to requesting
044: * parties.</li>
045: *
046: * <li>Maintaining dependencies between <code>Initables</code> during
047: * early initalization phases, including circular dependencies
048: * detection.</li>
049: *
050: * </ul>
051: *
052: * @author <a href="mailto:burton@apache.org">Kevin Burton</a>
053: * @author <a href="mailto:krzewski@e-point.pl">Rafal Krzewski</a>
054: * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
055: * @version $Id: BaseInitableBroker.java 534527 2007-05-02 16:10:59Z tv $
056: */
057: public abstract class BaseInitableBroker implements InitableBroker {
058: /** A repository of Initable instances. */
059: protected Hashtable initables = new Hashtable();
060:
061: /**
062: * Names of classes being early-initialized are pushed onto this
063: * stack. A name appearing twice indicates a circular dependency
064: * chain.
065: */
066: protected Stack stack = new Stack();
067:
068: /** Logging */
069: private Log log = LogFactory.getLog(this .getClass());
070:
071: /**
072: * Default constructor of InitableBroker.
073: *
074: * This constructor does nothing. Your brokers should be
075: * singletons, therefore their constructors should be
076: * private. They should also have public YourBroker getInstance()
077: * methods.
078: */
079: protected BaseInitableBroker() {
080: }
081:
082: /**
083: * Performs early initialization of an Initable class.
084: *
085: * @param className The name of the class to be initialized.
086: * @param data An Object to be used for initialization activities.
087: * @exception InitializationException Initialization was not successful.
088: */
089: public void initClass(String className, Object data)
090: throws InitializationException {
091: // make sure that only one thread calls this method recursively
092: synchronized (stack) {
093: int pos = stack.search(className);
094: if (pos != -1) {
095: StringBuffer msg = new StringBuffer()
096: .append(className)
097: .append(
098: " couldn't be initialized because of circular depency chain:\n");
099: for (int i = pos; i > 0; i--) {
100: msg.append((String) stack.elementAt(stack.size()
101: - i - 1)
102: + "->");
103: }
104: msg.append(className).append('\n');
105:
106: throw new InitializationException(msg.toString());
107: }
108: try {
109: stack.push(className);
110: Initable instance = getInitableInstance(className);
111: if (!instance.getInit()) {
112: // this call might result in an indirect recursion
113: instance.init(data);
114: }
115: } finally {
116: // Succeeded or not, make sure the name gets off the stack.
117: stack.pop();
118: }
119: }
120: }
121:
122: /**
123: * Shuts down an <code>Initable</code>.
124: *
125: * This method is used to release resources allocated by an
126: * <code>Initable</code>, and return it to its initial (uninitailized)
127: * state.
128: *
129: * @param className The name of the class to be uninitialized.
130: */
131: public void shutdownClass(String className) {
132: try {
133: Initable initable = getInitableInstance(className);
134: if (initable.getInit()) {
135: initable.shutdown();
136: ((BaseInitable) initable).setInit(false);
137: }
138: } catch (InstantiationException e) {
139: // Shutdown of a nonexistent class was requested.
140: // This does not hurt anything, so we log the error and continue.
141: log.error("Shutdown of a nonexistent class " + className
142: + " was requested", e);
143: }
144: }
145:
146: /**
147: * Provides an instance of Initable class ready to work.
148: *
149: * If the requested class couldn't be instatiated or initialized,
150: * an InstantiationException will be thrown. You needn't handle
151: * this exception in your code, since it indicates fatal
152: * misconfigurtion of the system.
153: *
154: * @param className The name of the Initable requested.
155: * @return An instance of the requested Initable.
156: * @exception InstantiationException if there was a problem
157: * during instantiation or initialization of the Initable.
158: */
159: public Initable getInitable(String className)
160: throws InstantiationException {
161: Initable initable;
162: try {
163: initable = getInitableInstance(className);
164: if (!initable.getInit()) {
165: synchronized (initable.getClass()) {
166: if (!initable.getInit()) {
167: initable.init();
168: }
169: if (!initable.getInit()) {
170: // this exception will be caught & rethrown by this
171: // very method. getInit() returning false indicates
172: // some initialization issue, which in turn prevents
173: // the InitableBroker from passing a working
174: // instance of the initable to the client.
175: throw new InitializationException(
176: "init() failed to initialize class "
177: + className);
178: }
179: }
180: }
181: return initable;
182: } catch (InitializationException e) {
183: throw new InstantiationException("Class " + className
184: + " failed to initialize", e);
185: }
186: }
187:
188: /**
189: * Retrieves an instance of an Initable from the repository.
190: *
191: * If the requested class is not present in the repository, it is
192: * instantiated and passed a reference to the broker, saved and
193: * then returned.
194: *
195: * @param className The name of the class to be instantiated.
196: * @exception InstantiationException if the requested class can't
197: * be instantiated.
198: */
199: protected Initable getInitableInstance(String className)
200: throws InstantiationException {
201: Initable initable = (Initable) initables.get(className);
202:
203: if (initable == null) {
204: try {
205: initable = (Initable) Class.forName(className)
206: .newInstance();
207: }
208:
209: // those two errors must be passed to the VM
210: catch (ThreadDeath t) {
211: throw t;
212: } catch (OutOfMemoryError t) {
213: throw t;
214: }
215:
216: catch (Throwable t) {
217: // Used to indicate error condition.
218: String msg = null;
219:
220: if (t instanceof NoClassDefFoundError) {
221: msg = "A class referenced by "
222: + className
223: + " is unavailable. Check your jars and classes.";
224: } else if (t instanceof ClassNotFoundException) {
225: msg = "Class "
226: + className
227: + " is unavailable. Check your jars and classes.";
228: } else if (t instanceof ClassCastException) {
229: msg = "Class " + className
230: + " doesn't implement Initable.";
231: } else {
232: msg = "Failed to instantiate " + className;
233: }
234:
235: throw new InstantiationException(msg, t);
236: }
237:
238: initable.setInitableBroker(this);
239: initables.put(className, initable);
240: }
241:
242: return initable;
243: }
244:
245: }
|