View Javadoc

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