View Javadoc

    1   package org.apache.turbine.services.assemblerbroker.util.python;
    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.io.File;
    25
    26  import org.apache.commons.configuration.Configuration;
    27  import org.apache.commons.lang.StringUtils;
    28  import org.apache.commons.logging.Log;
    29  import org.apache.commons.logging.LogFactory;
    30  import org.apache.turbine.modules.Assembler;
    31  import org.apache.turbine.modules.Loader;
    32  import org.apache.turbine.services.assemblerbroker.TurbineAssemblerBroker;
    33  import org.apache.turbine.services.assemblerbroker.util.AssemblerFactory;
    34  import org.python.core.Py;
    35  import org.python.util.PythonInterpreter;
    36
    37  /**
    38   * A factory that attempts to load a python class in the
    39   * JPython interpreter and execute it as a Turbine screen.
    40   * The JPython script should inherit from Turbine Screen or one
    41   * of its subclasses.
    42   *
    43   * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
    44   * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
    45   * @param <T> the specialized assembler type
    46   */
    47  public abstract class PythonBaseFactory<T extends Assembler>
    48          implements AssemblerFactory<T>
    49  {
    50      /** Key for the python path */
    51      public static final String PYTHON_PATH = "python.path";
    52
    53      /** Global config file. This is executed before every screen */
    54      public static final String PYTHON_CONFIG_FILE = "conf.py";
    55
    56      /** Logging */
    57      private static Log log = LogFactory.getLog(PythonBaseFactory.class);
    58
    59      /** Our configuration */
    60      private final Configuration conf =
    61          TurbineAssemblerBroker.getService().getConfiguration();
    62
    63      /**
    64       * Get an Assembler.
    65       *
    66       * @param subDirectory subdirectory within python.path
    67       * @param name name of the requested Assembler
    68       * @return an Assembler
    69       * @throws Exception generic exception
    70       */
    71      public T getAssembler(String subDirectory, String name)
    72              throws Exception
    73      {
    74          String path = conf.getString(PYTHON_PATH);
    75
    76          if (StringUtils.isEmpty(path))
    77          {
    78              throw new Exception(
    79                  "Python path not found - check your Properties");
    80          }
    81
    82          log.debug("Screen name for JPython: " + name);
    83
    84          T assembler = null;
    85
    86          String confName = path + "/" + PYTHON_CONFIG_FILE;
    87
    88          // The filename of the Python script
    89          StringBuilder fName = new StringBuilder();
    90
    91          fName.append(path);
    92          fName.append("/");
    93          fName.append(subDirectory);
    94          fName.append("/");
    95          fName.append(name.toLowerCase());
    96          fName.append(".py");
    97
    98          File f = new File(fName.toString());
    99
    100         if (f.exists())
    101         {
    102             PythonInterpreter interp = null;
    103
    104             try
    105             {
    106                 // We try to open the Py Interpreter
    107                 interp = new PythonInterpreter();
    108
    109                 // Make sure the Py Interpreter use the right classloader
    110                 // This is necessary for servlet engines generally has
    111                 // their own classloader implementations and servlets aren't
    112                 // loaded in the system classloader.  The python script will
    113                 // load java package
    114                 // org.apache.turbine.services.assemblerbroker.util.python;
    115                 // the new classes to it as well.
    116                 Py.getSystemState().setClassLoader(this.getClass().getClassLoader());
    117
    118                 // We import the Python SYS module. Now we don't need to do this
    119                 // explicitly in the script.  We always use the sys module to
    120                 // do stuff like loading java package
    121                 // org.apache.turbine.services.assemblerbroker.util.python;
    122                 interp.exec("import sys");
    123
    124                 // Now we try to load the script file
    125                 interp.execfile(confName);
    126                 interp.execfile(fName.toString());
    127
    128                 try
    129                 {
    130                     // We create an instance of the screen class from the
    131                     // python script
    132                     interp.exec("scr = " + name + "()");
    133                 }
    134                 catch (Throwable e)
    135                 {
    136                     throw new Exception(
    137                         "\nCannot create an instance of the python class.\n"
    138                         + "You probably gave your class the wrong name.\n"
    139                         + "Your class should have the same name as your "
    140                         + "filename.\nFilenames should be all lowercase and "
    141                         + "classnames should start with a capital.\n"
    142                         + "Expected class name: " + name + "\n");
    143                 }
    144
    145                 // Here we convert the python screen instance to a java instance.
    146                 @SuppressWarnings("unchecked") // Cast from Object necessary
    147 				T t = (T) interp.get("scr", Assembler.class);
    148 				assembler = t;
    149             }
    150             catch (Exception e)
    151             {
    152                 // We log the error here because this code is not widely tested
    153                 // yet. After we tested the code on a range of platforms this
    154                 // won't be useful anymore.
    155                 log.error("PYTHON SCRIPT SCREEN LOADER ERROR:", e);
    156                 throw e;
    157             }
    158             finally
    159             {
    160                 if (interp != null)
    161                 {
    162                     interp.close();
    163                 }
    164             }
    165         }
    166         return assembler;
    167     }
    168
    169     /**
    170      * Get the loader for this type of assembler
    171      *
    172      * @return a Loader
    173      */
    174     @Override
    175     public abstract Loader<T> getLoader();
    176
    177     /**
    178      * Get the size of a possibly configured cache
    179      *
    180      * @return the size of the cache in bytes
    181      */
    182     @Override
    183     public int getCacheSize()
    184
    185     {
    186         return getLoader().getCacheSize();
    187     }
    188 }