org.xchain.StandAloneExecutor.java Source code

Java tutorial

Introduction

Here is the source code for org.xchain.StandAloneExecutor.java

Source

/**
 *    Copyright 2011 meltmedia
 *
 *    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.xchain;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;

import javax.xml.namespace.QName;

import org.apache.commons.jxpath.JXPathContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xchain.Catalog;
import org.xchain.Command;
import org.xchain.framework.factory.CatalogFactory;
import org.xchain.framework.jxpath.ScopedQNameVariables;
import org.xchain.framework.lifecycle.Execution;
import org.xchain.framework.lifecycle.ExecutionException;
import org.xchain.framework.lifecycle.ExecutionTraceElement;
import org.xchain.framework.lifecycle.Lifecycle;
import org.xchain.framework.lifecycle.LifecycleException;
import org.xml.sax.Locator;

/**
 * <p>Bootstraps the environment for executing an XChain.  It used an XML configuration file for
 * determining what catalog to use and which commands to execute.</p>
 * 
 * @author Josh Kennedy
 */
public class StandAloneExecutor implements Executor {
    public static final String CONFIGURATION_SYSTEM_PROPERTY = "org.xchain.executor.configuration";
    public static final String DATASETLOADER_CONFIGURATION_DEFAULT = "executor.xml";

    public static final String CATALOG_URI = "{http://www.xchain.org/}catalog";
    public static final String EXECUTE_COMMAND = "{http://www.xchain.org/}command";

    public static final Logger log = LoggerFactory.getLogger(StandAloneExecutor.class);

    private Properties properties;

    /**
     * Complete any configuration needed for starting the Life Cycle
     */
    public void initLifeCycle() {
    }

    /**
     * Start the Life Cycle
     */
    public void startLifeCycle() throws LifecycleException {
        Lifecycle.startLifecycle();
    }

    /**
     * Get the given Catalog based on the property CATALOG_URI
     */
    public Catalog getCatalog(String uri) throws CatalogNotFoundException, CatalogLoadException {
        return CatalogFactory.getInstance().getCatalog(uri);
    }

    /**
     * Get the command off of the Catalog
     */
    public Command getCommand(Catalog catalog, String cmd) throws CommandNotFoundException {
        return catalog.getCommand(cmd);
    }

    /**
     * Create the JXPathContext that is going to be used
     */
    public JXPathContext getContext() {
        return JXPathContext.newContext(new HashMap<Object, Object>());
    }

    /**
     * Calls configure context passing in the properties configuration
     */
    public void configureContext(JXPathContext context, Map<QName, Object> variables) {
        ScopedQNameVariables contextVariables = (ScopedQNameVariables) context.getVariables();

        for (Entry<QName, Object> entry : variables.entrySet()) {
            contextVariables.declareVariable(entry.getKey(), entry.getValue());
        }
    }

    /**
     * Load the properties configuration into the Context, while maintaining proper scoping
     * if it's present in the variable name
     * 
     * @param context
     * @param properties
     */
    protected void configureContext(JXPathContext context, Properties properties) {
        HashMap<QName, Object> variables = new HashMap<QName, Object>();

        for (Object key : properties.keySet()) {
            variables.put(new QName((String) key), properties.get(key));
        }

        configureContext(context, variables);
    }

    /**
     * Stop the Life Cycle
     */
    public void stopLifeCycle() throws LifecycleException {
        Lifecycle.stopLifecycle();
    }

    /**
     * Step through the process of using an XChain in the needed order
     * 
     * @throws Exception
     */
    public void execute() throws Exception {
        // Step 1: Configure Lifecycle
        initLifeCycle();

        // Step 2: Start life cycle
        startLifeCycle();

        try {
            // Step 3: Get the catalog
            Catalog catalog = getCatalog(properties.getProperty(CATALOG_URI));

            // Step 4: Get the command
            Command command = getCommand(catalog, properties.getProperty(EXECUTE_COMMAND));

            // Step 5: Get the context
            JXPathContext context = getContext();

            // Step 5.5: Configure Context
            configureContext(context, properties);

            // Step 6: Execute the command
            command.execute(context);
        } catch (ExecutionException e) {
            printStack(e.getMessage(), "error", e.getExecutionTrace());
            throw e;
        } catch (Exception e) {
            printStack(e.getMessage(), "error");
            throw e;
        } finally {
            // Step 8: Stop life cycle
            Lifecycle.stopLifecycle();
        }
    }

    /**
     * Retrieve the properties configuration that the Executor is using
     * @return
     */
    public Properties getProperties() {
        return properties;
    }

    /**
     * Set the properties configuration that the Executor will use
     * @param properties
     */
    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public void printStack(String message) {
        printStack(message, "warn");
    }

    public void printStack(String message, String level) {
        printStack(message, level, Execution.getExecutionTrace());
    }

    public void printStack(String message, String level, List<ExecutionTraceElement> stack) {
        StringBuffer buffer = new StringBuffer();

        buffer.append(message);
        buffer.append("\n");
        for (ExecutionTraceElement element : stack) {
            Locator locator = element.getLocator();
            buffer.append("\t running ");
            buffer.append(element.getQName().toString());
            buffer.append(" in ");
            buffer.append(element.getSystemId());
            buffer.append(" at ");
            buffer.append(locator.getLineNumber());
            buffer.append(":");
            buffer.append(locator.getColumnNumber());
            buffer.append("\n");
        }

        if (level.toLowerCase().equals("trace")) {
            log.trace(buffer.toString());
        }
        if (level.toLowerCase().equals("debug")) {
            log.debug(buffer.toString());
        }
        if (level.toLowerCase().equals("info")) {
            log.info(buffer.toString());
        }
        if (level.toLowerCase().equals("warn")) {
            log.warn(buffer.toString());
        }
        if (level.toLowerCase().equals("error")) {
            log.error(buffer.toString());
        }
    }

    /**
     * This method is used to populate the defaults for the properties that will be used to execute 
     * the XChain.
     * 
     * @return
     */
    protected static Properties getDefaultProperties() {
        return new Properties() {
            private static final long serialVersionUID = -3730355766429570858L;

            {
                this.setProperty(EXECUTE_COMMAND, "dispatcher");
                this.setProperty("command", "latest");
            }
        };
    }

    /**
     * Attempts to load properties based off of the three methods to define where a
     * configuration file is.  First it will try the "standard" configuration placement
     * "./executor.xml", then it will look for a jvm property CONFIGURATION_SYSTEM_PROPERTY,
     * and finally it will check to see if a file name was passed in
     * 
     * @param properties Properties Object to use
     * @param args Arguments passed in from the command line
     * @return
     */
    protected static boolean loadProperties(Properties properties, String[] args) {
        boolean defaultProp = false;
        boolean jvmProp = false;
        boolean cmdProp = false;
        // This follows the order of default, JVM Param, and finally command line
        // As they are loaded, each new one will overwrite any properties that were
        // loaded prior, but leave any that don't exist in the current file
        defaultProp = loadPropertiesFromFile(DATASETLOADER_CONFIGURATION_DEFAULT, properties);

        if (System.getProperty(CONFIGURATION_SYSTEM_PROPERTY) != null) {
            jvmProp = loadPropertiesFromFile(System.getProperty(CONFIGURATION_SYSTEM_PROPERTY), properties);
        }

        if (args.length > 0) {
            cmdProp = loadPropertiesFromFile(args[0], properties);
        }

        if (defaultProp || jvmProp || cmdProp) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Attempts to load the properties from both the regular file system, as well as
     * from the contents of the class loader.  If there are files in both locations
     * they will both be loaded into the properties, the file system takes precedence.
     * 
     * @param name file name
     * @param properties properties object to load file into
     * @return
     */
    protected static boolean loadPropertiesFromFile(String name, Properties properties) {
        boolean status = false;

        InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(name);
        File propertiesFile = new File(name);

        if (stream != null) {
            try {
                properties.loadFromXML(stream);
                status = true;
            } catch (IOException e) {
                log.debug("Unable to read configuration file '" + name + "'", e);
            } finally {
                try {
                    stream.close();
                } catch (NullPointerException e) {
                } catch (IOException e) {
                }
            }
        }

        if (propertiesFile != null && propertiesFile.exists()) {
            try {
                stream = new FileInputStream(propertiesFile);
                properties.loadFromXML(stream);
                status = true;
            } catch (FileNotFoundException e) {
                log.debug("Unable to find configuration file '" + name + "'", e);
            } catch (IOException e) {
                log.debug("Unable to load configuration file '" + name + "'", e);
            } finally {
                try {
                    stream.close();
                } catch (NullPointerException e) {
                } catch (IOException e) {
                }
            }
        }

        return status;
    }

    public static void main(String[] args) {
        // Set some sane defaults . . .
        Properties properties = getDefaultProperties();

        if (!loadProperties(properties, args)) {
            log.warn(
                    "Unable to find and load a configuration, proceeding with defaults.  Please see the usage documentaion.");
        }

        try {
            StandAloneExecutor executor = new StandAloneExecutor();

            executor.setProperties(properties);

            executor.execute();
        } catch (Exception e) {
            log.error("There was an unexpected exception while executing the xchain", e);
        }
    }
}