View Javadoc

1   package org.apache.turbine.services;
2   
3   
4   /*
5    * Copyright 2001-2004 The Apache Software Foundation.
6    *
7    * Licensed under the Apache License, Version 2.0 (the "License")
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
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          // make sure that only one thread calls this method recursively
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                     // this call might result in an indirect recursion
117                     instance.init(data);
118                 }
119             }
120             finally
121             {
122                 // Succeeded or not, make sure the name gets off the stack.
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             // Shutdown of a nonexistent class was requested.
151             // This does not hurt anything, so we log the error and continue.
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                         // this exception will be caught & rethrown by this
188                         // very method. getInit() returning false indicates
189                         // some initialization issue, which in turn prevents
190                         // the InitableBroker from passing a working
191                         // instance of the initable to the client.
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                     // those two errors must be passed to the VM
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                 // Used to indicate error condition.
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 }