/* * $Id: CallFilter.java,v 1.1 2003-02-01 02:10:18 cbj Exp $ * Copyright (C) 1999-2001 David Brownell * * This file is part of GNU JAXP, a library. * * GNU JAXP is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GNU JAXP is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * As a special exception, if you link this library with other files to * produce an executable, this library does not by itself cause the * resulting executable to be covered by the GNU General Public License. * This exception does not however invalidate any other reasons why the * executable file might be covered by the GNU General Public License. */ package gnu.xml.pipeline; import java.io.*; import java.net.*; import org.xml.sax.*; import org.xml.sax.ext.*; import org.xml.sax.helpers.XMLReaderFactory; import gnu.xml.util.Resolver; import gnu.xml.util.XMLWriter; // $Id: CallFilter.java,v 1.1 2003-02-01 02:10:18 cbj Exp $ /** * Input is sent as an XML request to given URI, and the output of this * filter is the parsed response to that request. * A connection is opened to the remote URI when the startDocument call is * issued through this filter, and the request is finished when the * endDocument call is issued. Events should be written quickly enough to * prevent the remote HTTP server from aborting the connection due to * inactivity; you may want to buffer text in an earlier pipeline stage. * If your application requires validity checking of such * outputs, have the output pipeline include a validation stage. * *

In effect, this makes a remote procedure call to the URI, with the * request and response document syntax as chosen by the application. * Note that all the input events must be seen, and sent to the URI, * before the first output event can be seen. Clients are delayed * at least by waiting for the server to respond, constraining concurrency. * Services can thus be used to synchronize concurrent activities, and * even to prioritize service among different clients. * *

You are advised to avoid restricting yourself to an "RPC" model * for distributed computation. With a World Wide Web, network latencies * and failures (e.g. non-availability) * are significant; adopting a "procedure" model, rather than a workflow * model where bulk requests are sent and worked on asynchronously, is not * generally an optimal system-wide architecture. When the messages may * need authentication, such as with an OpenPGP signature, or when server * loads don't argue in favor of immediate responses, non-RPC models can * be advantageous. (So-called "peer to peer" computing models are one * additional type of model, though too often that term is applied to * systems that still have a centralized control structure.) * *

Be strict in what you send, liberal in what you accept, as * the Internet tradition goes. Strictly conformant data should never cause * problems to its receiver; make your request pipeline be very strict, and * don't compromise on that. Make your response pipeline strict as well, * but be ready to tolerate specific mild, temporary, and well-documented * variations from specific communications peers. * * @see XmlServlet * * @author David Brownell * @version $Date: 2003-02-01 02:10:18 $ */ final public class CallFilter implements EventConsumer { private Requestor req; private EventConsumer next; private URL target; private URLConnection conn; private ErrorHandler errHandler; /** * Initializes a call filter so that its inputs are sent to the * specified URI, and its outputs are sent to the next consumer * provided. * * @exception IOException if the URI isn't accepted as a URL */ // constructor used by PipelineFactory public CallFilter (String uri, EventConsumer next) throws IOException { this.next = next; req = new Requestor (); setCallTarget (uri); } /** * Assigns the URI of the call target to be used. * Does not affect calls currently being made. */ final public void setCallTarget (String uri) throws IOException { target = new URL (uri); } /** * Assigns the error handler to be used to present most fatal * errors. */ public void setErrorHandler (ErrorHandler handler) { req.setErrorHandler (handler); } /** * Returns the call target's URI. */ final public String getCallTarget () { return target.toString (); } /** Returns the content handler currently in use. */ final public org.xml.sax.ContentHandler getContentHandler () { return req; } /** Returns the DTD handler currently in use. */ final public DTDHandler getDTDHandler () { return req; } /** * Returns the declaration or lexical handler currently in * use, or throws an exception for other properties. */ final public Object getProperty (String id) throws SAXNotRecognizedException { if (EventFilter.DECL_HANDLER.equals (id)) return req; if (EventFilter.LEXICAL_HANDLER.equals (id)) return req; throw new SAXNotRecognizedException (id); } // JDK 1.1 seems to need it to be done this way, sigh ErrorHandler getErrorHandler () { return errHandler; } // // Takes input and echoes to server as POST input. // Then sends the POST reply to the next pipeline element. // final class Requestor extends XMLWriter { Requestor () { super ((Writer)null); } public synchronized void startDocument () throws SAXException { // Connect to remote object and set up to send it XML text try { if (conn != null) throw new IllegalStateException ("call is being made"); conn = target.openConnection (); conn.setDoOutput (true); conn.setRequestProperty ("Content-Type", "application/xml;charset=UTF-8"); setWriter (new OutputStreamWriter ( conn.getOutputStream (), "UTF8"), "UTF-8"); } catch (IOException e) { fatal ("can't write (POST) to URI: " + target, e); } // NOW base class can safely write that text! super.startDocument (); } public void endDocument () throws SAXException { // // Finish writing the request (for HTTP, a POST); // this closes the output stream. // super.endDocument (); // // Receive the response. // Produce events for the next stage. // InputSource source; XMLReader producer; String encoding; try { source = new InputSource (conn.getInputStream ()); // FIXME if status is anything but success, report it!! It'd be good to // save the request data just in case we need to deal with a forward. encoding = Resolver.getEncoding (conn.getContentType ()); if (encoding != null) source.setEncoding (encoding); producer = XMLReaderFactory.createXMLReader (); producer.setErrorHandler (getErrorHandler ()); EventFilter.bind (producer, next); producer.parse (source); conn = null; } catch (IOException e) { fatal ("I/O Exception reading response, " + e.getMessage (), e); } } } }