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 }