1 package org.apache.turbine.services;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 import java.util.Hashtable;
22 import java.util.Stack;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26
27 /***
28 * A generic implementation of <code>InitableBroker</code>.
29 * Functionality provided by the broker includes:
30 *
31 * <ul>
32 *
33 * <li>Maintaining single instance of each <code>Initable</code> in
34 * the system.</li>
35 *
36 * <li>Early initialization of <code>Initables</code> during system
37 * startup.</li>
38 *
39 * <li>Late initialization of <code>Initables</code> before they are
40 * used.</li>
41 *
42 * <li>Providing instances of <code>Initables</code> to requesting
43 * parties.</li>
44 *
45 * <li>Maintaining dependencies between <code>Initables</code> during
46 * early initalization phases, including circular dependencies
47 * detection.</li>
48 *
49 * </ul>
50 *
51 * @author <a href="mailto:burton@apache.org">Kevin Burton</a>
52 * @author <a href="mailto:krzewski@e-point.pl">Rafal Krzewski</a>
53 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
54 * @version $Id: BaseInitableBroker.java 222043 2004-12-06 17:47:33Z painter $
55 */
56 public abstract class BaseInitableBroker
57 implements InitableBroker
58 {
59 /*** A repository of Initable instances. */
60 protected Hashtable initables = new Hashtable();
61
62 /***
63 * Names of classes being early-initialized are pushed onto this
64 * stack. A name appearing twice indicates a circular dependency
65 * chain.
66 */
67 protected Stack stack = new Stack();
68
69 /*** Logging */
70 private Log log = LogFactory.getLog(this.getClass());
71
72 /***
73 * Default constructor of InitableBroker.
74 *
75 * This constructor does nothing. Your brokers should be
76 * singletons, therefore their constructors should be
77 * private. They should also have public YourBroker getInstance()
78 * methods.
79 */
80 protected BaseInitableBroker()
81 {
82 }
83
84 /***
85 * Performs early initialization of an Initable class.
86 *
87 * @param className The name of the class to be initialized.
88 * @param data An Object to be used for initialization activities.
89 * @exception InitializationException Initialization was not successful.
90 */
91 public void initClass(String className, Object data)
92 throws InitializationException
93 {
94
95 synchronized (stack)
96 {
97 int pos = stack.search(className);
98 if (pos != -1)
99 {
100 StringBuffer msg = new StringBuffer().append(className)
101 .append(" couldn't be initialized because of circular depency chain:\n");
102 for (int i = pos; i > 0; i--)
103 {
104 msg.append((String) stack.elementAt(stack.size() - i - 1) + "->");
105 }
106 msg.append(className).append('\n');
107
108 throw new InitializationException(msg.toString());
109 }
110 try
111 {
112 stack.push(className);
113 Initable instance = getInitableInstance(className);
114 if (!instance.getInit())
115 {
116
117 instance.init(data);
118 }
119 }
120 finally
121 {
122
123 stack.pop();
124 }
125 }
126 }
127
128 /***
129 * Shuts down an <code>Initable</code>.
130 *
131 * This method is used to release resources allocated by an
132 * <code>Initable</code>, and return it to its initial (uninitailized)
133 * state.
134 *
135 * @param className The name of the class to be uninitialized.
136 */
137 public void shutdownClass(String className)
138 {
139 try
140 {
141 Initable initable = getInitableInstance(className);
142 if (initable.getInit())
143 {
144 initable.shutdown();
145 ((BaseInitable) initable).setInit(false);
146 }
147 }
148 catch (InstantiationException e)
149 {
150
151
152 log.error("Shutdown of a nonexistent class " +
153 className + " was requested", e);
154 }
155 }
156
157 /***
158 * Provides an instance of Initable class ready to work.
159 *
160 * If the requested class couldn't be instatiated or initialized,
161 * an InstantiationException will be thrown. You needn't handle
162 * this exception in your code, since it indicates fatal
163 * misconfigurtion of the system.
164 *
165 * @param className The name of the Initable requested.
166 * @return An instance of the requested Initable.
167 * @exception InstantiationException if there was a problem
168 * during instantiation or initialization of the Initable.
169 */
170 public Initable getInitable(String className)
171 throws InstantiationException
172 {
173 Initable initable;
174 try
175 {
176 initable = getInitableInstance(className);
177 if (!initable.getInit())
178 {
179 synchronized (initable.getClass())
180 {
181 if (!initable.getInit())
182 {
183 initable.init();
184 }
185 if (!initable.getInit())
186 {
187
188
189
190
191
192 throw new InitializationException(
193 "init() failed to initialize class "
194 + className);
195 }
196 }
197 }
198 return initable;
199 }
200 catch (InitializationException e)
201 {
202 throw new InstantiationException("Class " + className +
203 " failed to initialize", e);
204 }
205 }
206
207 /***
208 * Retrieves an instance of an Initable from the repository.
209 *
210 * If the requested class is not present in the repository, it is
211 * instantiated and passed a reference to the broker, saved and
212 * then returned.
213 *
214 * @param className The name of the class to be instantiated.
215 * @exception InstantiationException if the requested class can't
216 * be instantiated.
217 */
218 protected Initable getInitableInstance(String className)
219 throws InstantiationException
220 {
221 Initable initable = (Initable) initables.get(className);
222
223 if (initable == null)
224 {
225 try
226 {
227 initable = (Initable) Class.forName(className).newInstance();
228 }
229
230
231 catch (ThreadDeath t)
232 {
233 throw t;
234 }
235 catch (OutOfMemoryError t)
236 {
237 throw t;
238 }
239
240 catch (Throwable t)
241 {
242
243 String msg = null;
244
245 if (t instanceof NoClassDefFoundError)
246 {
247 msg = "A class referenced by " + className +
248 " is unavailable. Check your jars and classes.";
249 }
250 else if (t instanceof ClassNotFoundException)
251 {
252 msg = "Class " + className +
253 " is unavailable. Check your jars and classes.";
254 }
255 else if (t instanceof ClassCastException)
256 {
257 msg = "Class " + className +
258 " doesn't implement Initable.";
259 }
260 else
261 {
262 msg = "Failed to instantiate " + className;
263 }
264
265 throw new InstantiationException(msg, t);
266 }
267
268 initable.setInitableBroker(this);
269 initables.put(className, initable);
270 }
271
272 return initable;
273 }
274
275 }