001: /*
002:
003: Copyright 2004, Martian Software, Inc.
004:
005: Licensed under the Apache License, Version 2.0 (the "License");
006: you may not use this file except in compliance with the License.
007: You may obtain a copy of the License at
008:
009: http://www.apache.org/licenses/LICENSE-2.0
010:
011: Unless required by applicable law or agreed to in writing, software
012: distributed under the License is distributed on an "AS IS" BASIS,
013: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: See the License for the specific language governing permissions and
015: limitations under the License.
016:
017: */
018:
019: package com.martiansoftware.nailgun;
020:
021: import java.io.FilterInputStream;
022: import java.io.IOException;
023:
024: /**
025: * A FilterInputStream that is able to read the chunked stdin stream
026: * from a NailGun client.
027: *
028: * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
029: */
030: class NGInputStream extends FilterInputStream {
031:
032: private byte[] header;
033: private boolean eof = false;
034: private long remaining = 0;
035:
036: /**
037: * Creates a new NGInputStream wrapping the specified InputStream
038: * @param in the InputStream to wrap
039: */
040: public NGInputStream(java.io.InputStream in) {
041: super (in);
042: header = new byte[5];
043: }
044:
045: /**
046: * Reads a NailGun chunk header from the underlying InputStream.
047: *
048: * @throws IOException if thrown by the underlying InputStream,
049: * or if an unexpected NailGun chunk type is encountered.
050: */
051: private void readHeader() throws IOException {
052: if (eof)
053: return;
054:
055: int bytesRead = in.read(header);
056: int this Pass = 0;
057: while (bytesRead < 5) {
058: this Pass = in.read(header, bytesRead, 5 - bytesRead);
059: if (this Pass < 0) {
060: eof = true;
061: return;
062: }
063: bytesRead += this Pass;
064: }
065: switch (header[4]) {
066: case NGConstants.CHUNKTYPE_STDIN:
067: remaining = LongUtils.fromArray(header, 0);
068: break;
069:
070: case NGConstants.CHUNKTYPE_STDIN_EOF:
071: eof = true;
072: break;
073:
074: default:
075: throw (new IOException("Unknown stream type: "
076: + (char) header[4]));
077: }
078: }
079:
080: /**
081: * @see java.io.InputStream#available()
082: */
083: public int available() throws IOException {
084: if (eof)
085: return (0);
086: if (remaining > 0)
087: return (in.available());
088: return (Math.max(0, in.available() - 5));
089: }
090:
091: /**
092: * @see java.io.InputStream#markSupported()
093: */
094: public boolean markSupported() {
095: return (false);
096: }
097:
098: /**
099: * @see java.io.InputStream#read()
100: */
101: public int read() throws IOException {
102: // this should be more readable.
103: // this stomps on the first byte of the header array,
104: // which is ok
105: return ((read(header, 0, 1) == -1) ? -1 : (int) header[0]);
106: }
107:
108: /**
109: * @see java.io.InputStream.read(byte[])
110: */
111: public int read(byte[] b) throws IOException {
112: return (read(b, 0, b.length));
113: }
114:
115: /**
116: * @see java.io.InputStream.read(byte[],offset,length)
117: */
118: public int read(byte[] b, int offset, int length)
119: throws IOException {
120: if (remaining == 0)
121: readHeader();
122: if (eof)
123: return (-1);
124:
125: int bytesToRead = Math.min((int) remaining, length);
126: int result = in.read(b, offset, bytesToRead);
127: remaining -= result;
128: return (result);
129: }
130:
131: }
|