KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > generation > HttpProxyGenerator


1 /*
2  * Copyright 1999-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 package org.apache.cocoon.generation;
17
18 import java.io.IOException JavaDoc;
19 import java.io.InputStream JavaDoc;
20 import java.util.ArrayList JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.Map JavaDoc;
23
24 import org.apache.avalon.framework.configuration.Configurable;
25 import org.apache.avalon.framework.configuration.Configuration;
26 import org.apache.avalon.framework.configuration.ConfigurationException;
27 import org.apache.avalon.framework.parameters.Parameters;
28 import org.apache.avalon.framework.service.ServiceException;
29
30 import org.apache.cocoon.ProcessingException;
31 import org.apache.cocoon.ResourceNotFoundException;
32 import org.apache.cocoon.environment.SourceResolver;
33
34 import org.apache.commons.httpclient.HttpConnection;
35 import org.apache.commons.httpclient.HttpMethodBase;
36 import org.apache.commons.httpclient.HttpState;
37 import org.apache.commons.httpclient.HttpURL;
38 import org.apache.commons.httpclient.HostConfiguration;
39 import org.apache.commons.httpclient.NameValuePair;
40 import org.apache.commons.httpclient.URIException;
41 import org.apache.commons.httpclient.methods.GetMethod;
42 import org.apache.commons.httpclient.methods.PostMethod;
43
44 import org.apache.excalibur.xml.sax.SAXParser;
45
46 import org.xml.sax.InputSource JavaDoc;
47 import org.xml.sax.SAXException JavaDoc;
48 import org.xml.sax.helpers.AttributesImpl JavaDoc;
49
50 /**
51  * The <code>HttpProxyGenerator</code> is a Cocoon generator using the
52  * <b>Jakarta Commons HTTPClient Library</b> to access an XML stream
53  * over HTTP.
54  *
55  * @author <a HREF="mailto:ivelin@apache.org">Ivelin Ivanov</a>, June 2002
56  * @author <a HREF="mailto:tony@apache.org">Tony Collen</a>, December 2002
57  * @author <a HREF="mailto:pier@apache.org">Pier Fumagalli</a>, February 2003
58  * @version CVS $Id: HttpProxyGenerator.java 164808 2005-04-26 16:07:03Z vgritsenko $
59  */

60 public class HttpProxyGenerator extends ServiceableGenerator implements Configurable {
61
62     /** The HTTP method to use at request time. */
63     private HttpMethodBase method = null;
64     /** The base HTTP URL for requests. */
65     private HttpURL url = null;
66     /** The list of request parameters for the request */
67     private ArrayList JavaDoc reqParams = null;
68     /** The list of query parameters for the request */
69     private ArrayList JavaDoc qryParams = null;
70     /** Wether we want a debug output or not */
71     private boolean debug = false;
72
73     /**
74      * Default (empty) constructor.
75      */

76     public HttpProxyGenerator() {
77         super();
78     }
79
80     /**
81      * Set up this <code>Generator</code> instance from its sitemap <code>Configuration</code>
82      *
83      * @param configuration The base <code>Configuration</code> for this <code>Generator</code>.
84      * @throws ConfigurationException If this instance cannot be configured properly.
85      * @see #recycle()
86      */

87     public void configure(Configuration configuration)
88     throws ConfigurationException {
89
90         /* Setup the HTTP method to use. */
91         String JavaDoc method = configuration.getChild("method").getValue("GET");
92         if ("GET".equalsIgnoreCase(method)) {
93             this.method = new GetMethod();
94         } else if ("POST".equalsIgnoreCase(method)) {
95             this.method = new PostMethod();
96             /* TODO: Is this still needed? Does it refer to a bug in bugzilla?
97              * At least the handling in httpclient has completely changed.
98              * Work around a bug from the HttpClient library */

99             ((PostMethod) this.method).setRequestBody("");
100         } else {
101             throw new ConfigurationException("Invalid method \"" + method + "\" specified"
102                     + " at " + configuration.getChild("method").getLocation());
103         }
104
105         /* Create the base URL */
106         String JavaDoc url = configuration.getChild("url").getValue(null);
107         try {
108             if (url != null) this.url = new HttpURL(url);
109         } catch (URIException e) {
110             throw new ConfigurationException("Cannot process URL \"" + url + "\" specified"
111                     + " at " + configuration.getChild("url").getLocation());
112         }
113
114         /* Prepare the base request and query parameters */
115         this.reqParams = this.getParams(configuration.getChildren("param"));
116         this.qryParams = this.getParams(configuration.getChildren("query"));
117     }
118
119     /**
120      * Setup this <code>Generator</code> with its runtime configurations and parameters
121      * specified in the sitemap, and prepare it for generation.
122      *
123      * @param sourceResolver The <code>SourceResolver</code> instance resolving sources by
124      * system identifiers.
125      * @param objectModel The Cocoon "object model" <code>Map</code>
126      * @param parameters The runtime <code>Parameters</code> instance.
127      * @throws ProcessingException If this instance could not be setup.
128      * @throws SAXException If a SAX error occurred during setup.
129      * @throws IOException If an I/O error occurred during setup.
130      * @see #recycle()
131      */

132     public void setup(SourceResolver sourceResolver, Map JavaDoc objectModel,
133             String JavaDoc source, Parameters parameters)
134     throws ProcessingException, SAXException JavaDoc, IOException JavaDoc {
135         /* Do the usual stuff */
136         super.setup(sourceResolver, objectModel, source, parameters);
137
138         /*
139          * Parameter handling: In case the method is a POST method, query
140          * parameters and request parameters will be two different arrays
141          * (one for the body, one for the query string, otherwise it's going
142          * to be the same one, as all parameters are passed on the query string
143          */

144         ArrayList JavaDoc req = new ArrayList JavaDoc();
145         ArrayList JavaDoc qry = req;
146         if (this.method instanceof PostMethod) qry = new ArrayList JavaDoc();
147         req.addAll(this.reqParams);
148         qry.addAll(this.qryParams);
149
150         /*
151          * Parameter handling: complete or override the configured parameters with
152          * those specified in the pipeline.
153          */

154         String JavaDoc names[] = parameters.getNames();
155         for (int x = 0; x < names.length; x++) {
156             String JavaDoc name = names[x];
157             String JavaDoc value = parameters.getParameter(name, null);
158             if (value == null) continue;
159
160             if (name.startsWith("query:")) {
161                 name = name.substring("query:".length());
162                 qry.add(new NameValuePair(name, value));
163             } else if (name.startsWith("param:")) {
164                 name = name.substring("param:".length());
165                 req.add(new NameValuePair(name, value));
166             } else if (name.startsWith("query-override:")) {
167                 name = name.substring("query-override:".length());
168                 qry = overrideParams(qry, name, value);
169             } else if (name.startsWith("param-override:")) {
170                 name = name.substring("param-override:".length());
171                 req = overrideParams(req, name, value);
172             }
173         }
174
175         /* Process the current source URL in relation to the configured one */
176         HttpURL src = (super.source == null ? null : new HttpURL(super.source));
177         if (this.url != null) src = (src == null ? this.url : new HttpURL(this.url, src));
178         if (src == null) throw new ProcessingException("No URL specified");
179         if (src.isRelativeURI()) {
180             throw new ProcessingException("Invalid URL \"" + src.toString() + "\"");
181         }
182
183         /* Configure the method with the resolved URL */
184         HostConfiguration hc = new HostConfiguration();
185         hc.setHost(src);
186         this.method.setHostConfiguration(hc);
187         this.method.setPath(src.getPath());
188         this.method.setQueryString(src.getQuery());
189
190         /* And now process the query string (from the parameters above) */
191         if (qry.size() > 0) {
192             String JavaDoc qs = this.method.getQueryString();
193             NameValuePair nvpa[] = new NameValuePair[qry.size()];
194             this.method.setQueryString((NameValuePair []) qry.toArray(nvpa));
195             if (qs != null) {
196                 this.method.setQueryString(qs + "&" + this.method.getQueryString());
197             }
198         }
199
200         /* Finally process the body parameters */
201         if ((this.method instanceof PostMethod) && (req.size() > 0)) {
202             PostMethod post = (PostMethod) this.method;
203             NameValuePair nvpa[] = new NameValuePair[req.size()];
204             post.setRequestBody((NameValuePair []) req.toArray(nvpa));
205         }
206
207         /* Check the debugging flag */
208         this.debug = parameters.getParameterAsBoolean("debug", false);
209     }
210
211     /**
212      * Recycle this instance, clearing all done during setup and generation, and reverting
213      * back to what was configured in the sitemap.
214      *
215      * @see #configure(Configuration)
216      * @see #setup(SourceResolver, Map, String, Parameters)
217      * @see #generate()
218      */

219     public void recycle() {
220         /* Recycle the method */
221         this.method.recycle();
222         /* TODO: Is this still needed? Does it refer to a bug in bugzilla?
223          * At least the handling in httpclient has completely changed.
224          * Work around a bug from the HttpClient library */

225         if (this.method instanceof PostMethod) ((PostMethod) this.method).setRequestBody("");
226
227         /* Clean up our parent */
228         super.recycle();
229     }
230
231     /**
232      * Parse the remote <code>InputStream</code> accessed over HTTP.
233      *
234      * @throws ResourceNotFoundException If the remote HTTP resource could not be found.
235      * @throws ProcessingException If an error occurred processing generation.
236      * @throws SAXException If an error occurred parsing or processing XML in the pipeline.
237      * @throws IOException If an I/O error occurred accessing the HTTP server.
238      */

239     public void generate()
240     throws ResourceNotFoundException, ProcessingException, SAXException JavaDoc, IOException JavaDoc {
241         /* Do the boring stuff in case we have to do a debug output (blablabla) */
242         if (this.debug) {
243             this.generateDebugOutput();
244             return;
245         }
246
247         /* Call up the remote HTTP server */
248         HttpConnection connection = new HttpConnection(this.method.getHostConfiguration());
249         HttpState state = new HttpState();
250         this.method.setFollowRedirects(true);
251         int status = this.method.execute(state, connection);
252         if (status == 404) {
253             throw new ResourceNotFoundException("Unable to access \"" + this.method.getURI()
254                     + "\" (HTTP 404 Error)");
255         } else if ((status < 200) || (status > 299)) {
256             throw new IOException JavaDoc("Unable to access HTTP resource at \""
257                     + this.method.getURI().toString() + "\" (status=" + status + ")");
258         }
259         InputStream JavaDoc response = this.method.getResponseBodyAsStream();
260
261         /* Let's try to set up our InputSource from the response output stream and to parse it */
262         SAXParser parser = null;
263         try {
264             InputSource JavaDoc inputSource = new InputSource JavaDoc(response);
265             parser = (SAXParser) this.manager.lookup(SAXParser.ROLE);
266             parser.parse(inputSource, super.xmlConsumer);
267         } catch (ServiceException ex) {
268             throw new ProcessingException("Unable to get parser", ex);
269         } finally {
270             this.manager.release(parser);
271             this.method.releaseConnection();
272             connection.close();
273         }
274     }
275
276     /**
277      * Generate debugging output as XML data from the current configuration.
278      *
279      * @throws SAXException If an error occurred parsing or processing XML in the pipeline.
280      * @throws IOException If an I/O error occurred accessing the HTTP server.
281      */

282     private void generateDebugOutput()
283     throws SAXException JavaDoc, IOException JavaDoc {
284         super.xmlConsumer.startDocument();
285
286         AttributesImpl JavaDoc attributes = new AttributesImpl JavaDoc();
287         attributes.addAttribute("", "method", "method", "CDATA", this.method.getName());
288         attributes.addAttribute("", "url", "url", "CDATA", this.method.getURI().toString());
289         attributes.addAttribute("", "protocol", "protocol", "CDATA",
290                 (this.method.isHttp11() ? "HTTP/1.1" : "HTTP/1.0"));
291         super.xmlConsumer.startElement("", "request", "request", attributes);
292
293         if (this.method instanceof PostMethod) {
294             String JavaDoc body = ((PostMethod) this.method).getRequestBodyAsString();
295
296             attributes.clear();
297             attributes.addAttribute("", "name", "name", "CDATA", "Content-Type");
298             attributes.addAttribute("", "value", "value", "CDATA", "application/x-www-form-urlencoded");
299             super.xmlConsumer.startElement("", "header", "header", attributes);
300             super.xmlConsumer.endElement("", "header", "header");
301
302             attributes.clear();
303             attributes.addAttribute("", "name", "name", "CDATA", "Content-Length");
304             attributes.addAttribute("", "value", "value", "CDATA", Integer.toString(body.length()));
305             super.xmlConsumer.startElement("", "header", "header", attributes);
306             super.xmlConsumer.endElement("", "header", "header");
307
308             attributes.clear();
309             super.xmlConsumer.startElement("", "body", "body", attributes);
310             super.xmlConsumer.characters(body.toCharArray(), 0, body.length());
311             super.xmlConsumer.endElement("", "body", "body");
312         }
313
314         super.xmlConsumer.endElement("", "request", "request");
315
316         super.xmlConsumer.endDocument();
317         return;
318     }
319
320     /**
321      * Prepare a map of parameters from an array of <code>Configuration</code>
322      * items.
323      *
324      * @param configurations An array of <code>Configuration</code> elements.
325      * @return A <code>List</code> of <code>NameValuePair</code> elements.
326      * @throws ConfigurationException If a parameter doesn't specify a name.
327      */

328     private ArrayList JavaDoc getParams(Configuration configurations[])
329     throws ConfigurationException {
330         ArrayList JavaDoc list = new ArrayList JavaDoc();
331
332         if (configurations.length < 1) return (list);
333
334         for (int x = 0; x < configurations.length; x++) {
335             Configuration configuration = configurations[x];
336             String JavaDoc name = configuration.getAttribute("name", null);
337             if (name == null) {
338                 throw new ConfigurationException("No name specified for parameter at "
339                         + configuration.getLocation());
340             }
341
342             String JavaDoc value = configuration.getAttribute("value", null);
343             if (value != null) list.add(new NameValuePair(name, value));
344
345             Configuration subconfigurations[] = configuration.getChildren("value");
346             for (int y = 0; y < subconfigurations.length; y++) {
347                 value = subconfigurations[y].getValue(null);
348                 if (value != null) list.add(new NameValuePair(name, value));
349             }
350         }
351
352         return (list);
353     }
354
355     /**
356      * Override the value for a named parameter in a specfied <code>ArrayList</code>
357      * or add it if the parameter was not found.
358      *
359      * @param list The <code>ArrayList</code> where the parameter is stored.
360      * @param name The parameter name.
361      * @param value The new parameter value.
362      * @return The same <code>List</code> of <code>NameValuePair</code> elements.
363      */

364     private ArrayList JavaDoc overrideParams(ArrayList JavaDoc list, String JavaDoc name, String JavaDoc value) {
365         Iterator JavaDoc iterator = list.iterator();
366         while (iterator.hasNext()) {
367             NameValuePair param = (NameValuePair) iterator.next();
368             if (param.getName().equals(name)) {
369                 iterator.remove();
370                 break;
371             }
372         }
373         list.add(new NameValuePair(name, value));
374         return (list);
375     }
376 }
377
378
Popular Tags