Package org.apache.cocoon.components.language.generator

Source Code of org.apache.cocoon.components.language.generator.ProgramGeneratorImpl

/*
* Copyright 1999-2004 The Apache Software Foundation.
*
* Licensed 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.generator;

import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.ComponentSelector;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.component.Recomposable;
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.logger.AbstractLogEnabled;
import org.apache.avalon.framework.parameters.ParameterException;
import org.apache.avalon.framework.parameters.Parameterizable;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.thread.ThreadSafe;
import org.apache.cocoon.Constants;
import org.apache.cocoon.ProcessingException;
import org.apache.cocoon.components.classloader.ClassLoaderManager;
import org.apache.cocoon.components.language.LanguageException;
import org.apache.cocoon.components.language.markup.MarkupLanguage;
import org.apache.cocoon.components.language.programming.CodeFormatter;
import org.apache.cocoon.components.language.programming.Program;
import org.apache.cocoon.components.language.programming.ProgrammingLanguage;
import org.apache.cocoon.environment.SourceResolver;
import org.apache.cocoon.util.IOUtils;
import org.apache.excalibur.source.Source;

import java.io.File;
import java.net.MalformedURLException;

/**
* The default implementation of <code>ProgramGenerator</code>
*
* @author <a href="mailto:ricardo@apache.org">Ricardo Rocha</a>
* @author <a href="mailto:vgritsenko@apache.org">Vadim Gritsenko</a>
* @author <a href="mailto:tcurdt@apache.org">Torsten Curdt</a>
* @version CVS $Id: ProgramGeneratorImpl.java,v 1.1 2004/03/10 12:58:04 stephan Exp $
*/
public class ProgramGeneratorImpl extends AbstractLogEnabled
    implements ProgramGenerator, Contextualizable, Composable, Parameterizable,
               Disposable, ThreadSafe {

    /** The auto-reloading option */
    protected boolean autoReload = true;

    /** The pre-loading option */
    protected boolean preload = false;

    /** The check for manual source changes in the repository*/
    protected boolean watchSource = false;

    /**
     * The ComponentSelector for programs. Caches Program by program
     * source file.
     */
    protected GeneratorSelector cache;

    /** The component manager */
    protected ComponentManager manager;

    /** The markup language component selector */
    protected ComponentSelector markupSelector;

    /** The programming language component selector */
    protected ComponentSelector languageSelector;

    /** The working directory */
    protected File workDir;

    /** The ClassLoaderManager */
    protected ClassLoaderManager classManager;

    /** The root package */
    protected String rootPackage;

    /** Servlet Context Directory */
    protected String contextDir;


    /** Contextualize this class */
    public void contextualize(Context context) throws ContextException {
        if (this.workDir == null) {
            this.workDir = (File) context.get(Constants.CONTEXT_WORK_DIR);
        }

        if (this.contextDir == null) {
            org.apache.cocoon.environment.Context ctx =
                (org.apache.cocoon.environment.Context) context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT);

            // Determine the context directory, preferably as a file
            // FIXME (SW) - this is purposely redundant with some code in CocoonServlet
            //              to have the same rootPath. How to avoid this ?
            try {
                String rootPath = ctx.getRealPath("/");
                if (rootPath != null) {
                    this.contextDir = new File(rootPath).toURL().toExternalForm();
                } else {
                    String webInf = ctx.getResource("/WEB-INF").toExternalForm();
                    this.contextDir = webInf.substring(0, webInf.length() - "WEB-INF".length());
                }
                if (getLogger().isDebugEnabled()) {
                    getLogger().debug("Context directory is " + this.contextDir);
                }
            } catch (MalformedURLException e) {
                getLogger().warn("Could not get context directory", e);
                this.contextDir = "";
            }
        }
    }

    /**
     * Set the global component manager. This method also sets the
     * <code>ComponentSelector</code> used as language factory for both markup
     * and programming languages.
     * @param manager The global component manager
     */
    public void compose(ComponentManager manager) throws ComponentException {
        if (this.manager == null && manager != null) {
            this.manager = manager;
            this.cache = (GeneratorSelector) this.manager.lookup(GeneratorSelector.ROLE + "Selector");
            this.markupSelector = (ComponentSelector)this.manager.lookup(MarkupLanguage.ROLE + "Selector");
            this.languageSelector = (ComponentSelector)this.manager.lookup(ProgrammingLanguage.ROLE + "Selector");
            this.classManager = (ClassLoaderManager)this.manager.lookup(ClassLoaderManager.ROLE);
        }
    }

    /**
     * Set the sitemap-provided configuration. This method sets the persistent code repository and the auto-reload option
     * @param params The configuration information
     * @exception ParameterException Not thrown here
     */
    public void parameterize(Parameters params) throws ParameterException {
        this.autoReload = params.getParameterAsBoolean("auto-reload", autoReload);
        this.rootPackage = params.getParameter("root-package", "org.apache.cocoon.www");
        this.preload = params.getParameterAsBoolean("preload", preload);
        this.watchSource = params.getParameterAsBoolean("watch-source", watchSource);
    }

    /**
     * Generates program source file name in the working directory
     * from the source SystemID
     */
    private String getNormalizedName(final String systemId) {
        StringBuffer contextFilename = new StringBuffer(this.rootPackage.replace('.', File.separatorChar));
        contextFilename.append(File.separator);
        if(systemId.startsWith(this.contextDir)) {
            // VG: File is located under contextDir; using relative file name ...
            contextFilename.append(systemId.substring(this.contextDir.length()));
        } else {
            // VG: File is located outside of contextDir; using systemId ...
            contextFilename.append(systemId);
        }
        return IOUtils.normalizedFilename(contextFilename.toString());
    }

    /**
     * Load a program built from an XML document written in a <code>MarkupLanguage</code>
     *
     * @param fileName The input document's <code>File</code>
     * @param markupLanguageName The <code>MarkupLanguage</code> in which the input document is written
     * @param programmingLanguageName The <code>ProgrammingLanguage</code> in which the program must be written
     * @return The loaded program instance
     * @exception Exception If an error occurs during generation or loading
     * @deprecated Pass Source object instead of file name.
     */
    public CompiledComponent load(ComponentManager newManager,
                                  String fileName,
                                  String markupLanguageName,
                                  String programmingLanguageName,
                                  SourceResolver resolver)
    throws Exception {

        final Source source = resolver.resolveURI(fileName);
        try {
            return load(newManager, source, markupLanguageName, programmingLanguageName, resolver);
        } finally {
            resolver.release(source);
        }
    }

    /**
     * Load a program built from an XML document written in a <code>MarkupLanguage</code>.
     *
     * This method does not releases passed source object. Caller of the method must release
     * source when needed.
     *
     * @param source The input document's <code>File</code>
     * @param markupLanguageName The <code>MarkupLanguage</code> in which the input document is written
     * @param programmingLanguageName The <code>ProgrammingLanguage</code> in which the program must be written
     * @return The loaded program instance
     * @exception Exception If an error occurs during generation or loading
     */
    public CompiledComponent load(ComponentManager newManager,
                                  Source source,
                                  String markupLanguageName,
                                  String programmingLanguageName,
                                  SourceResolver resolver)
    throws Exception {

        final String id = source.getURI();

        ProgrammingLanguage programmingLanguage = null;
        MarkupLanguage markupLanguage = null;
        try {
            // Create file name for the program generated from the provided source.
            final String normalizedName = getNormalizedName(id);

            if (getLogger().isDebugEnabled()) {
                getLogger().debug("Loading serverpage systemId=[" + id + "]" +
                    " markupLanguageName=[" + markupLanguageName + "]" +
                    " programmingLanguageName=[" + programmingLanguageName + "]" +
                    " -> normalizedName=[" + normalizedName + "]");
            }

            markupLanguage = (MarkupLanguage) this.markupSelector.select(markupLanguageName);
            programmingLanguage = (ProgrammingLanguage) this.languageSelector.select(programmingLanguageName);
            programmingLanguage.setLanguageName(programmingLanguageName);

            Program program = null;
            CompiledComponent programInstance = null;

            // Attempt to load program object from cache
            try {
                programInstance = (CompiledComponent) this.cache.select(normalizedName);
            } catch (Exception e) {
                if (getLogger().isDebugEnabled()) {
                    getLogger().debug("The serverpage [" + id + "] is not in the cache yet");
                }
            }

            if (programInstance == null && this.preload) {
                // Preloading: Load program if its source/[object file] is available
                try {
                    program = programmingLanguage.preload(normalizedName,
                                                          this.workDir,
                                                          markupLanguage.getEncoding());

                    this.cache.addGenerator(newManager, normalizedName, program);
                    programInstance = (CompiledComponent) this.cache.select(normalizedName);

                    if (getLogger().isDebugEnabled()) {
                        getLogger().debug("Successfully preloaded serverpage [" + id + "]");
                    }
                } catch (Exception e) {
                    if (getLogger().isInfoEnabled()) {
                        getLogger().info("The serverpage [" + id
                                         + "] could not be preloaded, will be re-created ("
                                         + e + ")");
                    }
                }
            }

            if (programInstance == null) {
                // no instance found
                if (getLogger().isDebugEnabled()) {
                    getLogger().debug("Creating new serverpage for [" + id + "]");
                }
                synchronized (this) {
                    generateSourcecode(source,
                                       normalizedName,
                                       markupLanguage,
                                       programmingLanguage);

                    programInstance = loadProgram(newManager,
                                                  normalizedName,
                                                  markupLanguage,
                                                  programmingLanguage);
                }
            } else {
                // found an instance
                if (this.autoReload) {
                    long sourceLastModified = source.getLastModified();
                    // Has XSP changed?
                    // Note : lastModified can be 0 if source is dynamically generated.
                    // In that case, let the program instance decide if it is modified or not.
                    if (programInstance.modifiedSince(sourceLastModified)) {
                        if (getLogger().isDebugEnabled()) {
                            getLogger().debug("ReCreating serverpage for [" + id + "]");
                        }
                        synchronized (this) {
                            if (getLogger().isDebugEnabled()) {
                                getLogger().debug("Releasing old serverpage program [" + id + "]");
                            }
                            release(programInstance);
                            programmingLanguage.unload(program, normalizedName, this.workDir);
                            this.cache.removeGenerator(normalizedName);
                            programInstance = null;
                            program = null;

                            generateSourcecode(source,
                                               normalizedName,
                                               markupLanguage,
                                               programmingLanguage);

                            programInstance = loadProgram(newManager,
                                                          normalizedName,
                                                          markupLanguage,
                                                          programmingLanguage);
                        }
                    } else {
                        // check the repository for changes at all?
                        if (this.watchSource) {
                            if (getLogger().isDebugEnabled()) {
                                getLogger().debug("Checking sourcecode of [" + id + "] for a change");
                            }
                            File sourcecodeFile = new File(this.workDir,
                                                           normalizedName + "." + programmingLanguage.getSourceExtension());
                            // has sourcecode in repository changed ?
                            if (sourcecodeFile != null && sourcecodeFile.exists()) {
                                long sourcecodeLastModified = sourcecodeFile.lastModified();
                                if (sourcecodeLastModified > sourceLastModified
                                        || sourceLastModified == 0
                                        || sourcecodeLastModified == 0) {
                                    if (getLogger().isDebugEnabled()) {
                                        getLogger().debug("Create new serverpage program for [" + id + "] - repository has changed");
                                    }
                                    synchronized (this) {
                                        if (getLogger().isDebugEnabled()) {
                                            getLogger().debug("Releasing old serverpage program [" + id + "]");
                                        }
                                        release(programInstance);
                                        //programmingLanguage.unload(program, normalizedName, this.workDir);
                                        this.cache.removeGenerator(normalizedName);
                                        programInstance = null;
                                        program = null;

                                        programInstance = loadProgram(newManager,
                                                                      normalizedName,
                                                                      markupLanguage,
                                                                      programmingLanguage);
                                    }
                                } else {
                                    if (getLogger().isDebugEnabled()) {
                                        getLogger().debug("Sourcecode of [" + id + "] has not changed - returning program from cache");
                                    }
                                }
                            } else {
                                if (getLogger().isErrorEnabled()) {
                                    getLogger().error("Could not find sourcecode for [" + id + "]");
                                }
                            }
                        }
                    }
                } else {
                    if (getLogger().isDebugEnabled()) {
                        getLogger().debug("Not checking for modifications [autoReload=false] - using current version");
                    }
                }
            }

            // Recompose with the new manager if program needs it.
            // This is required to provide XSP with manager from the correct
            // sitemap so it will be able to find all components declared in
            // the sitemap.
            if (programInstance instanceof Recomposable) {
                ((Recomposable) programInstance).recompose(newManager);
            }

            return (programInstance);
        } finally {
            this.markupSelector.release(markupLanguage);
            this.languageSelector.release(programmingLanguage);
        }
    }

    private CompiledComponent loadProgram(ComponentManager newManager,
                                          String normalizedName,
                                          MarkupLanguage markupLanguage,
                                          ProgrammingLanguage programmingLanguage)
            throws Exception {

        CompiledComponent programInstance = null;

        try {
            return (CompiledComponent) this.cache.select(normalizedName);
        } catch (Exception e) {
        }

        try {
            if (getLogger().isDebugEnabled()) {
                getLogger().debug("Loading program [" + normalizedName + "]");
            }
            Program program = programmingLanguage.load(normalizedName, this.workDir, markupLanguage.getEncoding());

            this.cache.addGenerator(newManager, normalizedName, program);
            if (getLogger().isDebugEnabled()) {
                getLogger().debug("Successfully loaded program [" + normalizedName + "]");
            }
        } catch (LanguageException le) {
            if (getLogger().isDebugEnabled()) {
                getLogger().debug("Got Language Exception", le);
            }
            throw new ProcessingException("Language Exception", le);
        }

        try {
            programInstance = (CompiledComponent) this.cache.select(normalizedName);
        } catch (Exception cme) {
            if (getLogger().isDebugEnabled()) {
                getLogger().debug("Can't load ServerPage: got exception", cme);
            }
            throw new ProcessingException("Can't load ServerPage", cme);
        }

        return (programInstance);
    }


    private void generateSourcecode(Source source,
                                    String normalizedName,
                                    MarkupLanguage markupLanguage,
                                    ProgrammingLanguage programmingLanguage)
            throws Exception {

        if (getLogger().isDebugEnabled()) {
            getLogger().debug("Creating sourcecode for [" + source.getURI() + "]");
        }

        // Generate code
        String code = markupLanguage.generateCode(source, normalizedName, programmingLanguage);
        if (code == null || code.length() == 0) {
            // FIXME(VG): Xalan with incremental-processing=true does not propagate exceptions
            // from working thread to main thread. See
            // http://nagoya.apache.org/bugzilla/show_bug.cgi?id=8033
            throw new ProcessingException("Failed to generate program code (this may happen " +
                    "if you use Xalan in incremental processing mode). " +
                    "Please check log file and/or console for errors.");
        }

        String encoding = markupLanguage.getEncoding();

        // Format source code if applicable
        CodeFormatter codeFormatter = programmingLanguage.getCodeFormatter();
        if (codeFormatter != null) {
            code = codeFormatter.format(code, encoding);
        }

        // Store generated code
        final File sourceFile = new File(this.workDir, normalizedName + "." + programmingLanguage.getSourceExtension());
        final File sourceDir = sourceFile.getParentFile();
        if (sourceDir != null) {
            sourceDir.mkdirs();
        }
        IOUtils.serializeString(sourceFile, code);
        if (getLogger().isDebugEnabled()) {
            getLogger().debug("Successfully created sourcecode for [" + source.getURI() + "]");
        }
    }

    /**
     * Releases the program instance.
     * @param component program instance to be released
     */
    public void release(CompiledComponent component) {
        this.cache.release(component);
    }

    /**
     * Removes named program from the program generator's cache.
     * Disposes all created instances of the program.
     * @param source of the program to be removed
     */
    public void remove(Source source) {
        final String normalizedName = getNormalizedName(source.getURI());
        this.cache.removeGenerator(normalizedName);
    }

    /**
     *  dispose
     */
    public void dispose() {
        this.manager.release(this.cache);
        this.cache = null;
        this.manager.release(this.markupSelector);
        this.markupSelector = null;
        this.manager.release(this.languageSelector);
        this.languageSelector = null;
        this.manager.release(this.classManager);
        this.classManager = null;

        this.manager = null;

        this.workDir = null;
        this.contextDir = null;
    }
}
TOP

Related Classes of org.apache.cocoon.components.language.generator.ProgramGeneratorImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.