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.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 }