View Javadoc

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