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.configuration2.Configuration;
27  import org.apache.commons.lang3.StringUtils;
28  import org.apache.logging.log4j.LogManager;
29  import org.apache.logging.log4j.Logger;
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 Logger log = LogManager.getLogger(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             // We try to open the Py Interpreter
103             try (PythonInterpreter interp = new PythonInterpreter())
104             {
105                 // Make sure the Py Interpreter use the right classloader
106                 // This is necessary for servlet engines generally has
107                 // their own classloader implementations and servlets aren't
108                 // loaded in the system classloader.  The python script will
109                 // load java package
110                 // org.apache.turbine.services.assemblerbroker.util.python;
111                 // the new classes to it as well.
112                 Py.getSystemState().setClassLoader(this.getClass().getClassLoader());
113 
114                 // We import the Python SYS module. Now we don't need to do this
115                 // explicitly in the script.  We always use the sys module to
116                 // do stuff like loading java package
117                 // org.apache.turbine.services.assemblerbroker.util.python;
118                 interp.exec("import sys");
119 
120                 // Now we try to load the script file
121                 interp.execfile(confName);
122                 interp.execfile(fName.toString());
123 
124                 try
125                 {
126                     // We create an instance of the screen class from the
127                     // python script
128                     interp.exec("scr = " + name + "()");
129                 }
130                 catch (Throwable e)
131                 {
132                     throw new Exception(
133                         "\nCannot create an instance of the python class.\n"
134                         + "You probably gave your class the wrong name.\n"
135                         + "Your class should have the same name as your "
136                         + "filename.\nFilenames should be all lowercase and "
137                         + "classnames should start with a capital.\n"
138                         + "Expected class name: " + name + "\n");
139                 }
140 
141                 // Here we convert the python screen instance to a java instance.
142                 @SuppressWarnings("unchecked") // Cast from Object necessary
143 				T t = (T) interp.get("scr", Assembler.class);
144 				assembler = t;
145             }
146             catch (Exception e)
147             {
148                 // We log the error here because this code is not widely tested
149                 // yet. After we tested the code on a range of platforms this
150                 // won't be useful anymore.
151                 log.error("PYTHON SCRIPT SCREEN LOADER ERROR:", e);
152                 throw e;
153             }
154         }
155         return assembler;
156     }
157 
158     /**
159      * Get the loader for this type of assembler
160      *
161      * @return a Loader
162      */
163     @Override
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     @Override
172     public int getCacheSize()
173 
174     {
175         return getLoader().getCacheSize();
176     }
177 }