/** * Copyright (C) 2012 Orbeon, Inc. * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU Lesser General Public License as published by the Free Software Foundation; either version * 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * The full text of the license is available at http://www.gnu.org/copyleft/lesser.html */ package org.orbeon.oxf.main; import org.apache.commons.cli.*; import org.apache.log4j.Logger; import org.orbeon.dom.Document; import org.orbeon.dom.QName; import org.orbeon.dom.io.DocumentException; import org.orbeon.dom.io.SAXReader; import org.orbeon.exception.OrbeonFormatter; import org.orbeon.oxf.common.OXFException; import org.orbeon.oxf.common.Version; import org.orbeon.oxf.main.CommandLineExternalContext; import org.orbeon.oxf.pipeline.InitUtils; import org.orbeon.oxf.externalcontext.ExternalContext; import org.orbeon.oxf.pipeline.api.PipelineContext; import org.orbeon.oxf.pipeline.api.ProcessorDefinition; import org.orbeon.oxf.processor.Processor; import org.orbeon.oxf.processor.ProcessorOutput; import org.orbeon.oxf.processor.serializer.URLSerializer; import org.orbeon.oxf.properties.Properties; import org.orbeon.oxf.resources.ResourceManagerWrapper; import org.orbeon.oxf.resources.URLFactory; import org.orbeon.oxf.util.LoggerFactory; import org.orbeon.oxf.util.NetUtils; import org.orbeon.oxf.util.PipelineUtils; import org.orbeon.oxf.webapp.ProcessorService; import org.orbeon.oxf.xml.XMLConstants; import org.orbeon.oxf.xml.XMLParsing; import org.xml.sax.InputSource; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import java.io.File; import java.io.StringReader; import java.net.MalformedURLException; import java.net.URL; import java.util.LinkedHashMap; import java.util.Map; import java.util.StringTokenizer; import static org.orbeon.oxf.pipeline.InitUtils.createProcessor; /** * This is a simple command-line interface to the pipeline engine. * * The command-line interface also illustrates how to use the basic pipeline engine APIs. * * This code performs the following major steps: * * 1. Parse the command-line arguments * 2. Initialize a resource manager * 3. Initialize OXF Properties * 4. Initialize logger based on properties * 5. Build a processor definition object * 6. Initialize a PipelineContext * 7. Run the pipeline * 8. Display exceptions if needed */ public class OPS { private static Logger logger = Logger.getLogger(OPS.class); private String resourceManagerSandbox; private String[] otherArgs; private ProcessorDefinition processorDefinition; private String[] inputs; private String[] outputs; public OPS(String[] args) { // 1. Parse the command-line arguments parseArgs(args); } public void init() { // Initialize a basic logging configuration until the resource manager is setup LoggerFactory.initBasicLogger(); // 2. Initialize resource manager // Resources are first searched in a file hierarchy, then from the classloader final Map<String, Object> props = new LinkedHashMap<String, Object>(); props.put("oxf.resources.factory", "org.orbeon.oxf.resources.PriorityResourceManagerFactory"); if (resourceManagerSandbox != null) { // Use a sandbox props.put("oxf.resources.filesystem.sandbox-directory", resourceManagerSandbox); } props.put("oxf.resources.priority.1", "org.orbeon.oxf.resources.FilesystemResourceManagerFactory"); props.put("oxf.resources.priority.2", "org.orbeon.oxf.resources.ClassLoaderResourceManagerFactory"); if (logger.isInfoEnabled()) logger.info("Initializing Resource Manager with: " + ResourceManagerWrapper.propertiesAsJson(props)); ResourceManagerWrapper.init(props); // 3. Initialize properties with default properties file. Properties.init(Properties.DEFAULT_PROPERTIES_URI); // 4. Initialize log4j (using the properties this time) LoggerFactory.initLogger(); // 5. Build processor definition from command-line parameters if (otherArgs != null && otherArgs.length == 1) { // Assume the pipeline processor and a config input processorDefinition = new ProcessorDefinition(QName.get("pipeline", XMLConstants.OXF_PROCESSORS_NAMESPACE)); final String configURL; if (!NetUtils.urlHasProtocol(otherArgs[0])) { // URL is considered relative to current directory try { // Create absolute URL, and switch to the oxf: protocol String fileURL = new URL(new File(".").toURI().toURL(), otherArgs[0]).toExternalForm(); configURL = "oxf:" + fileURL.substring(fileURL.indexOf(':') + 1); } catch (MalformedURLException e) { throw new OXFException(e); } } else { configURL = otherArgs[0]; } processorDefinition.addInput("config", configURL); // Add additional inputs if any for (int i=0; inputs!=null && i < inputs.length; i ++) { String input = inputs[i]; int iEqual = input.indexOf("="); if (iEqual <= 0 || iEqual >= input.length() - 1) { throw new OXFException("Input \"" + input + "\" doesn't follow the syntax <name>=<url>"); } String inputName = input.substring(0, iEqual ); String inputValue = input.substring(iEqual + 1); if (inputValue.startsWith("<")) { // XML document try { processorDefinition.addInput(inputName, parseText(inputValue).getRootElement()); } catch (DocumentException e) { throw new OXFException(e); } } else { // URL processorDefinition.addInput(inputName, inputValue); } } } else { throw new OXFException("No main processor definition found."); } } private static Document parseText(String text) throws DocumentException { final SAXReader reader = new SAXReader(XMLParsing.newXMLReader(XMLParsing.ParserConfiguration.PLAIN)); final String encoding = getEncoding(text); InputSource source = new InputSource(new StringReader(text)); source.setEncoding(encoding); return reader.read(source); } private static String getEncoding(String text) { String result = null; final String xml = text.trim(); if (xml.startsWith("<?xml")) { final int end = xml.indexOf("?>"); final String sub = xml.substring(0, end); final StringTokenizer tokens = new StringTokenizer(sub, " =\"\'"); while (tokens.hasMoreTokens()) { final String token = tokens.nextToken(); if ("encoding".equals(token)) { if (tokens.hasMoreTokens()) { result = tokens.nextToken(); } break; } } } return result; } private void parseArgs(String[] args) { final Options options = new Options(); { final Option o = new Option("r", "root", true, "Specifies the resource manager root"); o.setRequired(false); options.addOption(o); } { final Option o = new Option("v", "version", false, "Displays the version of Orbeon Forms"); o.setRequired(false); options.addOption(o); } { final Option o = new Option("i", "input", true, "Map an input to a URL (<input name>=<URL>)"); o.setRequired(false); options.addOption(o); } { final Option o = new Option("o", "output", true, "Map an output to a URL (<output name>=<URL>)"); o.setRequired(false); options.addOption(o); } try { // Parse the command line options final CommandLine cmd = new PosixParser().parse(options, args, true); // Get resource manager root if any resourceManagerSandbox = cmd.getOptionValue('r'); // Get inputs and outputs if any inputs = cmd.getOptionValues('i'); outputs = cmd.getOptionValues('o'); // Check for remaining args otherArgs = cmd.getArgs(); // Print version if asked if (cmd.hasOption('v')) { System.out.println(Version.VersionString()); // Terminate if there is no other argument and no pipeline URL if (!cmd.hasOption('r') && (otherArgs == null || otherArgs.length == 0)) System.exit(0); } if (otherArgs == null || otherArgs.length != 1) { new HelpFormatter().printHelp("Pipeline URL is required", options); System.exit(1); } } catch (MissingArgumentException e) { new HelpFormatter().printHelp("Missing argument", options); System.exit(1); } catch (UnrecognizedOptionException e) { new HelpFormatter().printHelp("Unrecognized option", options); System.exit(1); } catch (MissingOptionException e) { new HelpFormatter().printHelp("Missing option", options); System.exit(1); } catch (Exception e) { new HelpFormatter().printHelp("Unknown error", options); System.exit(1); } } public void start() { // 6. Initialize a PipelineContext final PipelineContext pipelineContext = new PipelineContext(); // Some processors may require a JNDI context. In general, this is not required. final Context jndiContext; try { jndiContext = new InitialContext(); } catch (NamingException e) { throw new OXFException(e); } pipelineContext.setAttribute(ProcessorService.JNDIContext(), jndiContext); try { // 7. Run the pipeline from the processor definition created earlier. An ExternalContext // is supplied for those processors using external contexts, such as most serializers. InitUtils.processorDefinitions(); final ExternalContext externalContext = new CommandLineExternalContext(); if (outputs == null) { // No outputs to connect: just execute the pipeline InitUtils.runProcessor(createProcessor(processorDefinition), externalContext, pipelineContext, logger); } else { // At least one output to connect... final Processor processor = createProcessor(processorDefinition); processor.reset(pipelineContext); pipelineContext.setAttribute(PipelineContext.EXTERNAL_CONTEXT, externalContext); // Loop over outputs for (int i = 0; i < outputs.length; i ++) { final String outputArg = outputs[i]; final int iEqual = outputArg.indexOf("="); if (iEqual <= 0 || iEqual >= outputArg.length() - 1) { throw new OXFException("Output \"" + outputArg + "\" doesn't follow the syntax <name>=<url>"); } final String outputName = outputArg.substring(0, iEqual ); final URL url = URLFactory.createURL(outputArg.substring(iEqual + 1)); // Create the output ProcessorOutput output = processor.createOutput(outputName); // Connect to an URL serializer final URLSerializer urlSerializer = new URLSerializer(); PipelineUtils.connect(processor, output.getName(), urlSerializer, "data"); // Serialize urlSerializer.serialize(pipelineContext, url); } } } catch (Exception e) { // 8. Display exceptions if needed logger.error(OrbeonFormatter.format(e)); System.exit(1); } } public static void main(String[] args) { try { final OPS orbeon = new OPS(args); orbeon.init(); orbeon.start(); } catch (Exception e) { logger.error(OrbeonFormatter.format(e)); System.exit(1); } } }