Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/clra/web/MembershipServlet.java


1   /*
2    * Copyright (c) Carnegie Lake Rowing Association 2002. All rights reserved.
3    * Distributed under the GPL license. See doc/COPYING.
4    * $RCSfile: MembershipServlet.java,v $
5    * $Date: 2003/03/05 13:55:13 $
6    * $Revision: 1.2 $
7    */
8   
9   package com.clra.web;
10  
11  import com.clra.web.MemberView;
12  import java.io.IOException;
13  import java.io.InputStream;
14  import java.io.InputStreamReader;
15  import java.io.OutputStream;
16  import java.io.PrintWriter;
17  import java.io.StringBufferInputStream;
18  import java.io.StringWriter;
19  import java.io.Writer;
20  import java.net.MalformedURLException;
21  import java.net.Socket;
22  import java.net.SocketException;
23  import java.net.UnknownHostException;
24  import java.net.URL;
25  import javax.ejb.NoSuchEntityException;
26  import javax.servlet.ServletConfig;
27  import javax.servlet.ServletException;
28  import javax.servlet.http.HttpServlet;
29  import javax.servlet.http.HttpServletRequest;
30  import javax.servlet.http.HttpServletResponse;
31  import javax.xml.transform.Source;
32  import javax.xml.transform.Transformer;
33  import javax.xml.transform.TransformerConfigurationException;
34  import javax.xml.transform.TransformerException;
35  import javax.xml.transform.TransformerFactory;
36  import javax.xml.transform.stream.StreamSource;
37  import javax.xml.transform.stream.StreamResult;
38  import org.apache.axis.encoding.Base64;
39  import org.apache.log4j.Category;
40  import org.apache.log4j.helpers.Loader;
41  
42  /**
43   * Converts a soap response to CSV or beautified XML message.
44   * The only valid request parameters are:<ul>
45   * <li><code>format</code>.
46   * Specifies what action to perform.<br>
47   * The only valid values for this parameter are:<ul>
48   * <li><code>CSV</code><br>
49   * Converts a SOAP message to comma-separarted-value format.</li>
50   * <li><code>XML</code><br>
51   * Converts a SOAP message to a simplified XML format.</li></ul></p>
52   * <p>
53   * If the <code>format</code> parameter is omitted, the output format
54   * defaults to beautified XML.</p>
55   *
56   * @version $Revision: 1.2 $
57   */
58  public class MembershipServlet extends HttpServlet {
59  
60    private final static String base = MembershipServlet.class.getName();
61    private final static Category theLog = Category.getInstance( base );
62  
63    /** Socket timeout (milliseconds) */
64    private final static int timeoutMillis = 60000;
65  
66    /** Default buffer size */
67    private final static int BUFSIZE = 100*1024;
68  
69    /** The name of the "format" request parameter */
70    public final static String RPN_FORMAT = "format";
71  
72    /** The "CSV" value for the "format" request parameter */
73    public final static String RPV_FORMAT_CSV = "CSV";
74     
75    /** The "XML" value for the "format" request parameter */
76    public final static String RPV_FORMAT_XML = "XML";
77  
78    /** The file that holds the pre-built SOAP "findAll" request headers */
79    public final static String FILE_FINDALL_HEADERS =
80        "com/clra/web/findAll-request-headers.http";
81  
82    /** The file that holds the pre-built SOAP "findAll" request content */
83    public final static String FILE_FINDALL_CONTENT =
84        "com/clra/web/findAll-request-content.http";
85  
86    /** The file that holds the CVS stylesheet */
87    public final static String FILE_CSV_STYLESHEET =
88        "com/clra/web/csv.xsl";
89     
90    /** The file that holds the simplified-XML stylesheet */
91    public final static String FILE_XML_STYLESHEET =
92        "com/clra/web/memberXml.xsl";
93  
94    /** The pre-built "findAll" request headers */
95    private final static String requestHeaders =
96      loadStringFromFile( FILE_FINDALL_HEADERS );
97  
98    /** The pre-built "findAll" request message */
99    private final static String requestContent =
100     loadStringFromFile( FILE_FINDALL_CONTENT );
101 
102   /** The csv stylesheet */
103   private final static String csvStylesheet =
104     loadStringFromFile( FILE_CSV_STYLESHEET );
105 
106   /** The simplified-XML stylesheet */
107   private final static String xmlStylesheet =
108     loadStringFromFile( FILE_XML_STYLESHEET );
109 
110   private static String loadStringFromFile( final String strFile ) {
111 
112     String retVal = null;
113     try {
114       URL url = Loader.getResource( strFile, MembershipServlet.class );
115       retVal = loadStringFromInputStream( url.openStream() );
116       if ( theLog.isDebugEnabled() ) {
117         final int total = retVal.length();
118         theLog.debug( "read " + strFile + " bytes from " + strFile );
119         theLog.debug( "'" + strFile + "' contents == '" + retVal + "'" );
120       }
121     }
122     catch( Exception x ){
123       String msg = "unable to load request message from '" + strFile + "'";
124       theLog.fatal(msg,x);
125       throw new IllegalStateException( msg );
126     }
127 
128     return retVal;
129   } // loadStringFromFile(String)
130 
131   // Closes input stream after string is loaded
132   private static String loadStringFromInputStream( InputStream is )
133     throws IOException {
134     StringWriter out = new StringWriter( BUFSIZE );
135     writeFromInputStreamToWriter( is, out );
136     return out.toString();
137   }
138 
139   /** An implementation-specific exception that indicates a SOAP problem */
140   public static class MembershipSoapException extends Exception {
141     private MembershipSoapException( String msg ) { super(msg); }
142   };
143 
144   /** Initializes the servlet */
145   public void init(ServletConfig config) throws ServletException {
146     super.init(config);
147   }
148 
149   /** Destroys the servlet */
150   public void destroy() {
151   }
152 
153   private static Socket getFindAllResponseSocket(
154     String username, String password ) throws IOException,
155       MalformedURLException, SocketException, UnknownHostException,
156         NoSuchEntityException {
157 
158     username = username == null ? null : username.trim() ;
159     password = password == null ? null : password.trim() ;
160 
161     Socket retVal = null;
162 
163     // Create the request message
164     String requestMessage = null;
165     if ( username != null && password != null ) {
166       String authInfo = username + ":" + password;
167       String encoded = Base64.encode( authInfo.getBytes() );
168       StringBuffer sb = new StringBuffer();
169       sb.append( requestHeaders );
170       sb.append( "Authorization: Basic " );
171       sb.append( encoded );
172       sb.append( "\r\n\r\n" );
173       sb.append( requestContent );
174       requestMessage = new String(sb);
175     } else {
176       StringBuffer sb = new StringBuffer();
177       sb.append( requestHeaders );
178       sb.append( "\r\n" );
179       sb.append( requestContent );
180       requestMessage = new String(sb);
181     }
182 
183     // Send the SOAP request
184     final String strURL = Configuration.SOAP_SERVER_URL;
185     URL url = new URL( strURL );
186     retVal = new Socket( url.getHost(), url.getPort() );
187     retVal.setSoTimeout( timeoutMillis );
188     OutputStream os = retVal.getOutputStream();
189     final byte[] buf = requestMessage.getBytes();
190     final int total = buf.length;
191     os.write( buf, 0, total );
192     theLog.debug( "wrote " + total + " bytes to " + strURL );
193 
194     return retVal;
195   } // getFindAllResponseSocket()
196 
197   // Assumes buf != null && buf.length > 3
198   private static boolean foundCRNL_CRNL( char[] buf ) {
199     return ( buf[0] == '\r' )
200         && ( buf[1] == '\n' )
201         && ( buf[2] == '\r' )
202         && ( buf[3] == '\n' );
203   }
204 
205   private static void readUntilEndOfHttpHeaders( InputStream is )
206     throws IOException, MembershipSoapException {
207 
208     final int  EOS = -1;
209 
210     char[] buf = new char[4]; // initialized to all zero's
211 
212     int b = is.read();
213     while ( b != EOS ) {
214 
215       buf[0] = buf[1];
216       buf[1] = buf[2];
217       buf[2] = buf[3];
218       buf[3] = (char) b;
219       if ( foundCRNL_CRNL(buf) ) {
220         break;
221       }
222 
223       b = is.read();
224     } // while !EOS
225 
226     if ( b == EOS ) {
227       throw new MembershipSoapException( "did not find end of HTTP headers" );
228     }
229 
230     return;
231   } // readUntilEndOfHttpHeaders(InputStream)
232 
233   /**
234    * Processes requests for both HTTP <code>GET</code> and <code>POST</code>
235    * methods. The current implementation uses a pre-built SOAP message
236    * stored in the text file specified by <code>FINDALL-REQUEST</code>
237    * to retrieve a SOAP message, and then uses XSL stylesheets to
238    * to simplify the SOAP XML.
239    *
240    * @param request servlet request
241    * @param response servlet response
242    */
243   protected void processRequest(
244     HttpServletRequest request, HttpServletResponse response)
245       throws ServletException, java.io.IOException {
246 
247     // Determine how to format the response
248     String strFormat = request.getParameter( RPN_FORMAT );
249     if ( strFormat == null ) {
250       theLog.debug( "null format defaults to XML" );
251       strFormat = RPV_FORMAT_XML;
252     }
253 
254     // Set the response content-type and get the appropriate stylesheet
255     String xsl = null;
256     if ( RPV_FORMAT_CSV.equalsIgnoreCase(strFormat) ) {
257       theLog.debug( RPV_FORMAT_CSV );
258       response.setContentType( "text/plain" );
259       xsl = csvStylesheet;
260     }
261     else if (RPV_FORMAT_XML.equalsIgnoreCase(strFormat) ) {
262       theLog.debug( RPV_FORMAT_XML );
263       response.setContentType( "text/plain" );
264       xsl = xmlStylesheet;
265     }
266     else {
267       String msg = "ERROR: unexpected format == '" + strFormat + "'";
268       theLog.error( msg );
269       throw new ServletException( msg );
270     }
271 
272     try {
273       MemberView mv = MemberTag.getMemberFromAuthenticatedUser(request);
274       final String username = mv.getAccountName();
275       final String password = mv.getAccountPassword();
276 
277       PrintWriter out = response.getWriter();
278       writeResponse( username, password, xsl, out );
279     }
280     catch( Exception x ) {
281       theLog.error( x );
282       throw new ServletException( x );
283     }
284 
285     return;
286   } // processRequest(HttpServletRequest,HttpServletResponse)
287 
288   /** Delegates to the <code>processRequest</code> method */
289   protected void doGet(
290     HttpServletRequest request, HttpServletResponse response)
291       throws ServletException, java.io.IOException {
292     processRequest(request, response);
293   }
294 
295   /** Delegates to the <code>processRequest</code> method */
296   protected void doPost(
297     HttpServletRequest request, HttpServletResponse response)
298       throws ServletException, java.io.IOException {
299     processRequest(request, response);
300   }
301 
302   /**
303    * Returns a short description of the servlet.
304    */
305   public String getServletInfo() {
306     return "Reformats a SOAP message to CSV or simplified XML";
307   }
308 
309   /**
310    * Writes the entire SOAP response for <code>Member.findAllMembers</code>
311    * to the specified PrintWriter. Used for JUnit testing.
312    */
313   public static void
314   writeFindAllSoapXmlResponse(String username,String password,PrintWriter out)
315   throws Exception {
316     writeResponse(username,password,null,out);
317   } // getFindAllSoapXmlResponse()
318 
319   /**
320    * Writes the CSV-formatted response for <code>Member.findAllMembers</code>
321    * to the specified PrintWriter. Used for JUnit testing.
322    */
323   public static void
324   writeFindAllCsvResponse(String username,String password,PrintWriter out)
325   throws Exception {
326     writeResponse(username,password,csvStylesheet,out);
327   }
328 
329   /**
330    * Returns a simplified XML-formatted response for
331    * <code>Member.findAllMembers</code>. Used for JUnit testing.
332    */
333   public static void
334   writeFindAllSimplifiedXmlResponse(String name,String pass,PrintWriter out)
335   throws Exception {
336     writeResponse(name,pass,xmlStylesheet,out);
337   }
338 
339   // Does NOT close the Writer.
340   private static void
341   writeResponse(String username, String password, String xsl, PrintWriter out)
342   throws TransformerConfigurationException, TransformerException, IOException,
343   MembershipSoapException {
344 
345     // Precondition
346     if ( out == null ) {
347       throw new IllegalArgumentException( "null PrintWriter" );
348     }
349 
350     Socket s = null;
351     InputStream soapStream = null;
352 
353     try {
354 
355       s = getFindAllResponseSocket(username,password);
356       soapStream = s.getInputStream();
357 
358       if ( xsl == null ) {
359         writeFromInputStreamToWriter( soapStream, out );
360       }
361       else {
362         readUntilEndOfHttpHeaders( soapStream );
363         Source xmlSource = new StreamSource( soapStream );
364         Source xslSource =
365           new StreamSource( new StringBufferInputStream(xsl) );
366         TransformerFactory f = TransformerFactory.newInstance();
367         Transformer t = f.newTransformer( xslSource );
368         t.transform( xmlSource, new StreamResult(out) );
369       }
370       out.flush();
371 
372     }
373     finally {
374       if( s != null ) {
375         try { s.close(); } catch( Exception x ) {}
376       }
377     }
378 
379     return;
380   } // getFindAllFormattedResponse(String)
381 
382   // Closes input stream after string is loaded.
383   // Does NOT close the Writer.
384   private static void writeFromInputStreamToWriter(InputStream is, Writer out)
385   throws IOException {
386 
387     InputStreamReader isr = null;
388     try {
389       isr = new InputStreamReader( is );
390       char[] cbuf = new char[BUFSIZE];
391       int numRead = isr.read( cbuf, 0, cbuf.length );
392       while ( numRead > 0 ) {
393         out.write( cbuf, 0, numRead );
394         numRead = isr.read( cbuf, 0, cbuf.length );
395       }
396       out.flush();
397     }
398     finally {
399       if ( isr != null ) {
400         try { isr.close(); } catch( Exception x ) {}
401       }
402       if ( is != null ) {
403         try { is.close(); } catch( Exception x ) {}
404         is = null;
405       }
406     }
407 
408     return;
409   } // readFromInputStreamToOutputWriter(InputStream,Writer)
410 
411 } // MembershipServlet
412 
413 /*
414  * $Log: MembershipServlet.java,v $
415  * Revision 1.2  2003/03/05 13:55:13  rphall
416  * Added username/password to SOAP call
417  *
418  * Revision 1.1  2003/03/04 02:16:33  rphall
419  * Moved MembershipServlet and *.xsl files to com/clra/web
420  *
421  * Revision 1.1  2003/03/02 17:31:43  rphall
422  * Converts a soap response to CSV or beautified XML message
423  *
424  */
425