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.avalon;
18  
19  import java.util.Map;
20  import java.util.HashMap;
21  import java.util.Iterator;
22  import java.net.URL;
23  import java.net.MalformedURLException;
24  import java.io.File;
25  import java.io.InputStream;
26  import java.io.OutputStream;
27  
28  // Avalon
29  import org.apache.avalon.framework.configuration.Configurable;
30  import org.apache.avalon.framework.configuration.Configuration;
31  import org.apache.avalon.framework.configuration.ConfigurationException;
32  
33  // Jelly
34  import org.apache.commons.jelly.Jelly;
35  import org.apache.commons.jelly.JellyContext;
36  import org.apache.commons.jelly.JellyException;
37  import org.apache.commons.jelly.Script;
38  import org.apache.commons.jelly.XMLOutput;
39  
40  /***
41   * An Avalon based service for executing Jelly scripts. The
42   * service allows executing a script based on a name as well
43   * as by a URL.
44   *
45   * @author <a href="mailto:robert@bull-enterprises.com">Robert McIntosh</a>
46   * @version $Revision: 1.4 $
47   */
48  public class JellyServiceImpl implements JellyService, Configurable {
49  
50      private boolean m_configured = false;
51      private Map m_scripts = new HashMap();
52  
53      /***
54       * Constructor for JellyService.
55       */
56      public JellyServiceImpl() {
57          super();
58      }
59  
60      /***
61       * @see org.apache.commons.jelly.avalon.JellyService.runNamedScript(String, Map)
62       */
63      public Map runNamedScript( String name, Map params ) throws Exception {
64          return runNamedScript(name, params, createXMLOutput());
65      }
66  
67      /***
68       * @see org.apache.commons.jelly.avalon.JellyService.runNamedScript(String, Map, XMLOutput)
69       */
70      public Map runNamedScript( String name, Map params, XMLOutput output ) throws Exception {
71          if( !m_scripts.containsKey( name ) )
72              throw new JellyException( "No script exists for script name [" + name + "]" );
73  
74          Script script = (Script)m_scripts.get( name );
75          JellyContext context = createJellyContext();
76  
77          context.setVariables( params );
78  
79          script.run( context, output );
80          return context.getVariables();
81      }
82  
83       /***
84       * @see org.apache.commons.jelly.avalon.JellyService.runNamedScript(String, Map, OutputStream)
85       */
86      public Map runNamedScript( String name, Map params, OutputStream out ) throws Exception {
87          XMLOutput xmlOutput = XMLOutput.createXMLOutput( out );
88          Map answer = runNamedScript(name, params, xmlOutput);
89          xmlOutput.flush();
90          return answer;
91      }
92  
93       /***
94       * @see org.apache.commons.jelly.avalon.JellyService.runScript(String, Map, XMLOutput)
95       */
96      public Map runScript( String url, Map params, XMLOutput output ) throws Exception {
97          URL actualUrl = null;
98          try {
99             actualUrl = new URL( url );
100         }
101         catch( MalformedURLException x ) {
102             throw new JellyException( "Could not find script at URL [" + url + "]: " +
103                                         x.getMessage(), x );
104         }
105 
106         // Set up the context
107         JellyContext context = createJellyContext();
108         context.setVariables( params );
109 
110         // Run the script
111         context.runScript(url, output);
112         return context.getVariables();
113     }
114 
115      /***
116      * @see org.apache.commons.jelly.avalon.JellyService.runScript(String, Map, OutputStream)
117      */
118     public Map runScript( String url, Map params, OutputStream out ) throws Exception {
119         XMLOutput xmlOutput = XMLOutput.createXMLOutput( out );
120         Map answer = runScript(url, params, xmlOutput);
121         xmlOutput.flush();
122         return answer;
123     }
124 
125      /***
126      * @see org.apache.commons.jelly.avalon.JellyService.runScript(String, Map)
127      */
128     public Map runScript( String url, Map params ) throws Exception {
129         return runScript(url, params, createXMLOutput());
130     }
131 
132 
133     // Configurable interface
134     //-------------------------------------------------------------------------
135 
136 
137     /***
138      * <p>Configures the Jelly Service with named scripts.</p>
139      *
140      * <p>
141      * The configuration looks like:
142      * </p>
143      * <p>
144      * &lt;jelly&gt;<br />
145      * &nbsp;&nbsp;&lt;script&gt;<br />
146      * &nbsp;&nbsp;&nbsp;&nbsp;&lt;name&gt;script name&lt;/name&gt;<br />
147      * &nbsp;&nbsp;&nbsp;&nbsp;&lt;url validate="false"&gt;url to script file&lt;/url&gt;<br />
148      * &nbsp;&nbsp;&lt;/script&gt;<br />
149      * &lt;/jelly&gt;<br />
150      * </p>
151      * <p>
152      *   Where each &lt;script&gt; element defines a seperate script. The validate attribute
153      *   on the url tag is optional and defaults to false.
154      * </p>
155      *
156      * @param config The configuration
157      * @exception ConfigurationException
158      */
159     public void configure( Configuration config ) throws ConfigurationException {
160         if( m_configured )
161             throw new ConfigurationException( "configure may only be executed once" );
162 
163         if( !"jelly".equals( config.getName() ) )
164             throw new ConfigurationException( "Expected <jelly> but got " + config.getName() );
165 
166         // Configure named scripts
167         Configuration[] scripts = config.getChildren( "scripts" );
168         for (int i = 0; i < scripts.length; i++) {
169             String name = config.getChild( "name" ).getValue();
170 
171             // Try to load and compile the script
172             try {
173                 String scriptName = config.getChild( "url" ).getValue();
174                 // Try to load the script via file, then by URL, then by classloader
175                 URL url = null;
176                 File file = new File( scriptName );
177                 if( file.exists() ) {
178                     url = file.toURL();
179                 }
180                 else {
181                     try {
182                         url = new URL( scriptName );
183                     }
184                     catch( MalformedURLException mfue ) {
185                       // Last try, via classloader
186                       url = getClass().getResource( scriptName );
187                     }
188                 }
189 
190                 // All atempts failed...
191                 if( url == null )
192                     throw new ConfigurationException( "Could not find script [" + scriptName + "]" );
193 
194                 // Get the script and store it
195                 Jelly jelly = new Jelly();
196                 jelly.setUrl( url );
197                 boolean validate = config.getChild( "url" ).getAttributeAsBoolean( "validate",  false );
198                 jelly.setValidateXML( validate );
199                 Script script = jelly.compileScript();
200 
201                 m_scripts.put( name, script );
202             }
203             catch( Throwable t ) {
204                 throw new ConfigurationException( "Could not load script [" + name + "]: " + t.getMessage() );
205             }
206         }
207     }
208 
209 
210     // Implementation methods
211     //-------------------------------------------------------------------------
212 
213     /***
214      * Factory method to create a new JellyContext instance. Derived classes
215      * could overload this method to provide a custom JellyContext instance.
216      */
217     protected JellyContext createJellyContext() {
218         return new JellyContext();
219     }
220 
221     /***
222      * Factory method to create a new XMLOutput to give to scripts as they run.
223      * Derived classes could overload this method, such as to pipe output to
224      * some log file etc.
225      */
226     protected XMLOutput createXMLOutput() {
227         // output will just be ignored
228         return new XMLOutput();
229     }
230 
231 }
232