| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.cocoon.components.language.programming.java; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.List; |
| import java.util.StringTokenizer; |
| |
| import org.apache.avalon.framework.activity.Disposable; |
| import org.apache.avalon.framework.activity.Initializable; |
| import org.apache.avalon.framework.logger.LogEnabled; |
| import org.apache.avalon.framework.parameters.ParameterException; |
| import org.apache.avalon.framework.parameters.Parameters; |
| import org.apache.avalon.framework.service.ServiceException; |
| import org.apache.avalon.framework.service.ServiceManager; |
| import org.apache.avalon.framework.service.Serviceable; |
| import org.apache.avalon.framework.thread.ThreadSafe; |
| |
| import org.apache.cocoon.components.classloader.ClassLoaderManager; |
| import org.apache.cocoon.components.language.LanguageException; |
| import org.apache.cocoon.components.language.markup.xsp.XSLTExtension; |
| import org.apache.cocoon.components.language.programming.CompiledProgrammingLanguage; |
| import org.apache.cocoon.components.language.programming.CompilerError; |
| import org.apache.cocoon.components.language.programming.LanguageCompiler; |
| import org.apache.cocoon.util.ClassUtils; |
| import org.apache.cocoon.util.JavaArchiveFilter; |
| import org.apache.commons.lang.SystemUtils; |
| |
| /** |
| * The Java programming language processor |
| * |
| * @author <a href="mailto:ricardo@apache.org">Ricardo Rocha</a> |
| * @version CVS $Id$ |
| */ |
| public class JavaLanguage extends CompiledProgrammingLanguage |
| implements Initializable, ThreadSafe, Serviceable, Disposable { |
| |
| /** The class loader */ |
| private ClassLoaderManager classLoaderManager; |
| |
| /** The service manager */ |
| protected ServiceManager manager = null; |
| |
| /** Classpath */ |
| private String classpath; |
| |
| /** The Class Loader Class Name */ |
| private String classLoaderClass; |
| |
| /** Source code version */ |
| private int compilerComplianceLevel; |
| |
| /** |
| * Return the language's canonical source file extension. |
| * |
| * @return The source file extension |
| */ |
| public String getSourceExtension() { |
| return "java"; |
| } |
| |
| /** |
| * Return the language's canonical object file extension. |
| * |
| * @return The object file extension |
| */ |
| public String getObjectExtension() { |
| return "class"; |
| } |
| |
| /** |
| * Set the configuration parameters. This method instantiates the |
| * sitemap-specified <code>ClassLoaderManager</code> |
| * |
| * @param params The configuration parameters |
| * @throws ParameterException If the class loader manager cannot be |
| * instantiated or looked up. |
| */ |
| public void parameterize(Parameters params) throws ParameterException { |
| super.parameterize(params); |
| |
| |
| this.classLoaderClass = params.getParameter("class-loader", null); |
| if (this.classLoaderClass != null) { |
| try { |
| this.classLoaderManager = (ClassLoaderManager) |
| ClassUtils.newInstance(this.classLoaderClass); |
| } catch (Exception e) { |
| throw new ParameterException("Unable to load class loader: " |
| + this.classLoaderClass, e); |
| } |
| } else { |
| try { |
| getLogger().debug("Looking up " + ClassLoaderManager.ROLE); |
| this.classLoaderManager = (ClassLoaderManager) |
| manager.lookup(ClassLoaderManager.ROLE); |
| } catch (ServiceException e) { |
| throw new ParameterException("Lookup of ClassLoaderManager failed", e); |
| } |
| } |
| // Get the compiler compliance level (source Code version) |
| String sourceVer = params.getParameter("compiler-compliance-level", "auto"); |
| if (sourceVer.equalsIgnoreCase("auto")) { |
| this.compilerComplianceLevel = SystemUtils.JAVA_VERSION_INT; |
| } else { |
| try { |
| compilerComplianceLevel = new Float(Float.parseFloat(sourceVer) * 100).intValue(); |
| } catch (NumberFormatException e) { |
| throw new ParameterException("XSP: compiler-compliance-level parameter value not valid!", e); |
| } |
| } |
| } |
| |
| /** |
| * Set the global service manager. |
| * |
| * @param manager The global service manager |
| */ |
| public void service(ServiceManager manager) throws ServiceException { |
| this.manager = manager; |
| } |
| |
| public void initialize() throws Exception { |
| |
| // Initialize the classpath |
| String systemBootClasspath = System.getProperty("sun.boot.class.path"); |
| String systemClasspath = SystemUtils.JAVA_CLASS_PATH; |
| String systemExtDirs = SystemUtils.JAVA_EXT_DIRS; |
| String systemExtClasspath = null; |
| |
| try { |
| systemExtClasspath = expandDirs(systemExtDirs); |
| } catch (Exception e) { |
| getLogger().warn("Could not expand Directory:" + systemExtDirs, e); |
| } |
| |
| this.classpath = |
| ((super.classpath != null) ? File.pathSeparator + super.classpath : "") + |
| ((systemBootClasspath != null) ? File.pathSeparator + systemBootClasspath : "") + |
| ((systemClasspath != null) ? File.pathSeparator + systemClasspath : "") + |
| ((systemExtClasspath != null) ? File.pathSeparator + systemExtClasspath : ""); |
| } |
| |
| /** |
| * Actually load an object program from a class file. |
| * |
| * @param name The object program base file name |
| * @param baseDirectory The directory containing the object program file |
| * @return The loaded object program |
| * @exception LanguageException If an error occurs during loading |
| */ |
| protected Class loadProgram(String name, File baseDirectory) |
| throws LanguageException { |
| try { |
| this.classLoaderManager.addDirectory(baseDirectory); |
| return this.classLoaderManager.loadClass(name.replace(File.separatorChar, '.')); |
| } catch (Exception e) { |
| throw new LanguageException("Could not load class for program '" + name + "' due to a " + e.getClass().getName() + ": " + e.getMessage()); |
| } |
| } |
| |
| /** |
| * Compile a source file yielding a loadable class file. |
| * |
| * @param name The object program base file name |
| * @param baseDirectory The directory containing the object program file |
| * @param encoding The encoding expected in the source file or |
| * <code>null</code> if it is the platform's default encoding |
| * @exception LanguageException If an error occurs during compilation |
| */ |
| protected void compile(String name, File baseDirectory, String encoding) |
| throws LanguageException { |
| |
| try { |
| LanguageCompiler compiler = (LanguageCompiler)this.compilerClass.newInstance(); |
| // AbstractJavaCompiler is LogEnabled |
| if (compiler instanceof LogEnabled) { |
| ((LogEnabled)compiler).enableLogging(getLogger()); |
| } |
| if (compiler instanceof Serviceable) { |
| ((Serviceable)compiler).service(this.manager); |
| } |
| |
| int pos = name.lastIndexOf(File.separatorChar); |
| String filename = name.substring(pos + 1); |
| |
| final String basePath = baseDirectory.getCanonicalPath(); |
| String filepath = basePath + File.separator + name + "." + getSourceExtension(); |
| |
| compiler.setFile(filepath); |
| compiler.setSource(basePath); |
| compiler.setDestination(basePath); |
| compiler.setClasspath(basePath + this.classpath); |
| compiler.setCompilerComplianceLevel(compilerComplianceLevel); |
| |
| if (encoding != null) { |
| compiler.setEncoding(encoding); |
| } |
| |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("Compiling " + filepath); |
| } |
| if (!compiler.compile()) { |
| StringBuffer message = new StringBuffer("Error compiling "); |
| message.append(filename); |
| message.append(":\n"); |
| |
| List errors = compiler.getErrors(); |
| CompilerError[] compilerErrors = new CompilerError[errors.size()]; |
| errors.toArray(compilerErrors); |
| |
| throw new LanguageException(message.toString(), filepath, compilerErrors); |
| } |
| |
| } catch (InstantiationException e) { |
| getLogger().warn("Could not instantiate the compiler", e); |
| throw new LanguageException("Could not instantiate the compiler: " + e.getMessage()); |
| } catch (IllegalAccessException e) { |
| getLogger().warn("Could not access the compiler class", e); |
| throw new LanguageException("Could not access the compiler class: " + e.getMessage()); |
| } catch (IOException e) { |
| getLogger().warn("Error during compilation", e); |
| throw new LanguageException("Error during compilation: " + e.getMessage()); |
| } catch (ServiceException e) { |
| getLogger().warn("Could not initialize the compiler", e); |
| throw new LanguageException("Could not initialize the compiler: " + e.getMessage()); |
| } |
| } |
| |
| /** |
| * Unload a previously loaded class. This method simply reinstantiates the |
| * class loader to ensure that a new version of the same class will be |
| * correctly loaded in a future loading operation |
| * |
| * @param program A previously loaded class |
| * @exception LanguageException If an error occurs during unloading |
| */ |
| public void doUnload(Object program) throws LanguageException { |
| this.classLoaderManager.reinstantiate(); |
| } |
| |
| /** |
| * Escape a <code>String</code> according to the Java string constant |
| * encoding rules. |
| * |
| * @param constant The string to be escaped |
| * @return The escaped string |
| */ |
| public String quoteString(String constant) { |
| return XSLTExtension.escapeJavaString(constant); |
| } |
| |
| /** |
| * Expand a directory path or list of directory paths (File.pathSeparator |
| * delimited) into a list of file paths of all the jar files in those |
| * directories. |
| * |
| * @param dirPaths The string containing the directory path or list of |
| * directory paths. |
| * @return The file paths of the jar files in the directories. This is an |
| * empty string if no files were found, and is terminated by an |
| * additional pathSeparator in all other cases. |
| */ |
| private String expandDirs(String dirPaths) { |
| StringTokenizer st = new StringTokenizer(dirPaths, File.pathSeparator); |
| StringBuffer buffer = new StringBuffer(); |
| while (st.hasMoreTokens()) { |
| String d = st.nextToken(); |
| File dir = new File(d); |
| if (!dir.isDirectory()) { |
| // The absence of a listed directory may not be an error. |
| if (getLogger().isWarnEnabled()) { |
| getLogger().warn("Attempted to retrieve directory listing of non-directory " + dir.toString()); |
| } |
| } else { |
| File[] files = dir.listFiles(new JavaArchiveFilter()); |
| for (int i = 0; i < files.length; i++) { |
| buffer.append(files[i]).append(File.pathSeparator); |
| } |
| } |
| } |
| return buffer.toString(); |
| } |
| |
| /** |
| * dispose |
| */ |
| public void dispose() { |
| if (this.classLoaderClass == null && this.classLoaderManager != null) { |
| manager.release(this.classLoaderManager); |
| this.classLoaderManager = null; |
| } |
| } |
| } |