summaryrefslogtreecommitdiff
path: root/external/jaxp/source/gnu/xml/pipeline/CallFilter.java
blob: 141b50a7addec36bfb5f1a25ad3381dd15d9439f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
/*
 * $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.
 *
 * <p>In effect, this makes a remote procedure call to the URI, with the
 * request and response document syntax as chosen by the application.
 * <em>Note that all the input events must be seen, and sent to the URI,
 * before the first output event can be seen. </em>  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.
 *
 * <p> 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.)
 *
 * <p> <em>Be strict in what you send, liberal in what you accept,</em> 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);
	    }
	}
    }
}