KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > hp > hpl > jena > joseki > HttpQuery


1 /*
2  * (c) Copyright 2003, 2004 Hewlett-Packard Development Company, LP
3  * [See end of file]
4  */

5
6 package com.hp.hpl.jena.joseki;
7
8 import org.joseki.*;
9 import org.joseki.util.Convert ;
10 import org.joseki.util.HttpContentType;
11 import org.joseki.vocabulary.*;
12
13 import java.net.* ;
14 import java.io.* ;
15 import java.util.* ;
16 import org.apache.commons.logging.* ;
17
18 import com.hp.hpl.jena.rdf.model.* ;
19 import com.hp.hpl.jena.shared.* ;
20
21 /** Create an execution object for performing a query on a model
22  * over HTTP. This is the main protocol engine for HTTP query.
23  * There are higher level classes for doing a query in a particular
24  * langauge, such as RDQL.
25  *
26  * Normally, HTTP GET? is used for the query. If the queryStringLang is null or "",
27  * do a plain GET on the model.
28  *
29  * If the query string is large, then HTTP POST?query is used.
30
31  * @author Andy Seaborne
32  * @version $Id: HttpQuery.java,v 1.13 2004/04/30 14:13:13 andy_seaborne Exp $
33  */

34 public class HttpQuery
35 {
36     static final Log log = LogFactory.getLog(HttpQuery.class.getName()) ;
37     
38     /** The definition of "large" queries */
39     // Not final so that other code can change it for testing.
40
static public /*final*/ int urlLimit = 2*1024 ;
41     
42     // PSetting for this query
43
String JavaDoc modelURI ;
44     String JavaDoc queryLang ;
45     Map params = new HashMap() ;
46     
47     String JavaDoc queryString = null ;
48     
49     // An object indicate no value associated with parameter name
50
final static Object JavaDoc noValue = new Object JavaDoc() ;
51     
52     int responseCode = 0;
53     String JavaDoc responseMessage = null ;
54     boolean forcePOST = false ;
55     
56     static final String JavaDoc ENC_UTF8 = "UTF-8" ;
57     
58     /** Create a execution object for a whole model GET
59      * @param urlString The model
60      */

61     
62     public HttpQuery(String JavaDoc urlString)
63     {
64         init(urlString, null) ;
65     }
66         
67
68     /** Create a execution object for a whole model GET
69      * @param url The model
70      */

71     
72     public HttpQuery(URL url)
73     {
74         init(url.toString(), null) ;
75     }
76         
77
78     /** Create a execution object for a query.
79      * @param url The model
80      * @param queryLang The query language
81      */

82     
83     public HttpQuery(URL url, String JavaDoc queryLang)
84     {
85         // Strangely, we unpack the URL because we munge it with a query string.
86
init(url.toString(), queryLang) ;
87     }
88     
89
90     /** Create a execution object for a query.
91      * @param urlString The model
92      * @param queryLang The query language
93      */

94     
95     public HttpQuery(String JavaDoc urlString, String JavaDoc queryLang)
96     {
97         init(urlString, queryLang) ;
98     }
99     
100     private void init(String JavaDoc urlStr, String JavaDoc qLang)
101     {
102         if ( log.isTraceEnabled())
103             log.trace("URL: "+urlStr+" Lang="+qLang) ;
104
105         if ( urlStr.indexOf('?') >= 0 )
106             throw new HttpException(-1, "URL already has a query string ("+urlStr+")") ;
107         
108         modelURI = urlStr ;
109         queryLang = qLang ;
110         queryString = null ;
111     }
112
113     
114     private void makeQueryString()
115     {
116         if (queryString != null)
117             // Cached
118
return;
119             
120         // If lang is null, then we should be doing a plain GET
121
if (queryLang != null)
122             queryString = "?lang=" + Convert.encWWWForm(queryLang);
123
124         // but for generality we'll still encode the string correctly
125
boolean first = (queryString == null);
126         for (Iterator iter = params.keySet().iterator(); iter.hasNext();)
127         {
128             String JavaDoc name = (String JavaDoc) iter.next();
129             Object JavaDoc obj = params.get(name);
130             String JavaDoc tmp = name ;
131             if ( obj != noValue )
132                 tmp = name + "=" + Convert.encWWWForm((String JavaDoc)obj);
133
134             if (first)
135                 queryString = "?" + tmp;
136             else
137                 queryString = queryString + "&" + tmp;
138             first = false ;
139         }
140         
141         if ( queryString == null )
142             queryString = "" ;
143     }
144
145
146     /** Set a parameter to the operation
147      * @param name Name of the parameter
148      * @param value Value - May be null to indicate none - the name still goes.
149      */

150
151     public void setParam(String JavaDoc name, String JavaDoc value)
152     {
153         if ( value == null )
154             params.put(name, noValue) ;
155         else
156             params.put(name, value) ;
157         // Clear cached calculation
158
queryString = null ;
159     }
160
161     /** Add a parameter to the operation
162      * @param name Name of the parameter
163      * @param value Value - May be null to indicate none - the name still goes.
164      */

165
166     public void addParam(String JavaDoc name, String JavaDoc value)
167     {
168         setParam(name, value) ;
169     }
170
171     /** Add a parameter to operation (no value associated with the name)
172      * @param name Name of the parameter
173      */

174
175     public void addParam(String JavaDoc name) { addParam(name, null) ; }
176
177 // /** Return the that will, or has been, used.
178
// * @return URL
179
// */
180
// public URL getQueryOperationURL() { return modelURI ; }
181

182     
183     /** Return whether this request will go by GET or POST
184      * @return boolean
185      */

186     public boolean usesPOST()
187     {
188         if ( forcePOST )
189             return true ;
190         makeQueryString() ;
191         
192         return queryString != null && +modelURI.length()+queryString.length() >= urlLimit ;
193     }
194
195     /** Force the use of HTTP POST for the query operation
196      */

197
198     public void setForcePOST()
199     {
200         forcePOST = true ;
201     }
202
203     /** Return the results from the last execution */
204     public int getResponseCode() { return responseCode ; }
205     
206     /** Return the results from the last execution */
207     public String JavaDoc getResponseMessage() { return responseMessage ; }
208     
209
210     /** Execute the operation
211      * @return Model The resulting model
212      * @throws HttpException
213      */

214     public Model exec() throws HttpException
215     {
216         makeQueryString() ;
217
218         try {
219             if (usesPOST())
220                 return execPost();
221             else
222                 return execGet();
223         } catch (HttpException httpEx)
224         {
225             log.trace("Exception in exec", httpEx);
226             throw httpEx;
227         }
228         catch (JenaException jEx)
229         {
230             log.trace("JenaException in exec", jEx);
231             throw jEx ;
232         }
233     }
234      
235
236     private Model execGet() throws HttpException
237     {
238         URL target = null ;
239         try {
240             if ( queryString.equals(""))
241             {
242                 //assert queryLang == null ;
243
target = new URL(modelURI) ;
244             }
245             else
246                 target = new URL(modelURI+queryString) ;
247         }
248         catch (MalformedURLException malEx)
249         { throw new HttpException(0, "Malformed URL: "+malEx) ; }
250         log.trace("GET "+target.toExternalForm()) ;
251         
252         try
253         {
254             HttpURLConnection conn = (HttpURLConnection) target.openConnection();
255             conn.setRequestProperty("Accept", Joseki.contentTypeRDFXML+", "+
256                                               Joseki.contentTypeN3) ;
257             // By default, following 3xx redirects is true
258
//conn.setFollowRedirects(true) ;
259

260             conn.setDoInput(true);
261             conn.connect();
262             try
263             {
264                 return execCommon(conn);
265             }
266             catch (HttpException qEx)
267             {
268                 // Back-off and try POST if something complain about long URIs
269
// Broken
270
if (qEx.getResponseCode() == 414 /*HttpServletResponse.SC_REQUEST_URI_TOO_LONG*/ )
271                     return execPost();
272                 throw qEx;
273             }
274         }
275         catch (java.net.ConnectException JavaDoc connEx)
276         {
277             throw new HttpException(HttpException.NoServer, "Failed to connect to remote server");
278         }
279
280         catch (IOException ioEx)
281         {
282             throw new HttpException(ioEx);
283         }
284     }
285     
286     // Better (now) - turn into an HttpExec and use that engine
287

288     private Model execPost() throws HttpException
289     {
290         URL target = null;
291         try { target = new URL(modelURI + "?op=query"); }
292         catch (MalformedURLException malEx)
293         { throw new HttpException(0, "Malformed URL: " + malEx); }
294         log.trace("POST "+target.toExternalForm()) ;
295         
296         try
297         {
298             HttpURLConnection conn = (HttpURLConnection) target.openConnection();
299             conn.setRequestProperty("Accept", Joseki.contentTypeRDFXML+", "+
300                                               Joseki.contentTypeN3) ;
301             conn.setRequestProperty("Accept-Charset", ENC_UTF8) ;
302             conn.setRequestProperty("Content-Type", //Joseki.clientContentType) ;
303
Joseki.clientContentType+ "; charset="+ENC_UTF8) ;
304             conn.setDoOutput(true) ;
305             conn.setDoInput(true) ;
306             
307             //Writer w = new OutputStreamWriter(conn.getOutputStream(), encodingUTF8) ;
308

309             Model model = ModelFactory.createDefaultModel() ;
310             Resource r = model.createResource() ;
311             
312             // Old name - a bug.
313
// r.addProperty(JosekiVocab.queryOperationName, queryLang) ;
314
r.addProperty(JosekiVocab.requestQueryLanguage, queryLang) ;
315
316             for ( Iterator iter = params.keySet().iterator() ; iter.hasNext() ; )
317             {
318                 String JavaDoc name = (String JavaDoc)iter.next() ;
319                 if ( name.equals("query"))
320                     r.addProperty(JosekiVocab.queryScript, (String JavaDoc)params.get("query")) ;
321                 else
322                     log.warn("execPost: Skipping parameter: "+name) ;
323                 log.trace("Post: "+name+" = "+(String JavaDoc)params.get(name)) ;
324             }
325                  
326             String JavaDoc rdfSyntax = Joseki.getWriterType(Joseki.clientContentType) ;
327             RDFWriter rdfw = model.getWriter() ;
328             if ( rdfSyntax.startsWith("RDF/XML") )
329                 rdfw.setProperty("showXmlDeclaration", "true") ;
330             rdfw.write(model, conn.getOutputStream(), null) ;
331
332             conn.getOutputStream().flush() ;
333             conn.connect() ;
334             return execCommon(conn) ;
335         }
336         catch (RDFException rdfEx)
337         {
338             throw new HttpException(-1, "Failed to create RDF request");
339         }
340         catch (java.net.ConnectException JavaDoc connEx)
341         {
342             throw new HttpException(-1, "Failed to connect to remote server");
343         }
344
345         catch (IOException ioEx)
346         {
347             throw new HttpException(ioEx);
348         }
349     }
350     
351     private Model execCommon(HttpURLConnection conn) throws HttpException
352     {
353         try {
354             responseCode = conn.getResponseCode() ;
355             responseMessage = conn.getResponseMessage() ;
356             
357             // 1xx: Informational
358
// 2xx: Success
359
// 3xx: Redirection
360
// 4xx: Client Error
361
// 5xx: Server Error
362

363             if ( 300 <= responseCode && responseCode < 400 )
364             {
365                 throw new HttpException(responseCode, responseMessage) ;
366             }
367             
368             // Other 400 and 500 - errors
369

370             if ( responseCode >= 400 )
371             {
372                 throw new HttpException(responseCode, responseMessage) ;
373             }
374   
375             // Request suceeded
376
// Result coming back is a model
377
InputStream in = conn.getInputStream() ;
378             
379             // Get the content-type and character encoding of the response.
380
// RDF systems are required to support UTF-8
381

382             HttpContentType ct = new HttpContentType(conn.getContentType(), Joseki.contentTypeRDFXML, ENC_UTF8) ;
383             
384             if ( ! ct.getCharset().equalsIgnoreCase(ENC_UTF8) )
385                 log.warn("Charset is not UTF-8 : danger of mismatch with XML body") ;
386             
387             Reader r = null ;
388             try { r = new InputStreamReader(in, ct.getCharset()) ; }
389             catch ( UnsupportedEncodingException ex)
390             {
391                 log.warn("Unsupported encoding '"+ct.getCharset()+"' : trying with UTF-8") ;
392                 r = new InputStreamReader(in, ENC_UTF8) ;
393             }
394             
395             Model resultModel = ModelFactory.createDefaultModel() ;
396                 
397             resultModel.read(r, "http://somewhere/", Joseki.getReaderType(ct.getMediaType())) ;
398             return resultModel ;
399         }
400         catch (IOException ioEx)
401         {
402             throw new HttpException(ioEx) ;
403         }
404         catch (RDFException rdfEx)
405         {
406             throw new HttpException(rdfEx) ;
407         }
408     }
409     
410     public String JavaDoc toString()
411     {
412         makeQueryString() ;
413
414         if ( queryString.equals(""))
415             return modelURI ;
416         else
417             return modelURI+queryString ;
418     }
419 }
420
421 /*
422  * (c) Copyright 2003, 2004 Hewlett-Packard Development Company, LP
423  * All rights reserved.
424  *
425  * Redistribution and use in source and binary forms, with or without
426  * modification, are permitted provided that the following conditions
427  * are met:
428  * 1. Redistributions of source code must retain the above copyright
429  * notice, this list of conditions and the following disclaimer.
430  * 2. Redistributions in binary form must reproduce the above copyright
431  * notice, this list of conditions and the following disclaimer in the
432  * documentation and/or other materials provided with the distribution.
433  * 3. The name of the author may not be used to endorse or promote products
434  * derived from this software without specific prior written permission.
435  *
436  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
437  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
438  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
439  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
440  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
441  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
442  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
443  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
444  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
445  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
446  */

447
Popular Tags