KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > joseki > server > http > HttpOperationCodec


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

5
6 package org.joseki.server.http;
7
8 import java.util.* ;
9 import org.apache.commons.logging.* ;
10 import java.io.* ;
11
12 import javax.servlet.http.* ;
13 import org.joseki.* ;
14 import org.joseki.util.NullOutputStream;
15 import org.joseki.util.HttpContentType ;
16 import org.joseki.server.*;
17 import com.hp.hpl.jena.rdf.model.*;
18
19 /** Extracting operation data from HTTP servlet requests and formatting results for sending back.
20  *
21  * @author Andy Seaborne
22  * @version $Id: HttpOperationCodec.java,v 1.15 2004/04/30 14:13:13 andy_seaborne Exp $
23  */

24 public class HttpOperationCodec
25 {
26     static Log log = LogFactory.getLog(HttpOperationCodec.class.getName()) ;
27     static public final String ENC_UTF8 = "UTF-8" ;
28     
29     
30     public HttpOperationCodec() {}
31
32     public void setArgs(Request request, HttpServletRequest httpRequest) throws ExecutionException
33     {
34         // Must do own parameter parsing because standard Java code
35
// may read body thinking it is a form.
36
if ( request.getProcessor().argsNeeded() == 0)
37             return;
38
39         if (request.getProcessor().argsNeeded() > 1)
40         {
41             throw new ExecutionException(ExecutionError.rcOperationNotSupported,
42                     "Processor error: needs more args",
43                     "Processor needs "+request.getProcessor().argsNeeded()+" args - unsupported") ;
44         }
45
46
47         // ( operation.getProcessor().argsNeeded() == 1)
48
try{
49             int len = httpRequest.getContentLength();
50             if ( len == 0 )
51                 // No content
52
log.warn("No data supplied - Content-length: "+len) ;
53             
54             HttpContentType ct = new HttpContentType(httpRequest.getContentType(),
55                                                      "RDF/XML", ENC_UTF8) ;
56
57             // The reader uses encoding specified by the HTTP Header
58
BufferedReader r = httpRequest.getReader();
59
60             // NB Error 406 if content encodings are not possible.
61
// HttpServletResponse.SC_NOT_ACCEPTABLE
62
// Could do this work on request receipt.
63

64             if ( !ct.getCharset().equalsIgnoreCase("UTF-8") )
65                 log.warn("Request charset is "+ct.getCharset()) ;
66             
67             // The reader may not be ready() (i.e. the body not yet in the local
68
// I/O system). Should we do safe reads into a StringWriter/Reader
69
// as Jena readers may be a bit fragile on ready()??
70

71             try
72             {
73                 Model model = ModelFactory.createDefaultModel();
74                 
75                 model.read(r, "", Joseki.getReaderType(ct.getMediaType()));
76                 request.addArg(model);
77             }
78             catch (RDFException rdfEx)
79             {
80                 throw new ExecutionException(
81                     ExecutionError.rcArgumentUnreadable,
82                     "Argument error",
83                     rdfEx.getMessage());
84             }
85         }
86         catch (IOException ioEx)
87         {
88             throw new ExecutionException(
89                 ExecutionError.rcInternalError,
90                 "IOException",
91                 "IOExeception: " + ioEx);
92         }
93         catch (Exception ex)
94         {
95             log.warn("Exception: " + ex);
96             throw new ExecutionException(
97                 ExecutionError.rcArgumentUnreadable,
98                 "Argument error",
99                 null);
100         }
101         if ( request.getDataArgs().size() != request.getProcessor().argsNeeded() )
102             log.warn("Failed to get the argument(s)") ;
103     }
104
105     public void setParameters(Request request, HttpServletRequest httpRequest)
106     {
107         String s = httpRequest.getQueryString() ;
108         // Don't use ServletRequest.getParameter as that reads form data.
109
String[] params = s.split("&") ;
110         for ( int i = 0 ; i < params.length ; i++ )
111         {
112             String p = params[i] ;
113             String[] x = p.split("=",2) ;
114             if ( x.length == 0 )
115                 request.setParam(p,"" ) ;
116             else if ( x.length == 1 )
117                 request.setParam(x[0], "") ;
118             else
119                 request.setParam(x[0], x[1]) ;
120         }
121     }
122     
123     /** Send a response.
124      * @param resultModel
125      * @param request
126      * @param httpRequest
127      * @param httpResponse
128      * @return true for a successful send, false for any problem (ie.e HTTP repsonse if not 200)
129      * @throws IOException
130      */

131     
132     public boolean sendResponse(Model resultModel, Request request,
133                              HttpServletRequest httpRequest,
134                              HttpServletResponse httpResponse)
135         throws IOException
136     {
137         // Shouldn't be null - should be empty model
138
if (resultModel == null)
139         {
140             log.warn("Result is null pointer for result model") ;
141             sendPanic(request, httpRequest, httpResponse, null,
142                       "Server internal error: processor returned a null pointer, not a model") ;
143             return false;
144         }
145
146         String mimeType = Joseki.contentTypeRDFXML ;
147         
148         if ( Joseki.serverDebug )
149             mimeType = "text/plain" ;
150         
151         // Decide MIME type.
152
// Based on exact match - no */* stuff -
153
// so browsers (text/plain) and the Joseki library works (application/???)
154

155         // See also: Accept-Charset
156
// Currently, we ignore this and just do UTF-8.
157

158         Enumeration enum = httpRequest.getHeaders("Accept") ;
159         for ( ; enum.hasMoreElements() ; )
160         {
161             String s = (String)enum.nextElement() ;
162             String m = Joseki.getWriterType(s) ;
163             if ( m != null )
164             {
165                 mimeType = s ;
166                 break ;
167             }
168         }
169         
170         String acceptCharset = httpRequest.getHeader("Accept-Charset") ;
171         if ( acceptCharset != null )
172         {
173             if ( ! acceptCharset.equalsIgnoreCase(ENC_UTF8) )
174                 log.warn("Accept-Charset: "+acceptCharset) ;
175         }
176         
177         String writerType = Joseki.getWriterType(mimeType) ;
178
179         if ( writerType == null )
180         {
181             // No writer found. Default it ...
182
writerType = Joseki.getWriterType(Joseki.serverContentType) ;
183             //logger.warn("MIME type for response if null: force use of "+writerType) ;
184
}
185              
186         if (false)
187         {
188             FileOutputStream out = new FileOutputStream("response.n3");
189             resultModel.write(out, "N3");
190             out.close() ;
191         }
192         
193         if ( false )
194         {
195             log.info("Result model ("+writerType+")") ;
196             StringWriter sw = new StringWriter() ;
197             //resultModel.write(sw, writerType);
198
resultModel.write(sw, "N3");
199             log.info("\n"+sw.toString()) ;
200             
201         }
202
203         // Write result model.
204
// Need to do this robustly. The model may contain bad URIs
205
// which may cause the writer to crash part way though.
206
// To check this, we write to a null output sink first. If this
207
// works, we can create a HTTP response.
208

209         RDFWriter rdfw = resultModel.getWriter(writerType) ;
210         
211         if ( writerType.equals("RDF/XML-ABBREV") || writerType.equals("RDF/XML") )
212         {
213             rdfw.setProperty("showXmlDeclaration", "true") ;
214
215             if ( writerType.equals("RDF/XML-ABBREV") )
216                 // Workaround for the j.cook.up bug.
217
rdfw.setProperty("blockRules", "propertyAttr") ;
218         }
219         
220         // TODO: Allow a mode of write to buffer (memory, disk), write buffer later.
221
// Time/space tradeoff.
222
try {
223             OutputStream out = new NullOutputStream() ;
224             rdfw.write(resultModel, out, null) ;
225             out.flush() ;
226         } catch (Exception ex)
227         {
228             // Failed to write the model :-(
229
log.warn("Exception test writing model: "+ex.getMessage(), ex) ;
230             sendPanic(request, httpRequest, httpResponse, ex,
231                     "Server internal error: can't write the model.") ;
232             return false;
233         }
234         
235         // Managed to write it.
236

237         // Stop caching when debugging!
238
if ( Joseki.serverDebug )
239         {
240             httpResponse.setHeader("Cache-Control", "no-cache") ;
241             httpResponse.setHeader("Pragma", "no-cache") ;
242         }
243
244         httpResponse.setHeader(Joseki.httpHeaderField, Joseki.httpHeaderValue) ;
245
246         // See: http://www.w3.org/International/O-HTTP-charset.html
247
String contentType = mimeType+"; charset=UTF-8" ;
248         log.trace("Content-Type for response: "+contentType) ;
249         httpResponse.setContentType(contentType) ;
250         
251         log.trace("HTTP response 200") ;
252         rdfw.write(resultModel, httpResponse.getOutputStream(), null) ;
253         return true ;
254     }
255
256     
257
258     public void sendError(ExecutionException execEx, HttpServletResponse response)
259         throws IOException
260     {
261         int httpRC = -1;
262         String httpMsg = ExecutionError.errorString(execEx.returnCode);
263         if (execEx.shortMessage != null)
264             httpMsg = execEx.shortMessage;
265
266         // Map from internal error codes to HTTP ones.
267
switch (execEx.returnCode)
268         {
269             case ExecutionError.rcOK :
270                 httpRC = 200;
271                 break;
272             case ExecutionError.rcQueryParseFailure :
273                 httpRC = HttpServletResponse.SC_BAD_REQUEST;
274                 break;
275             case ExecutionError.rcQueryExecutionFailure :
276                 httpRC = HttpServletResponse.SC_BAD_REQUEST;
277                 break;
278             case ExecutionError.rcNoSuchQueryLanguage :
279                 httpRC = HttpServletResponse.SC_NOT_IMPLEMENTED ;
280                 break;
281             case ExecutionError.rcInternalError :
282                 httpRC = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
283                 break;
284             case ExecutionError.rcRDFException :
285                 httpRC = HttpServletResponse.SC_BAD_REQUEST ;
286                 break ;
287             case ExecutionError.rcNoSuchURI:
288                 httpRC = HttpServletResponse.SC_NOT_FOUND ;
289                 break ;
290             case ExecutionError.rcSecurityError:
291                 httpRC = HttpServletResponse.SC_FORBIDDEN ;
292                 break ;
293             case ExecutionError.rcOperationNotSupported:
294                 httpRC = HttpServletResponse.SC_NOT_IMPLEMENTED ;
295                 break ;
296             case ExecutionError.rcArgumentUnreadable:
297                 httpRC = HttpServletResponse.SC_BAD_REQUEST ;
298                 break ;
299             case ExecutionError.rcImmutableModel:
300                 httpRC = HttpServletResponse.SC_METHOD_NOT_ALLOWED ;
301                 break ;
302             case ExecutionError.rcConfigurationError:
303                 httpRC = HttpServletResponse.SC_INTERNAL_SERVER_ERROR ;
304                 break ;
305             case ExecutionError.rcArgumentError:
306                 httpRC = HttpServletResponse.SC_BAD_REQUEST ;
307             default :
308                 httpRC = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
309                 break;
310         }
311         response.setHeader(Joseki.httpHeaderField, Joseki.httpHeaderValue) ;
312         response.sendError(httpRC, httpMsg) ;
313     }
314     
315     
316     // Things are going very badly
317
private void sendPanic( Request request,
318                             HttpServletRequest httpRequest,
319                             HttpServletResponse httpResponse,
320                             Exception ex,
321                             String msg)
322         throws IOException
323     {
324         httpResponse.setContentType("text/plain");
325         httpResponse.setHeader(Joseki.httpHeaderField, Joseki.httpHeaderValue);
326         httpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
327
328         PrintWriter pw = httpResponse.getWriter();
329         pw.println(msg);
330         pw.println();
331         pw.println("URI = " + request.getModelURI());
332         for (Iterator iter = request.getParams().keySet().iterator(); iter.hasNext();)
333         {
334             String p = (String)iter.next();
335             pw.println(p + " = " + request.getParam(p));
336         }
337         pw.println() ;
338         if ( ex != null )
339             ex.printStackTrace(pw) ;
340         pw.flush();
341         return;
342     }
343 }
344
345
346 /*
347  * (c) Copyright 2003, 2004 Hewlett-Packard Development Company, LP
348  * All rights reserved.
349  *
350  * Redistribution and use in source and binary forms, with or without
351  * modification, are permitted provided that the following conditions
352  * are met:
353  * 1. Redistributions of source code must retain the above copyright
354  * notice, this list of conditions and the following disclaimer.
355  * 2. Redistributions in binary form must reproduce the above copyright
356  * notice, this list of conditions and the following disclaimer in the
357  * documentation and/or other materials provided with the distribution.
358  * 3. The name of the author may not be used to endorse or promote products
359  * derived from this software without specific prior written permission.
360  *
361  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
362  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
363  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
364  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
365  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
366  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
367  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
368  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
369  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
370  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
371  */

372
Popular Tags