2
* JBoss, Home of Professional Open Source
3
* Copyright 2005, JBoss Inc., and individual contributors as indicated
4
* by the @authors tag. See the copyright.txt in the distribution for a
5
* full listing of individual contributors.
7
* This is free software; you can redistribute it and/or modify it
8
* under the terms of the GNU Lesser General Public License as
9
* published by the Free Software Foundation; either version 2.1 of
10
* the License, or (at your option) any later version.
12
* This software is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
* Lesser General Public License for more details.
17
* You should have received a copy of the GNU Lesser General Public
18
* License along with this software; if not, write to the Free
19
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23
package org.jboss.remoting.stream;
25
import org.jboss.logging.Logger;
26
import org.jboss.remoting.Client;
27
import org.jboss.remoting.InvokerLocator;
29
import java.io.IOException;
30
import java.io.InputStream;
33
* This is the server side proxy back to the orginal stream
34
* on the client side. It implements InputStream, so can be
35
* passed and acted on by the server handler as a regular InputStream
36
* type. For all the InputStream methods, it should behave EXACTLY
37
* like a local InputStream with the one exception being that it
38
* will sometimes throw IOExceptions based on network exceptions
39
* or in the case when the method does not throw an IOException, throwing
40
* a RuntimeException if network problem (however none of the method
41
* signatures are changed).
43
* Internally, it will use remoting to callback to the client.
45
* @author <a href="mailto:tom.elrod@jboss.com">Tom Elrod</a>
47
public class StreamHandler extends InputStream //implements InvocationHandler
49
private InvokerLocator streamServerLocator = null;
50
private Client streamClient = null;
52
private static final Logger log = Logger.getLogger(StreamHandler.class);
54
// The remoting invocation methods the match the InputStream metnhods.
55
public static final String READ = "read()";
56
public static final String AVAILABLE = "available()";
57
public static final String CLOSE = "close()";
58
public static final String RESET = "reset()";
59
public static final String MARKSUPPORTED = "markSupported()";
60
public static final String MARKREADLIMIT = "mark(int readlimit)";
61
public static final String SKIP = "skip(long n)";
62
public static final String READBYTEARRAY = "read(byte b[])";
63
public static final String READOFFSET = "read(byte b[], int off, int len)";
66
* Constructor requiring the locator url back to the client's
67
* StreamServer connector (actually the connector's server invoker).
72
//private StreamHandler(String locatorURL) throws Exception
73
public StreamHandler(String locatorURL) throws Exception
75
streamServerLocator = new InvokerLocator(locatorURL);
76
streamClient = new Client(streamServerLocator);
77
streamClient.connect();
81
* Returns the number of bytes that can be read (or skipped over) from
82
* this input stream without blocking by the next caller of a method for
83
* this input stream. The next caller might be the same thread or or
86
* <p> The <code>available</code> method for class <code>InputStream</code>
87
* always returns <code>0</code>.
89
* <p> This method should be overridden by subclasses.
91
* @return the number of bytes that can be read from this input stream
93
* @throws java.io.IOException if an I/O error occurs.
95
public int available() throws IOException
101
Integer retInt = (Integer) streamClient.invoke(new StreamCallPayload(AVAILABLE));
104
readInt = retInt.intValue();
107
catch(Throwable throwable)
109
log.debug("Error getting available from client stream.", throwable);
110
throw new IOException(throwable.getMessage());
116
* Closes this input stream and releases any system resources associated
119
* <p> The <code>close</code> method of <code>InputStream</code> does
122
* @throws java.io.IOException if an I/O error occurs.
124
public void close() throws IOException
128
streamClient.invoke(new StreamCallPayload(CLOSE));
130
catch(Throwable throwable)
132
log.debug("Error closing client stream.", throwable);
133
throw new IOException(throwable.getMessage());
138
* Repositions this stream to the position at the time the
139
* <code>mark</code> method was last called on this input stream.
141
* <p> The general contract of <code>reset</code> is:
145
* <li> If the method <code>markSupported</code> returns
146
* <code>true</code>, then:
148
* <ul><li> If the method <code>mark</code> has not been called since
149
* the stream was created, or the number of bytes read from the stream
150
* since <code>mark</code> was last called is larger than the argument
151
* to <code>mark</code> at that last call, then an
152
* <code>IOException</code> might be thrown.
154
* <li> If such an <code>IOException</code> is not thrown, then the
155
* stream is reset to a state such that all the bytes read since the
156
* most recent call to <code>mark</code> (or since the start of the
157
* file, if <code>mark</code> has not been called) will be resupplied
158
* to subsequent callers of the <code>read</code> method, followed by
159
* any bytes that otherwise would have been the next input data as of
160
* the time of the call to <code>reset</code>. </ul>
162
* <li> If the method <code>markSupported</code> returns
163
* <code>false</code>, then:
165
* <ul><li> The call to <code>reset</code> may throw an
166
* <code>IOException</code>.
168
* <li> If an <code>IOException</code> is not thrown, then the stream
169
* is reset to a fixed state that depends on the particular type of the
170
* input stream and how it was created. The bytes that will be supplied
171
* to subsequent callers of the <code>read</code> method depend on the
172
* particular type of the input stream. </ul></ul>
174
* <p> The method <code>reset</code> for class <code>InputStream</code>
175
* does nothing and always throws an <code>IOException</code>.
177
* @throws java.io.IOException if this stream has not been marked or if the
178
* mark has been invalidated.
179
* @see java.io.InputStream#mark(int)
180
* @see java.io.IOException
182
public synchronized void reset() throws IOException
186
streamClient.invoke(new StreamCallPayload(RESET));
188
catch(Throwable throwable)
190
log.debug("Error reseting client stream.", throwable);
191
throw new IOException(throwable.getMessage());
196
* Tests if this input stream supports the <code>mark</code> and
197
* <code>reset</code> methods. Whether or not <code>mark</code> and
198
* <code>reset</code> are supported is an invariant property of a
199
* particular input stream instance. The <code>markSupported</code> method
200
* of <code>InputStream</code> returns <code>false</code>.
202
* @return <code>true</code> if this stream instance supports the mark
203
* and reset methods; <code>false</code> otherwise.
204
* @see java.io.InputStream#mark(int)
205
* @see java.io.InputStream#reset()
207
public boolean markSupported()
209
boolean supported = false;
213
Boolean bSupported = (Boolean) streamClient.invoke(new StreamCallPayload(MARKSUPPORTED));
214
if(bSupported != null)
216
supported = bSupported.booleanValue();
219
catch(Throwable throwable)
221
log.debug("Error getting markSupported from client stream.", throwable);
222
throw new RuntimeException(throwable.getMessage(), throwable);
228
* Marks the current position in this input stream. A subsequent call to
229
* the <code>reset</code> method repositions this stream at the last marked
230
* position so that subsequent reads re-read the same bytes.
232
* <p> The <code>readlimit</code> arguments tells this input stream to
233
* allow that many bytes to be read before the mark position gets
236
* <p> The general contract of <code>mark</code> is that, if the method
237
* <code>markSupported</code> returns <code>true</code>, the stream somehow
238
* remembers all the bytes read after the call to <code>mark</code> and
239
* stands ready to supply those same bytes again if and whenever the method
240
* <code>reset</code> is called. However, the stream is not required to
241
* remember any data at all if more than <code>readlimit</code> bytes are
242
* read from the stream before <code>reset</code> is called.
244
* <p> The <code>mark</code> method of <code>InputStream</code> does
247
* @param readlimit the maximum limit of bytes that can be read before
248
* the mark position becomes invalid.
249
* @see java.io.InputStream#reset()
251
public synchronized void mark(int readlimit)
255
StreamCallPayload payload = new StreamCallPayload(MARKREADLIMIT);
256
payload.setParams(new Object[]{new Integer(readlimit)});
257
streamClient.invoke(payload);
259
catch(Throwable throwable)
261
log.debug("Error marking with read limit on client stream.", throwable);
262
throw new RuntimeException(throwable.getMessage(), throwable);
267
* Skips over and discards <code>n</code> bytes of data from this input
268
* stream. The <code>skip</code> method may, for a variety of reasons, end
269
* up skipping over some smaller number of bytes, possibly <code>0</code>.
270
* This may result from any of a number of conditions; reaching end of file
271
* before <code>n</code> bytes have been skipped is only one possibility.
272
* The actual number of bytes skipped is returned. If <code>n</code> is
273
* negative, no bytes are skipped.
275
* <p> The <code>skip</code> method of <code>InputStream</code> creates a
276
* byte array and then repeatedly reads into it until <code>n</code> bytes
277
* have been read or the end of the stream has been reached. Subclasses are
278
* encouraged to provide a more efficient implementation of this method.
280
* @param n the number of bytes to be skipped.
281
* @return the actual number of bytes skipped.
282
* @throws java.io.IOException if an I/O error occurs.
284
public long skip(long n) throws IOException
286
long numSkipped = -1;
290
StreamCallPayload payload = new StreamCallPayload(SKIP);
291
payload.setParams(new Object[]{new Long(n)});
292
Long ret = (Long) streamClient.invoke(payload);
295
numSkipped = ret.longValue();
298
catch(Throwable throwable)
300
log.debug("Error skipping on client stream.", throwable);
301
throw new IOException(throwable.getMessage());
308
* Reads some number of bytes from the input stream and stores them into
309
* the buffer array <code>b</code>. The number of bytes actually read is
310
* returned as an integer. This method blocks until input data is
311
* available, end of file is detected, or an exception is thrown.
313
* <p> If <code>b</code> is <code>null</code>, a
314
* <code>NullPointerException</code> is thrown. If the length of
315
* <code>b</code> is zero, then no bytes are read and <code>0</code> is
316
* returned; otherwise, there is an attempt to read at least one byte. If
317
* no byte is available because the stream is at end of file, the value
318
* <code>-1</code> is returned; otherwise, at least one byte is read and
319
* stored into <code>b</code>.
321
* <p> The first byte read is stored into element <code>b[0]</code>, the
322
* next one into <code>b[1]</code>, and so on. The number of bytes read is,
323
* at most, equal to the length of <code>b</code>. Let <i>k</i> be the
324
* number of bytes actually read; these bytes will be stored in elements
325
* <code>b[0]</code> through <code>b[</code><i>k</i><code>-1]</code>,
326
* leaving elements <code>b[</code><i>k</i><code>]</code> through
327
* <code>b[b.length-1]</code> unaffected.
329
* <p> If the first byte cannot be read for any reason other than end of
330
* file, then an <code>IOException</code> is thrown. In particular, an
331
* <code>IOException</code> is thrown if the input stream has been closed.
333
* <p> The <code>read(b)</code> method for class <code>InputStream</code>
334
* has the same effect as: <pre><code> read(b, 0, b.length) </code></pre>
336
* @param b the buffer into which the data is read.
337
* @return the total number of bytes read into the buffer, or
338
* <code>-1</code> is there is no more data because the end of
339
* the stream has been reached.
340
* @throws java.io.IOException if an I/O error occurs.
341
* @throws NullPointerException if <code>b</code> is <code>null</code>.
342
* @see java.io.InputStream#read(byte[], int, int)
344
public int read(byte b[]) throws IOException
348
throw new NullPointerException("can not read for a null byte array.");
362
StreamCallPayload payload = new StreamCallPayload(READBYTEARRAY);
363
payload.setParams(new Object[]{b});
364
StreamCallPayload ret = (StreamCallPayload) streamClient.invoke(payload);
367
Object[] retVals = ret.getParams();
368
byte[] retBytes = (byte[]) retVals[0];
369
Integer retInt = (Integer) retVals[1];
371
retByte = retInt.intValue();
375
System.arraycopy(retBytes, 0, b, 0, retByte);
379
catch(Throwable throwable)
381
log.debug("Error reading from client stream.", throwable);
382
throw new IOException(throwable.getMessage());
389
* Reads up to <code>len</code> bytes of data from the input stream into
390
* an array of bytes. An attempt is made to read as many as
391
* <code>len</code> bytes, but a smaller number may be read, possibly
392
* zero. The number of bytes actually read is returned as an integer.
394
* <p> This method blocks until input data is available, end of file is
395
* detected, or an exception is thrown.
397
* <p> If <code>b</code> is <code>null</code>, a
398
* <code>NullPointerException</code> is thrown.
400
* <p> If <code>off</code> is negative, or <code>len</code> is negative, or
401
* <code>off+len</code> is greater than the length of the array
402
* <code>b</code>, then an <code>IndexOutOfBoundsException</code> is
405
* <p> If <code>len</code> is zero, then no bytes are read and
406
* <code>0</code> is returned; otherwise, there is an attempt to read at
407
* least one byte. If no byte is available because the stream is at end of
408
* file, the value <code>-1</code> is returned; otherwise, at least one
409
* byte is read and stored into <code>b</code>.
411
* <p> The first byte read is stored into element <code>b[off]</code>, the
412
* next one into <code>b[off+1]</code>, and so on. The number of bytes read
413
* is, at most, equal to <code>len</code>. Let <i>k</i> be the number of
414
* bytes actually read; these bytes will be stored in elements
415
* <code>b[off]</code> through <code>b[off+</code><i>k</i><code>-1]</code>,
416
* leaving elements <code>b[off+</code><i>k</i><code>]</code> through
417
* <code>b[off+len-1]</code> unaffected.
419
* <p> In every case, elements <code>b[0]</code> through
420
* <code>b[off]</code> and elements <code>b[off+len]</code> through
421
* <code>b[b.length-1]</code> are unaffected.
423
* <p> If the first byte cannot be read for any reason other than end of
424
* file, then an <code>IOException</code> is thrown. In particular, an
425
* <code>IOException</code> is thrown if the input stream has been closed.
427
* <p> The <code>read(b,</code> <code>off,</code> <code>len)</code> method
428
* for class <code>InputStream</code> simply calls the method
429
* <code>read()</code> repeatedly. If the first such call results in an
430
* <code>IOException</code>, that exception is returned from the call to
431
* the <code>read(b,</code> <code>off,</code> <code>len)</code> method. If
432
* any subsequent call to <code>read()</code> results in a
433
* <code>IOException</code>, the exception is caught and treated as if it
434
* were end of file; the bytes read up to that point are stored into
435
* <code>b</code> and the number of bytes read before the exception
436
* occurred is returned. Subclasses are encouraged to provide a more
437
* efficient implementation of this method.
439
* @param b the buffer into which the data is read.
440
* @param off the start offset in array <code>b</code>
441
* at which the data is written.
442
* @param len the maximum number of bytes to read.
443
* @return the total number of bytes read into the buffer, or
444
* <code>-1</code> if there is no more data because the end of
445
* the stream has been reached.
446
* @throws java.io.IOException if an I/O error occurs.
447
* @throws NullPointerException if <code>b</code> is <code>null</code>.
448
* @see java.io.InputStream#read()
450
public int read(byte b[], int off, int len) throws IOException
454
throw new NullPointerException("can not read for a null byte array.");
464
if(off < 0 || len < 0 || off + len > b.length)
466
throw new IndexOutOfBoundsException("Either off or len is negative or off+len is greater than length of b.");
479
byte[] payloadArray = new byte[len];
480
StreamCallPayload payload = new StreamCallPayload(READBYTEARRAY);
481
payload.setParams(new Object[]{payloadArray});
482
StreamCallPayload ret = (StreamCallPayload) streamClient.invoke(payload);
485
Object[] retVals = ret.getParams();
486
byte[] retBytes = (byte[]) retVals[0];
487
Integer retInt = (Integer) retVals[1];
489
retByte = retInt.intValue();
493
System.arraycopy(retBytes, 0, b, off, retByte);
497
catch(Throwable throwable)
499
log.debug("Error reading with offset from client stream.", throwable);
500
throw new IOException(throwable.getMessage());
507
* Reads the next byte of data from the input stream. The value byte is
508
* returned as an <code>int</code> in the range <code>0</code> to
509
* <code>255</code>. If no byte is available because the end of the stream
510
* has been reached, the value <code>-1</code> is returned. This method
511
* blocks until input data is available, the end of the stream is detected,
512
* or an exception is thrown.
514
* <p> A subclass must provide an implementation of this method.
516
* @return the next byte of data, or <code>-1</code> if the end of the
518
* @throws java.io.IOException if an I/O error occurs.
520
public int read() throws IOException
526
Integer retInt = (Integer) streamClient.invoke(new StreamCallPayload(READ));
529
readInt = retInt.intValue();
532
catch(Throwable throwable)
534
log.debug("Error reading from client stream.", throwable);
535
throw new IOException(throwable.getMessage());
b'\\ No newline at end of file'