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: 1.34 $
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      * Compiles the script
107      */
108     public Script compileScript() throws JellyException {
109         if (! loadedProperties) {
110             loadedProperties = true;
111             loadJellyProperties();
112         }
113         
114         XMLParser parser = new XMLParser();
115         try {
116             parser.setContext(getJellyContext());
117         } catch (MalformedURLException e) {
118             throw new JellyException(e.toString());
119         }
120         
121         Script script = null;
122         try {
123             parser.setDefaultNamespaceURI(this.defaultNamespaceURI);
124             parser.setValidating(this.validateXML);
125             script = parser.parse(getUrl());
126             script = script.compile();
127             if (log.isDebugEnabled()) {
128                log.debug("Compiled script: " + getUrl());
129             }
130         } catch (IOException e) {
131             throw new JellyException("could not parse Jelly script",e);
132         } catch (SAXException e) {
133             throw new JellyException("could not parse Jelly script",e);
134         }
135         
136         return script;
137     }
138 
139     
140     // Properties
141     //-------------------------------------------------------------------------                
142     
143     /*** 
144      * Sets the script URL to use as an absolute URL or a relative filename
145      */
146     public void setScript(String script) throws MalformedURLException {
147         setUrl(resolveURL(script));
148     }
149     
150     public URL getUrl() {
151         return url;
152     }
153     
154     /*** 
155      * Sets the script URL to use 
156      */
157     public void setUrl(URL url) {
158         this.url = url;
159     }
160     
161     /*** 
162      * Gets the root context
163      */
164     public URL getRootContext() throws MalformedURLException {
165         if (rootContext == null) {
166             rootContext = new File(System.getProperty("user.dir")).toURL();
167         }
168         return rootContext;
169     }
170     
171     /*** 
172      * Sets the root context
173      */
174     public void setRootContext(URL rootContext) {
175         this.rootContext = rootContext;
176     }
177     
178     /***
179      * The context to use
180      */
181     public JellyContext getJellyContext() throws MalformedURLException {
182         if (context == null) {
183             // take off the name off the URL
184             String text = getUrl().toString();
185             int idx = text.lastIndexOf('/');
186             text = text.substring(0, idx + 1);
187             context = new JellyContext(getRootContext(), new URL(text));
188         }
189         return context;
190     }
191 
192 
193     /***
194      * Set the jelly namespace to use for unprefixed elements.
195      * Will be overridden by an explicit namespace in the
196      * XML document.
197      * 
198      * @param namespace jelly namespace to use (e.g. 'jelly:core')
199      */
200     public void setDefaultNamespaceURI(String namespace) {
201         this.defaultNamespaceURI = namespace;
202     }
203 
204     /***
205      * When set to true, the XML parser will attempt to validate
206      * the Jelly XML before converting it into a Script.
207      * 
208      * @param validate whether or not to validate
209      */
210     public void setValidateXML(boolean validate) {
211         this.validateXML = validate;
212     }
213     
214     // Implementation methods
215     //-------------------------------------------------------------------------                
216     /***
217      * @return the URL for the relative file name or absolute URL 
218      */
219     protected URL resolveURL(String name) throws MalformedURLException {
220         
221         URL resourceUrl = ClassLoaderUtils.getClassLoader(getClass()).getResource(name);
222         if (resourceUrl == null)
223         {
224             File file = new File(name);
225             if (file.exists()) {
226                 return file.toURL();
227             }
228             return new URL(name);
229         } else {
230             return resourceUrl;
231         }
232     }
233 
234     /***
235      * Attempts to load jelly.properties from the current directory,
236      * the users home directory or from the classpath
237      */
238     protected void loadJellyProperties() {
239         InputStream is = null;
240     
241         String userDir = System.getProperty("user.home");
242         File f = new File(userDir + File.separator + "jelly.properties");
243         loadProperties(f);
244     
245         f = new File("jelly.properties");
246         loadProperties(f);
247         
248         
249         is = ClassLoaderUtils.getClassLoader(getClass()).getResourceAsStream("jelly.properties");
250         if (is != null) {
251             try {
252                 loadProperties(is);
253             }
254             catch (Exception e) {
255                 log.error( "Caught exception while loading jelly.properties from the classpath. Reason: " + e, e );
256             }
257         }
258     }
259 
260     /***
261      * Load properties from a file into the context
262      * @param f
263      */
264     private void loadProperties(File f) {
265         InputStream is = null;
266         try {
267             if (f.exists()) {
268                 is = new FileInputStream(f);
269                 loadProperties(is);
270             }
271         } catch (Exception e) {
272             log.error( "Caught exception while loading: " + f.getName() + ". Reason: " + e, e );
273         } finally {
274             if (is != null) {
275                 try {
276                     is.close();
277                 } catch (IOException e) {
278                     if (log.isDebugEnabled()) log.debug("error closing property input stream", e);
279                 }
280             }
281         }
282     }
283 
284     /***
285      * Loads the properties from the given input stream 
286      */    
287     protected void loadProperties(InputStream is) throws IOException {
288         JellyContext theContext = getJellyContext();
289         Properties props = new Properties();
290         props.load(is);
291         Enumeration propsEnum = props.propertyNames();
292         while (propsEnum.hasMoreElements()) {
293             String key = (String) propsEnum.nextElement();
294             String value = props.getProperty(key);
295             
296             // @todo we should parse the value in case its an Expression
297             theContext.setVariable(key, value);
298         }
299     }
300 }