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