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 }