/* * 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; } } }