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