001: /*
002: * Copyright 2002,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.commons.jelly;
018:
019: import java.io.File;
020: import java.io.InputStream;
021: import java.io.FileInputStream;
022: import java.io.IOException;
023: import java.net.MalformedURLException;
024: import java.net.URL;
025: import java.util.Enumeration;
026: import java.util.Properties;
027:
028: import org.apache.commons.jelly.parser.XMLParser;
029: import org.apache.commons.jelly.util.ClassLoaderUtils;
030: import org.apache.commons.jelly.util.CommandLineParser;
031: import org.apache.commons.logging.Log;
032: import org.apache.commons.logging.LogFactory;
033:
034: import org.xml.sax.SAXException;
035:
036: /**
037: * <p><code>Jelly</code> is a helper class which is capable of
038: * running a Jelly script. This class can be used from the command line
039: * or can be used as the basis of an Ant task.</p> Command line usage is as follows:
040: *
041: * <pre>
042: * jelly [scriptFile] [-script scriptFile -o outputFile -Dsysprop=syspropval]
043: * </pre>
044: *
045: * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
046: * @version $Revision: 155420 $
047: */
048: public class Jelly {
049:
050: /** The Log to which logging calls will be made. */
051: private static final Log log = LogFactory.getLog(Jelly.class);
052:
053: /** The JellyContext to use */
054: private JellyContext context;
055:
056: /** The URL of the script to execute */
057: private URL url;
058:
059: /** The URL of the root context for other scripts */
060: private URL rootContext;
061:
062: /** Whether we have loaded the properties yet */
063: private boolean loadedProperties = false;
064:
065: /**
066: * whether to override the default namespace
067: */
068: private String defaultNamespaceURI = null;
069:
070: /**
071: * whether or not to validate the Jelly script
072: */
073: private boolean validateXML = false;
074:
075: public Jelly() {
076: }
077:
078: /**
079: * Usage: jelly [scriptFile] [-script scriptFile -o outputFile -Dsysprop=syspropval]
080: */
081: public static void main(String[] args) throws Exception {
082:
083: try {
084: if (args.length <= 0) {
085: System.out
086: .println("Usage: jelly [scriptFile] [-script scriptFile -o outputFile -Dsysprop=syspropval]");
087: return;
088: }
089:
090: // parse the command line options using CLI
091: // using a separate class to avoid unnecessary
092: // dependencies
093: CommandLineParser.getInstance()
094: .invokeCommandLineJelly(args);
095: } catch (JellyException e) {
096: Throwable cause = e.getCause();
097:
098: if (cause == null) {
099: e.printStackTrace();
100: } else {
101: cause.printStackTrace();
102: }
103: }
104: }
105:
106: public static String getJellyVersion() {
107: return readBuildTimestampResource("jelly-version.txt");
108: }
109:
110: public static String getJellyBuildDate() {
111: return readBuildTimestampResource("jelly-build-date.txt");
112: }
113:
114: private static String readBuildTimestampResource(String name) {
115: java.io.Reader in = null;
116: try {
117: java.io.StringWriter w = new java.io.StringWriter();
118: in = new java.io.InputStreamReader(Jelly.class
119: .getResourceAsStream(name), "utf-8");
120: int r;
121: while ((r = in.read()) >= 0) {
122: w.write((char) r);
123: }
124: return w.toString();
125: } catch (Exception ex) {
126: ex.printStackTrace();
127: try {
128: in.close();
129: } catch (Exception e) {
130: }
131: throw new IllegalStateException("Resource \"" + name
132: + "\" not found.");
133: }
134: }
135:
136: /**
137: * Compiles the script
138: */
139: public Script compileScript() throws JellyException {
140: if (!loadedProperties) {
141: loadedProperties = true;
142: loadJellyProperties();
143: }
144:
145: XMLParser parser = new XMLParser();
146: try {
147: parser.setContext(getJellyContext());
148: } catch (MalformedURLException e) {
149: throw new JellyException(e.toString());
150: }
151:
152: Script script = null;
153: try {
154: parser.setDefaultNamespaceURI(this .defaultNamespaceURI);
155: parser.setValidating(this .validateXML);
156: script = parser.parse(getUrl());
157: script = script.compile();
158: if (log.isDebugEnabled()) {
159: log.debug("Compiled script: " + getUrl());
160: }
161: } catch (IOException e) {
162: throw new JellyException("could not parse Jelly script", e);
163: } catch (SAXException e) {
164: throw new JellyException("could not parse Jelly script", e);
165: }
166:
167: return script;
168: }
169:
170: // Properties
171: //-------------------------------------------------------------------------
172:
173: /**
174: * Sets the script URL to use as an absolute URL or a relative filename
175: */
176: public void setScript(String script) throws MalformedURLException {
177: setUrl(resolveURL(script));
178: }
179:
180: public URL getUrl() {
181: return url;
182: }
183:
184: /**
185: * Sets the script URL to use
186: */
187: public void setUrl(URL url) {
188: this .url = url;
189: }
190:
191: /**
192: * Gets the root context
193: */
194: public URL getRootContext() throws MalformedURLException {
195: if (rootContext == null) {
196: rootContext = new File(System.getProperty("user.dir"))
197: .toURL();
198: }
199: return rootContext;
200: }
201:
202: /**
203: * Sets the root context
204: */
205: public void setRootContext(URL rootContext) {
206: this .rootContext = rootContext;
207: }
208:
209: /**
210: * The context to use
211: */
212: public JellyContext getJellyContext() throws MalformedURLException {
213: if (context == null) {
214: // take off the name off the URL
215: String text = getUrl().toString();
216: int idx = text.lastIndexOf('/');
217: text = text.substring(0, idx + 1);
218: context = new JellyContext(getRootContext(), new URL(text));
219: }
220: return context;
221: }
222:
223: /**
224: * Set the jelly namespace to use for unprefixed elements.
225: * Will be overridden by an explicit namespace in the
226: * XML document.
227: *
228: * @param namespace jelly namespace to use (e.g. 'jelly:core')
229: */
230: public void setDefaultNamespaceURI(String namespace) {
231: this .defaultNamespaceURI = namespace;
232: }
233:
234: /**
235: * When set to true, the XML parser will attempt to validate
236: * the Jelly XML before converting it into a Script.
237: *
238: * @param validate whether or not to validate
239: */
240: public void setValidateXML(boolean validate) {
241: this .validateXML = validate;
242: }
243:
244: // Implementation methods
245: //-------------------------------------------------------------------------
246: /**
247: * @return the URL for the relative file name or absolute URL
248: */
249: protected URL resolveURL(String name) throws MalformedURLException {
250:
251: URL resourceUrl = ClassLoaderUtils.getClassLoader(getClass())
252: .getResource(name);
253: if (resourceUrl == null) {
254: File file = new File(name);
255: if (file.exists()) {
256: return file.toURL();
257: }
258: return new URL(name);
259: } else {
260: return resourceUrl;
261: }
262: }
263:
264: /**
265: * Attempts to load jelly.properties from the current directory,
266: * the users home directory or from the classpath
267: */
268: protected void loadJellyProperties() {
269: InputStream is = null;
270:
271: String userDir = System.getProperty("user.home");
272: File f = new File(userDir + File.separator + "jelly.properties");
273: loadProperties(f);
274:
275: f = new File("jelly.properties");
276: loadProperties(f);
277:
278: is = ClassLoaderUtils.getClassLoader(getClass())
279: .getResourceAsStream("jelly.properties");
280: if (is != null) {
281: try {
282: loadProperties(is);
283: } catch (Exception e) {
284: log.error(
285: "Caught exception while loading jelly.properties from the classpath. Reason: "
286: + e, e);
287: }
288: }
289: }
290:
291: /**
292: * Load properties from a file into the context
293: * @param f
294: */
295: private void loadProperties(File f) {
296: InputStream is = null;
297: try {
298: if (f.exists()) {
299: is = new FileInputStream(f);
300: loadProperties(is);
301: }
302: } catch (Exception e) {
303: log.error("Caught exception while loading: " + f.getName()
304: + ". Reason: " + e, e);
305: } finally {
306: if (is != null) {
307: try {
308: is.close();
309: } catch (IOException e) {
310: if (log.isDebugEnabled())
311: log.debug(
312: "error closing property input stream",
313: e);
314: }
315: }
316: }
317: }
318:
319: /**
320: * Loads the properties from the given input stream
321: */
322: protected void loadProperties(InputStream is) throws IOException {
323: JellyContext theContext = getJellyContext();
324: Properties props = new Properties();
325: props.load(is);
326: Enumeration propsEnum = props.propertyNames();
327: while (propsEnum.hasMoreElements()) {
328: String key = (String) propsEnum.nextElement();
329: String value = props.getProperty(key);
330:
331: // @todo we should parse the value in case its an Expression
332: theContext.setVariable(key, value);
333: }
334: }
335: }
|