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 }