Frames | No Frames |
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: }