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 }