View Javadoc

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