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.TurbineServices;
33 import org.apache.turbine.services.assemblerbroker.AssemblerBrokerService;
34 import org.apache.turbine.services.assemblerbroker.util.AssemblerFactory;
35 import org.python.core.Py;
36 import org.python.util.PythonInterpreter;
37
38 /**
39 * A factory that attempts to load a python class in the
40 * JPython interpreter and execute it as a Turbine screen.
41 * The JPython script should inherit from Turbine Screen or one
42 * of its subclasses.
43 *
44 * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
45 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
46 * @param <T> the specialized assembler type
47 */
48 public abstract class PythonBaseFactory<T extends Assembler>
49 implements AssemblerFactory<T>
50 {
51 /** Key for the python path */
52 public static final String PYTHON_PATH = "python.path";
53
54 /** Global config file. This is executed before every screen */
55 public static final String PYTHON_CONFIG_FILE = "conf.py";
56
57 /** Logging */
58 private static Log log = LogFactory.getLog(PythonBaseFactory.class);
59
60 /** Our configuration */
61 private final Configuration conf = TurbineServices.getInstance().getConfiguration(AssemblerBrokerService.SERVICE_NAME);
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 }