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