1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
90
91
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
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
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
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
297 theContext.setVariable(key, value);
298 }
299 }
300 }