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: 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
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 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
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
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
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
325 theContext.setVariable(key, value);
326 }
327 }
328 }