Source for gnu.xml.pipeline.CallFilter

   1: /* CallFilter.java --
   2:    Copyright (C) 1999,2000,2001 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: package gnu.xml.pipeline;
  39: 
  40: import java.io.IOException;
  41: import java.io.OutputStreamWriter;
  42: import java.net.URL;
  43: import java.net.URLConnection;
  44: import java.io.Writer;
  45: 
  46: import org.xml.sax.DTDHandler;
  47: import org.xml.sax.ErrorHandler;
  48: import org.xml.sax.InputSource;
  49: import org.xml.sax.SAXException;
  50: import org.xml.sax.SAXNotRecognizedException;
  51: import org.xml.sax.XMLReader;
  52: import org.xml.sax.helpers.XMLReaderFactory;
  53: 
  54: import gnu.xml.util.Resolver;
  55: import gnu.xml.util.XMLWriter;
  56: 
  57: 
  58: /**
  59:  * Input is sent as an XML request to given URI, and the output of this
  60:  * filter is the parsed response to that request.
  61:  * A connection is opened to the remote URI when the startDocument call is
  62:  * issued through this filter, and the request is finished when the
  63:  * endDocument call is issued.  Events should be written quickly enough to
  64:  * prevent the remote HTTP server from aborting the connection due to
  65:  * inactivity; you may want to buffer text in an earlier pipeline stage.
  66:  * If your application requires validity checking of such
  67:  * outputs, have the output pipeline include a validation stage.
  68:  *
  69:  * <p>In effect, this makes a remote procedure call to the URI, with the
  70:  * request and response document syntax as chosen by the application.
  71:  * <em>Note that all the input events must be seen, and sent to the URI,
  72:  * before the first output event can be seen. </em>  Clients are delayed
  73:  * at least by waiting for the server to respond, constraining concurrency.
  74:  * Services can thus be used to synchronize concurrent activities, and
  75:  * even to prioritize service among different clients.
  76:  *
  77:  * <p> You are advised to avoid restricting yourself to an "RPC" model
  78:  * for distributed computation.  With a World Wide Web, network latencies
  79:  * and failures (e.g. non-availability)
  80:  * are significant; adopting a "procedure" model, rather than a workflow
  81:  * model where bulk requests are sent and worked on asynchronously, is not
  82:  * generally an optimal system-wide architecture.  When the messages may
  83:  * need authentication, such as with an OpenPGP signature, or when server
  84:  * loads don't argue in favor of immediate responses, non-RPC models can
  85:  * be advantageous.  (So-called "peer to peer" computing models are one
  86:  * additional type of model, though too often that term is applied to
  87:  * systems that still have a centralized control structure.)
  88:  *
  89:  * <p> <em>Be strict in what you send, liberal in what you accept,</em> as
  90:  * the Internet tradition goes.  Strictly conformant data should never cause
  91:  * problems to its receiver; make your request pipeline be very strict, and
  92:  * don't compromise on that.  Make your response pipeline strict as well,
  93:  * but be ready to tolerate specific mild, temporary, and well-documented
  94:  * variations from specific communications peers.
  95:  *
  96:  * @see XmlServlet
  97:  *
  98:  * @author David Brownell
  99:  */
 100: final public class CallFilter implements EventConsumer
 101: {
 102:     private Requestor                   req;
 103:     private EventConsumer               next;
 104:     private URL                         target;
 105:     private URLConnection               conn;
 106:     private ErrorHandler                errHandler;
 107: 
 108: 
 109:     /**
 110:      * Initializes a call filter so that its inputs are sent to the
 111:      * specified URI, and its outputs are sent to the next consumer
 112:      * provided.
 113:      *
 114:      * @exception IOException if the URI isn't accepted as a URL
 115:      */
 116:         // constructor used by PipelineFactory
 117:     public CallFilter (String uri, EventConsumer next)
 118:     throws IOException
 119:     {
 120:         this.next = next;
 121:         req = new Requestor ();
 122:         setCallTarget (uri);
 123:     }
 124: 
 125:     /**
 126:      * Assigns the URI of the call target to be used.
 127:      * Does not affect calls currently being made.
 128:      */
 129:     final public void setCallTarget (String uri)
 130:     throws IOException
 131:     {
 132:         target = new URL (uri);
 133:     }
 134: 
 135:     /**
 136:      * Assigns the error handler to be used to present most fatal
 137:      * errors.
 138:      */
 139:     public void setErrorHandler (ErrorHandler handler)
 140:     {
 141:         req.setErrorHandler (handler);
 142:     }
 143: 
 144: 
 145:     /**
 146:      * Returns the call target's URI.
 147:      */
 148:     final public String getCallTarget ()
 149:     {
 150:         return target.toString ();
 151:     }
 152: 
 153:     /** Returns the content handler currently in use. */
 154:     final public org.xml.sax.ContentHandler getContentHandler ()
 155:     {
 156:         return req;
 157:     }
 158: 
 159:     /** Returns the DTD handler currently in use. */
 160:     final public DTDHandler getDTDHandler ()
 161:     {
 162:         return req;
 163:     }
 164: 
 165: 
 166:     /**
 167:      * Returns the declaration or lexical handler currently in
 168:      * use, or throws an exception for other properties.
 169:      */
 170:     final public Object getProperty (String id)
 171:     throws SAXNotRecognizedException
 172:     {
 173:         if (EventFilter.DECL_HANDLER.equals (id))
 174:             return req;
 175:         if (EventFilter.LEXICAL_HANDLER.equals (id))
 176:             return req;
 177:         throw new SAXNotRecognizedException (id);
 178:     }
 179: 
 180: 
 181:     // JDK 1.1 seems to need it to be done this way, sigh
 182:     ErrorHandler getErrorHandler () { return errHandler; }
 183: 
 184:     //
 185:     // Takes input and echoes to server as POST input.
 186:     // Then sends the POST reply to the next pipeline element.
 187:     //
 188:     final class Requestor extends XMLWriter
 189:     {
 190:         Requestor ()
 191:         {
 192:             super ((Writer)null);
 193:         }
 194: 
 195:         public synchronized void startDocument () throws SAXException
 196:         {
 197:             // Connect to remote object and set up to send it XML text
 198:             try {
 199:                 if (conn != null)
 200:                     throw new IllegalStateException ("call is being made");
 201: 
 202:                 conn = target.openConnection ();
 203:                 conn.setDoOutput (true);
 204:                 conn.setRequestProperty ("Content-Type",
 205:                             "application/xml;charset=UTF-8");
 206: 
 207:                 setWriter (new OutputStreamWriter (
 208:                         conn.getOutputStream (),
 209:                         "UTF8"), "UTF-8");
 210: 
 211:             } catch (IOException e) {
 212:                 fatal ("can't write (POST) to URI: " + target, e);
 213:             }
 214: 
 215:             // NOW base class can safely write that text!
 216:             super.startDocument ();
 217:         }
 218: 
 219:         public void endDocument () throws SAXException
 220:         {
 221:             //
 222:             // Finish writing the request (for HTTP, a POST);
 223:             // this closes the output stream.
 224:             //
 225:             super.endDocument ();
 226: 
 227:             //
 228:             // Receive the response.
 229:             // Produce events for the next stage.
 230:             //
 231:             InputSource source;
 232:             XMLReader   producer;
 233:             String      encoding;
 234: 
 235:             try {
 236: 
 237:                 source = new InputSource (conn.getInputStream ());
 238: 
 239: // FIXME if status is anything but success, report it!!  It'd be good to
 240: // save the request data just in case we need to deal with a forward.
 241: 
 242:                 encoding = Resolver.getEncoding (conn.getContentType ());
 243:                 if (encoding != null)
 244:                     source.setEncoding (encoding);
 245: 
 246:                 producer = XMLReaderFactory.createXMLReader ();
 247:                 producer.setErrorHandler (getErrorHandler ());
 248:                 EventFilter.bind (producer, next);
 249:                 producer.parse (source);
 250:                 conn = null;
 251: 
 252:             } catch (IOException e) {
 253:                 fatal ("I/O Exception reading response, " + e.getMessage (), e);
 254:             }
 255:         }
 256:     }
 257: }