    001    package org.apache.turbine.services.assemblerbroker.util.python;
    002
    003
    004    /*
    005     * Licensed to the Apache Software Foundation (ASF) under one
    006     * or more contributor license agreements.  See the NOTICE file
    007     * distributed with this work for additional information
    008     * regarding copyright ownership.  The ASF licenses this file
    009     * to you under the Apache License, Version 2.0 (the
    010     * "License"); you may not use this file except in compliance
    011     * with the License.  You may obtain a copy of the License at
    012     *
    013     *   http://www.apache.org/licenses/LICENSE-2.0
    014     *
    015     * Unless required by applicable law or agreed to in writing,
    016     * software distributed under the License is distributed on an
    017     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    018     * KIND, either express or implied.  See the License for the
    019     * specific language governing permissions and limitations
    020     * under the License.
    021     */
    022
    023
    024    import java.io.File;
    025
    026    import org.apache.commons.configuration.Configuration;
    027    import org.apache.commons.lang.StringUtils;
    028    import org.apache.commons.logging.Log;
    029    import org.apache.commons.logging.LogFactory;
    030    import org.apache.turbine.modules.Assembler;
    031    import org.apache.turbine.modules.Loader;
    032    import org.apache.turbine.services.assemblerbroker.TurbineAssemblerBroker;
    033    import org.apache.turbine.services.assemblerbroker.util.AssemblerFactory;
    034    import org.python.core.Py;
    035    import org.python.util.PythonInterpreter;
    036
    037    /**
    038     * A factory that attempts to load a python class in the
    039     * JPython interpreter and execute it as a Turbine screen.
    040     * The JPython script should inherit from Turbine Screen or one
    041     * of its subclasses.
    042     *
    043     * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
    044     * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
    045     * @version $Id: PythonBaseFactory.java 1078552 2011-03-06 19:58:46Z tv $
    046     */
    047    public abstract class PythonBaseFactory<T extends Assembler>
    048            implements AssemblerFactory<T>
    049    {
    050        /** Key for the python path */
    051        public static final String PYTHON_PATH = "python.path";
    052
    053        /** Global config file. This is executed before every screen */
    054        public static final String PYTHON_CONFIG_FILE = "conf.py";
    055
    056        /** Logging */
    057        private static Log log = LogFactory.getLog(PythonBaseFactory.class);
    058
    059        /** Our configuration */
    060        private final Configuration conf =
    061            TurbineAssemblerBroker.getService().getConfiguration();
    062
    063        /**
    064         * Get an Assembler.
    065         *
    066         * @param subDirectory subdirectory within python.path
    067         * @param name name of the requested Assembler
    068         * @return an Assembler
    069         * @throws Exception generic exception
    070         */
    071        public T getAssembler(String subDirectory, String name)
    072                throws Exception
    073        {
    074            String path = conf.getString(PYTHON_PATH);
    075
    076            if (StringUtils.isEmpty(path))
    077            {
    078                throw new Exception(
    079                    "Python path not found - check your Properties");
    080            }
    081
    082            log.debug("Screen name for JPython: " + name);
    083
    084            T assembler = null;
    085
    086            String confName = path + "/" + PYTHON_CONFIG_FILE;
    087
    088            // The filename of the Python script
    089            StringBuffer fName = new StringBuffer();
    090
    091            fName.append(path);
    092            fName.append("/");
    093            fName.append(subDirectory);
    094            fName.append("/");
    095            fName.append(name.toLowerCase());
    096            fName.append(".py");
    097
    098            File f = new File(fName.toString());
    099
    100            if (f.exists())
    101            {
    102                try
    103                {
    104                    // We try to open the Py Interpreter
    105                    PythonInterpreter interp = new PythonInterpreter();
    106
    107                    // Make sure the Py Interpreter use the right classloader
    108                    // This is necessary for servlet engines generally has
    109                    // their own classloader implementations and servlets aren't
    110                    // loaded in the system classloader.  The python script will
    111                    // load java package
    112                    // org.apache.turbine.services.assemblerbroker.util.python;
    113                    // the new classes to it as well.
    114                    Py.getSystemState().setClassLoader(
    115                            this.getClass().getClassLoader());
    116
    117                    // We import the Python SYS module. Now we don't need to do this
    118                    // explicitely in the script.  We always use the sys module to
    119                    // do stuff like loading java package
    120                    // org.apache.turbine.services.assemblerbroker.util.python;
    121                    interp.exec("import sys");
    122
    123                    // Now we try to load the script file
    124                    interp.execfile(confName);
    125                    interp.execfile(fName.toString());
    126
    127                    try
    128                    {
    129                        // We create an instance of the screen class from the
    130                        // python script
    131                        interp.exec("scr = " + name + "()");
    132                    }
    133                    catch (Throwable e)
    134                    {
    135                        throw new Exception(
    136                            "\nCannot create an instance of the python class.\n"
    137                            + "You probably gave your class the wrong name.\n"
    138                            + "Your class should have the same name as your "
    139                            + "filename.\nFilenames should be all lowercase and "
    140                            + "classnames should start with a capital.\n"
    141                            + "Expected class name: " + name + "\n");
    142                    }
    143
    144                    // Here we convert the python sceen instance to a java instance.
    145                    assembler = (T) interp.get("scr", Assembler.class);
    146                }
    147                catch (Exception e)
    148                {
    149                    // We log the error here because this code is not widely tested
    150                    // yet. After we tested the code on a range of platforms this
    151                    // won't be useful anymore.
    152                    log.error("PYTHON SCRIPT SCREEN LOADER ERROR:", e);
    153                    throw e;
    154                }
    155            }
    156            return assembler;
    157        }
    158
    159        /**
    160         * Get the loader for this type of assembler
    161         *
    162         * @return a Loader
    163         */
    164        public abstract Loader<T> getLoader();
    165
    166        /**
    167         * Get the size of a possibly configured cache
    168         *
    169         * @return the size of the cache in bytes
    170         */
    171        public int getCacheSize()
    172
    173        {
    174            return getLoader().getCacheSize();
    175        }
    176    }