blob: ce3434dc73d5976f42ddc91ec771fecf23ee6ae7 [file] [log] [blame]
/*
* Copyright 2002,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.commons.jelly;
import java.io.File;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.Properties;
import org.apache.commons.jelly.parser.XMLParser;
import org.apache.commons.jelly.util.ClassLoaderUtils;
import org.apache.commons.jelly.util.CommandLineParser;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.SAXException;
/**
* <p><code>Jelly</code> is a helper class which is capable of
* running a Jelly script. This class can be used from the command line
* or can be used as the basis of an Ant task.</p> Command line usage is as follows:
*
* <pre>
* jelly [scriptFile] [-script scriptFile -o outputFile -Dsysprop=syspropval]
* </pre>
*
* @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
* @version $Revision: 1.35 $
*/
public class Jelly {
/** The Log to which logging calls will be made. */
private static final Log log = LogFactory.getLog(Jelly.class);
/** The JellyContext to use */
private JellyContext context;
/** The URL of the script to execute */
private URL url;
/** The URL of the root context for other scripts */
private URL rootContext;
/** Whether we have loaded the properties yet */
private boolean loadedProperties = false;
/**
* whether to override the default namespace
*/
private String defaultNamespaceURI = null;
/**
* whether or not to validate the Jelly script
*/
private boolean validateXML = false;
public Jelly() {
}
/**
* Usage: jelly [scriptFile] [-script scriptFile -o outputFile -Dsysprop=syspropval]
*/
public static void main(String[] args) throws Exception {
try {
if (args.length <= 0) {
System.out.println("Usage: jelly [scriptFile] [-script scriptFile -o outputFile -Dsysprop=syspropval]");
return;
}
// parse the command line options using CLI
// using a separate class to avoid unnecessary
// dependencies
CommandLineParser.getInstance().invokeCommandLineJelly(args);
}
catch (JellyException e) {
Throwable cause = e.getCause();
if (cause == null) {
e.printStackTrace();
} else {
cause.printStackTrace();
}
}
}
public static String getJellyVersion() {
return readBuildTimestampResource("jelly-version.txt");
}
public static String getJellyBuildDate() {
return readBuildTimestampResource("jelly-build-date.txt");
}
private static String readBuildTimestampResource(String name) {
java.io.Reader in = null;
try {
java.io.StringWriter w = new java.io.StringWriter();
in = new java.io.InputStreamReader(Jelly.class.getResourceAsStream(name),"utf-8");
int r;
while ( (r=in.read()) >= 0 ) {
w.write((char) r);
}
return w.toString();
} catch(Exception ex) {
ex.printStackTrace();
try { in.close(); } catch(Exception e) {}
throw new IllegalStateException("Resource \"" + name + "\" not found.");
}
}
/**
* Compiles the script
*/
public Script compileScript() throws JellyException {
if (! loadedProperties) {
loadedProperties = true;
loadJellyProperties();
}
XMLParser parser = new XMLParser();
try {
parser.setContext(getJellyContext());
} catch (MalformedURLException e) {
throw new JellyException(e.toString());
}
Script script = null;
try {
parser.setDefaultNamespaceURI(this.defaultNamespaceURI);
parser.setValidating(this.validateXML);
script = parser.parse(getUrl());
script = script.compile();
if (log.isDebugEnabled()) {
log.debug("Compiled script: " + getUrl());
}
} catch (IOException e) {
throw new JellyException("could not parse Jelly script",e);
} catch (SAXException e) {
throw new JellyException("could not parse Jelly script",e);
}
return script;
}
// Properties
//-------------------------------------------------------------------------
/**
* Sets the script URL to use as an absolute URL or a relative filename
*/
public void setScript(String script) throws MalformedURLException {
setUrl(resolveURL(script));
}
public URL getUrl() {
return url;
}
/**
* Sets the script URL to use
*/
public void setUrl(URL url) {
this.url = url;
}
/**
* Gets the root context
*/
public URL getRootContext() throws MalformedURLException {
if (rootContext == null) {
rootContext = new File(System.getProperty("user.dir")).toURL();
}
return rootContext;
}
/**
* Sets the root context
*/
public void setRootContext(URL rootContext) {
this.rootContext = rootContext;
}
/**
* The context to use
*/
public JellyContext getJellyContext() throws MalformedURLException {
if (context == null) {
// take off the name off the URL
String text = getUrl().toString();
int idx = text.lastIndexOf('/');
text = text.substring(0, idx + 1);
context = new JellyContext(getRootContext(), new URL(text));
}
return context;
}
/**
* Set the jelly namespace to use for unprefixed elements.
* Will be overridden by an explicit namespace in the
* XML document.
*
* @param namespace jelly namespace to use (e.g. 'jelly:core')
*/
public void setDefaultNamespaceURI(String namespace) {
this.defaultNamespaceURI = namespace;
}
/**
* When set to true, the XML parser will attempt to validate
* the Jelly XML before converting it into a Script.
*
* @param validate whether or not to validate
*/
public void setValidateXML(boolean validate) {
this.validateXML = validate;
}
// Implementation methods
//-------------------------------------------------------------------------
/**
* @return the URL for the relative file name or absolute URL
*/
protected URL resolveURL(String name) throws MalformedURLException {
URL resourceUrl = ClassLoaderUtils.getClassLoader(getClass()).getResource(name);
if (resourceUrl == null)
{
File file = new File(name);
if (file.exists()) {
return file.toURL();
}
return new URL(name);
} else {
return resourceUrl;
}
}
/**
* Attempts to load jelly.properties from the current directory,
* the users home directory or from the classpath
*/
protected void loadJellyProperties() {
InputStream is = null;
String userDir = System.getProperty("user.home");
File f = new File(userDir + File.separator + "jelly.properties");
loadProperties(f);
f = new File("jelly.properties");
loadProperties(f);
is = ClassLoaderUtils.getClassLoader(getClass()).getResourceAsStream("jelly.properties");
if (is != null) {
try {
loadProperties(is);
}
catch (Exception e) {
log.error( "Caught exception while loading jelly.properties from the classpath. Reason: " + e, e );
}
}
}
/**
* Load properties from a file into the context
* @param f
*/
private void loadProperties(File f) {
InputStream is = null;
try {
if (f.exists()) {
is = new FileInputStream(f);
loadProperties(is);
}
} catch (Exception e) {
log.error( "Caught exception while loading: " + f.getName() + ". Reason: " + e, e );
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
if (log.isDebugEnabled()) log.debug("error closing property input stream", e);
}
}
}
}
/**
* Loads the properties from the given input stream
*/
protected void loadProperties(InputStream is) throws IOException {
JellyContext theContext = getJellyContext();
Properties props = new Properties();
props.load(is);
Enumeration propsEnum = props.propertyNames();
while (propsEnum.hasMoreElements()) {
String key = (String) propsEnum.nextElement();
String value = props.getProperty(key);
// @todo we should parse the value in case its an Expression
theContext.setVariable(key, value);
}
}
}