/* * 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; import org.apache.avalon.framework.context.Context; import org.apache.avalon.framework.context.ContextException; import org.apache.avalon.framework.context.Contextualizable; import org.apache.avalon.framework.parameters.Parameters; import org.apache.avalon.framework.parameters.ParameterException; import org.apache.cocoon.Constants; import org.apache.cocoon.components.language.LanguageException; import org.apache.cocoon.components.language.programming.java.JavaProgram; import org.apache.cocoon.util.ClassUtils; import org.apache.cocoon.util.IOUtils; import java.io.File; /** * A compiled programming language. This class extends <code>AbstractProgrammingLanguage</code> adding support for compilation * and object program files * @author <a href="mailto:ricardo@apache.org">Ricardo Rocha</a> * @version CVS $Id$ */ public abstract class CompiledProgrammingLanguage extends AbstractProgrammingLanguage implements Contextualizable { /** The compiler */ protected Class compilerClass; /** The local classpath */ protected String classpath; /** The source deletion option */ protected boolean deleteSources = false; /** * Set the configuration parameters. This method instantiates the sitemap-specified language compiler * @param params The configuration parameters * @exception ParameterException If the language compiler cannot be loaded */ public void parameterize(Parameters params) throws ParameterException { super.parameterize(params); String compilerClass = params.getParameter("compiler"); try { this.compilerClass = ClassUtils.loadClass(compilerClass); } catch (ClassNotFoundException e) { throw new ParameterException("Unable to load compiler: " + compilerClass, e); } this.deleteSources = params.getParameterAsBoolean("delete-sources", false); } /** * Set the context * @param context The context */ public void contextualize(Context context) throws ContextException { this.classpath = (String) context.get(Constants.CONTEXT_CLASSPATH); } /** * Return the language's canonical object file extension. * @return The object file extension */ public abstract String getObjectExtension(); /** * Unload a previously loaded program * @param program A previously loaded object program * @exception LanguageException If an error occurs during unloading */ public abstract void doUnload(Object program) throws LanguageException; /** * Unload a previously loaded program given its original filesystem location * @param program The previously loaded object program * @param filename The base filename of the object program * @param baseDirectory The directory contaning the object program file * @exception LanguageException If an error occurs */ protected final void doUnload(Object program, String filename, File baseDirectory) throws LanguageException { int index = filename.lastIndexOf(File.separator); String dir = filename.substring(0, index); String file = filename.substring(index + 1); File baseDir = new File(baseDirectory, dir); File[] files = baseDir.listFiles(); for (int i = 0;(files != null) && (i < files.length); i++) { if (files[i].getName().startsWith(file)) { files[i].delete(); } } this.doUnload(program); } /** * Actually load an object program from a file. * @param filename 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 abstract Class loadProgram(String filename, File baseDirectory) throws LanguageException; /** * Compile a source file yielding a loadable object file. * @param filename 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 abstract void compile(String filename, File baseDirectory, String encoding) throws LanguageException; /** * Preload an object program from a file. * This method does not compiles the corresponding source file. * * @param filename 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 * @return The loaded object program * @exception LanguageException If an error occurs during compilation */ public Program preload(String filename, File baseDirectory, String encoding) throws LanguageException { // Don't need to test for existence of the object code as it might be bundled into the WAR. try { Class program = this.loadProgram(filename, baseDirectory); // Create and discard test instance. program.newInstance(); return new JavaProgram(program); } catch (Throwable t) { throw new LanguageException("Unable to preload program " + filename, t); } } /** * Load an object program from a file. * This method compiles the corresponding source file if necessary. * * @param filename 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 * @return The loaded object program * @exception LanguageException If an error occurs during compilation */ public Program load(String filename, File baseDirectory, String encoding) throws LanguageException { // Does source file exist? File sourceFile = new File(baseDirectory, filename + "." + this.getSourceExtension()); if (!sourceFile.exists()) { throw new LanguageException("Can't load program - File doesn't exist: " + IOUtils.getFullFilename(sourceFile)); } if (!sourceFile.isFile()) { throw new LanguageException("Can't load program - File is not a normal file: " + IOUtils.getFullFilename(sourceFile)); } if (!sourceFile.canRead()) { throw new LanguageException("Can't load program - File cannot be read: " + IOUtils.getFullFilename(sourceFile)); } this.compile(filename, baseDirectory, encoding); if (this.deleteSources) { sourceFile.delete(); } Class program = this.loadProgram(filename, baseDirectory); // Try to instantiate once to ensure there are no exceptions thrown in the constructor try { // Create and discard test instance program.newInstance(); } catch(IllegalAccessException iae) { getLogger().debug("No public constructor for class " + program.getName()); } catch(Exception e) { // Unload class and delete the object file, or it won't be recompiled // (leave the source file to allow examination). this.doUnload(program); new File(baseDirectory, filename + "." + this.getObjectExtension()).delete(); String message = "Error while instantiating " + filename; getLogger().debug(message, e); throw new LanguageException(message, e); } if (program == null) { throw new LanguageException("Can't load program : " + baseDirectory.toString() + File.separator + filename); } return new JavaProgram(program); } }