View Javadoc

1   /*
2    * Copyright 2002,2004 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.apache.commons.jelly;
18  
19  import java.io.File;
20  import java.io.InputStream;
21  import java.io.FileInputStream;
22  import java.io.IOException;
23  import java.net.MalformedURLException;
24  import java.net.URL;
25  import java.util.Enumeration;
26  import java.util.Properties;
27  
28  import org.apache.commons.jelly.parser.XMLParser;
29  import org.apache.commons.jelly.util.ClassLoaderUtils;
30  import org.apache.commons.jelly.util.CommandLineParser;
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  
34  import org.xml.sax.SAXException;
35  
36  /*** 
37   * <p><code>Jelly</code> is a helper class which is capable of
38   * running a Jelly script. This class can be used from the command line
39   * or can be used as the basis of an Ant task.</p> Command line usage is as follows:
40   *
41   * <pre>
42   * jelly [scriptFile] [-script scriptFile -o outputFile -Dsysprop=syspropval]
43   * </pre>
44   *
45   * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
46   * @version $Revision: 155420 $
47   */
48  public class Jelly {
49      
50      /*** The Log to which logging calls will be made. */
51      private static final Log log = LogFactory.getLog(Jelly.class);
52      
53      /*** The JellyContext to use */
54      private JellyContext context;
55      
56      /*** The URL of the script to execute */
57      private URL url;
58      
59      /*** The URL of the root context for other scripts */
60      private URL rootContext;
61      
62      /*** Whether we have loaded the properties yet */
63      private boolean loadedProperties = false;
64  
65      /***
66       * whether to override the default namespace
67       */
68      private String defaultNamespaceURI = null;
69  
70      /***
71       * whether or not to validate the Jelly script
72       */
73      private boolean validateXML = false;
74          
75      public Jelly() {
76      }
77  
78      /***
79       * Usage: jelly [scriptFile] [-script scriptFile -o outputFile -Dsysprop=syspropval]
80       */
81      public static void main(String[] args) throws Exception {
82  
83          try {
84              if (args.length <= 0) {
85                  System.out.println("Usage: jelly [scriptFile] [-script scriptFile -o outputFile -Dsysprop=syspropval]");
86                  return;
87              }
88  
89              // parse the command line options using CLI
90              // using a separate class to avoid unnecessary 
91              // dependencies
92              CommandLineParser.getInstance().invokeCommandLineJelly(args);
93          }
94          catch (JellyException e) {
95              Throwable cause = e.getCause();
96  
97              if (cause == null) {
98                  e.printStackTrace();
99              } else {
100                 cause.printStackTrace();
101             }
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.getResourceAsStream(name),"utf-8");
119             int r;
120             while ( (r=in.read()) >= 0 ) {
121                 w.write((char) r);
122             }
123             return w.toString();
124         } catch(Exception ex) {
125             ex.printStackTrace();
126             try { in.close(); } catch(Exception e) {}
127             throw new IllegalStateException("Resource \"" + name + "\" not found.");
128         }
129     }
130     
131     
132 
133     /***
134      * Compiles the script
135      */
136     public Script compileScript() throws JellyException {
137         if (! loadedProperties) {
138             loadedProperties = true;
139             loadJellyProperties();
140         }
141         
142         XMLParser parser = new XMLParser();
143         try {
144             parser.setContext(getJellyContext());
145         } catch (MalformedURLException e) {
146             throw new JellyException(e.toString());
147         }
148         
149         Script script = null;
150         try {
151             parser.setDefaultNamespaceURI(this.defaultNamespaceURI);
152             parser.setValidating(this.validateXML);
153             script = parser.parse(getUrl());
154             script = script.compile();
155             if (log.isDebugEnabled()) {
156                log.debug("Compiled script: " + getUrl());
157             }
158         } catch (IOException e) {
159             throw new JellyException("could not parse Jelly script",e);
160         } catch (SAXException e) {
161             throw new JellyException("could not parse Jelly script",e);
162         }
163         
164         return script;
165     }
166 
167     
168     // Properties
169     //-------------------------------------------------------------------------                
170     
171     /*** 
172      * Sets the script URL to use as an absolute URL or a relative filename
173      */
174     public void setScript(String script) throws MalformedURLException {
175         setUrl(resolveURL(script));
176     }
177     
178     public URL getUrl() {
179         return url;
180     }
181     
182     /*** 
183      * Sets the script URL to use 
184      */
185     public void setUrl(URL url) {
186         this.url = url;
187     }
188     
189     /*** 
190      * Gets the root context
191      */
192     public URL getRootContext() throws MalformedURLException {
193         if (rootContext == null) {
194             rootContext = new File(System.getProperty("user.dir")).toURL();
195         }
196         return rootContext;
197     }
198     
199     /*** 
200      * Sets the root context
201      */
202     public void setRootContext(URL rootContext) {
203         this.rootContext = rootContext;
204     }
205     
206     /***
207      * The context to use
208      */
209     public JellyContext getJellyContext() throws MalformedURLException {
210         if (context == null) {
211             // take off the name off the URL
212             String text = getUrl().toString();
213             int idx = text.lastIndexOf('/');
214             text = text.substring(0, idx + 1);
215             context = new JellyContext(getRootContext(), new URL(text));
216         }
217         return context;
218     }
219 
220 
221     /***
222      * Set the jelly namespace to use for unprefixed elements.
223      * Will be overridden by an explicit namespace in the
224      * XML document.
225      * 
226      * @param namespace jelly namespace to use (e.g. 'jelly:core')
227      */
228     public void setDefaultNamespaceURI(String namespace) {
229         this.defaultNamespaceURI = namespace;
230     }
231 
232     /***
233      * When set to true, the XML parser will attempt to validate
234      * the Jelly XML before converting it into a Script.
235      * 
236      * @param validate whether or not to validate
237      */
238     public void setValidateXML(boolean validate) {
239         this.validateXML = validate;
240     }
241     
242     // Implementation methods
243     //-------------------------------------------------------------------------                
244     /***
245      * @return the URL for the relative file name or absolute URL 
246      */
247     protected URL resolveURL(String name) throws MalformedURLException {
248         
249         URL resourceUrl = ClassLoaderUtils.getClassLoader(getClass()).getResource(name);
250         if (resourceUrl == null)
251         {
252             File file = new File(name);
253             if (file.exists()) {
254                 return file.toURL();
255             }
256             return new URL(name);
257         } else {
258             return resourceUrl;
259         }
260     }
261 
262     /***
263      * Attempts to load jelly.properties from the current directory,
264      * the users home directory or from the classpath
265      */
266     protected void loadJellyProperties() {
267         InputStream is = null;
268     
269         String userDir = System.getProperty("user.home");
270         File f = new File(userDir + File.separator + "jelly.properties");
271         loadProperties(f);
272     
273         f = new File("jelly.properties");
274         loadProperties(f);
275         
276         
277         is = ClassLoaderUtils.getClassLoader(getClass()).getResourceAsStream("jelly.properties");
278         if (is != null) {
279             try {
280                 loadProperties(is);
281             }
282             catch (Exception e) {
283                 log.error( "Caught exception while loading jelly.properties from the classpath. Reason: " + e, e );
284             }
285         }
286     }
287 
288     /***
289      * Load properties from a file into the context
290      * @param f
291      */
292     private void loadProperties(File f) {
293         InputStream is = null;
294         try {
295             if (f.exists()) {
296                 is = new FileInputStream(f);
297                 loadProperties(is);
298             }
299         } catch (Exception e) {
300             log.error( "Caught exception while loading: " + f.getName() + ". Reason: " + e, e );
301         } finally {
302             if (is != null) {
303                 try {
304                     is.close();
305                 } catch (IOException e) {
306                     if (log.isDebugEnabled()) log.debug("error closing property input stream", e);
307                 }
308             }
309         }
310     }
311 
312     /***
313      * Loads the properties from the given input stream 
314      */    
315     protected void loadProperties(InputStream is) throws IOException {
316         JellyContext theContext = getJellyContext();
317         Properties props = new Properties();
318         props.load(is);
319         Enumeration propsEnum = props.propertyNames();
320         while (propsEnum.hasMoreElements()) {
321             String key = (String) propsEnum.nextElement();
322             String value = props.getProperty(key);
323             
324             // @todo we should parse the value in case its an Expression
325             theContext.setVariable(key, value);
326         }
327     }
328 }