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 1706239 2015-10-01 13:18:35Z 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      @Override
    96      public void initClass(String className, Object data)
    97              throws InitializationException
    98      {
    99          // make sure that only one thread calls this method recursively
    100         synchronized (stack)
    101         {
    102             int pos = stack.search(className);
    103             if (pos != -1)
    104             {
    105                 StringBuilder msg = new StringBuilder().append(className)
    106                         .append(" couldn't be initialized because of circular dependency chain:\n");
    107                 for (int i = pos; i > 0; i--)
    108                 {
    109                     msg.append(stack.elementAt(stack.size() - i - 1) + "->");
    110                 }
    111                 msg.append(className).append('\n');
    112
    113                 throw new InitializationException(msg.toString());
    114             }
    115             try
    116             {
    117                 stack.push(className);
    118                 Initable instance = getInitableInstance(className);
    119                 if (!instance.getInit())
    120                 {
    121                     // this call might result in an indirect recursion
    122                     instance.init(data);
    123                 }
    124             }
    125             finally
    126             {
    127                 // Succeeded or not, make sure the name gets off the stack.
    128                 stack.pop();
    129             }
    130         }
    131     }
    132
    133     /**
    134      * Shuts down an <code>Initable</code>.
    135      *
    136      * This method is used to release resources allocated by an
    137      * <code>Initable</code>, and return it to its initial (uninitialized)
    138      * state.
    139      *
    140      * @param className The name of the class to be uninitialized.
    141      */
    142     @Override
    143     public void shutdownClass(String className)
    144     {
    145         try
    146         {
    147             Initable initable = getInitableInstance(className);
    148             if (initable.getInit())
    149             {
    150                 initable.shutdown();
    151                 ((BaseInitable) initable).setInit(false);
    152             }
    153         }
    154         catch (InstantiationException e)
    155         {
    156             // Shutdown of a nonexistent class was requested.
    157             // This does not hurt anything, so we log the error and continue.
    158             log.error("Shutdown of a nonexistent class " +
    159                     className + " was requested", e);
    160         }
    161     }
    162
    163     /**
    164      * Provides an instance of Initable class ready to work.
    165      *
    166      * If the requested class couldn't be instantiated or initialized,
    167      * an InstantiationException will be thrown. You needn't handle
    168      * this exception in your code, since it indicates fatal
    169      * misconfiguration of the system.
    170      *
    171      * @param className The name of the Initable requested.
    172      * @return An instance of the requested Initable.
    173      * @exception InstantiationException if there was a problem
    174      * during instantiation or initialization of the Initable.
    175      */
    176     @Override
    177     public Initable getInitable(String className)
    178             throws InstantiationException
    179     {
    180         Initable initable;
    181         try
    182         {
    183             initable = getInitableInstance(className);
    184             if (!initable.getInit())
    185             {
    186                 synchronized (initable.getClass())
    187                 {
    188                     if (!initable.getInit())
    189                     {
    190                         initable.init();
    191                     }
    192                     if (!initable.getInit())
    193                     {
    194                         // this exception will be caught & rethrown by this
    195                         // very method. getInit() returning false indicates
    196                         // some initialization issue, which in turn prevents
    197                         // the InitableBroker from passing a working
    198                         // instance of the initable to the client.
    199                         throw new InitializationException(
    200                                 "init() failed to initialize class "
    201                                 + className);
    202                     }
    203                 }
    204             }
    205             return initable;
    206         }
    207         catch (InitializationException e)
    208         {
    209             throw new InstantiationException("Class " + className +
    210                     " failed to initialize", e);
    211         }
    212     }
    213
    214     /**
    215      * Retrieves an instance of an Initable from the repository.
    216      *
    217      * If the requested class is not present in the repository, it is
    218      * instantiated and passed a reference to the broker, saved and
    219      * then returned.
    220      *
    221      * @param className The name of the class to be instantiated.
    222      * @exception InstantiationException if the requested class can't
    223      * be instantiated.
    224      */
    225     protected Initable getInitableInstance(String className)
    226             throws InstantiationException
    227     {
    228         Initable initable = initables.get(className);
    229
    230         if (initable == null)
    231         {
    232             try
    233             {
    234                 initable = (Initable) Class.forName(className).newInstance();
    235             }
    236
    237                     // those two errors must be passed to the VM
    238             catch (ThreadDeath t)
    239             {
    240                 throw t;
    241             }
    242             catch (OutOfMemoryError t)
    243             {
    244                 throw t;
    245             }
    246
    247             catch (Throwable t)
    248             {
    249                 // Used to indicate error condition.
    250                 String msg = null;
    251
    252                 if (t instanceof NoClassDefFoundError)
    253                 {
    254                     msg = "A class referenced by " + className +
    255                             " is unavailable. Check your jars and classes.";
    256                 }
    257                 else if (t instanceof ClassNotFoundException)
    258                 {
    259                     msg = "Class " + className +
    260                             " is unavailable. Check your jars and classes.";
    261                 }
    262                 else if (t instanceof ClassCastException)
    263                 {
    264                     msg = "Class " + className +
    265                             " doesn't implement Initable.";
    266                 }
    267                 else
    268                 {
    269                     msg = "Failed to instantiate " + className;
    270                 }
    271
    272                 throw new InstantiationException(msg, t);
    273             }
    274
    275             initable.setInitableBroker(this);
    276             initables.put(className, initable);
    277         }
    278
    279         return initable;
    280     }
    281
    282 }