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    }