1 /*******************************************************************************
\r
2 * ============LICENSE_START====================================================
\r
4 * * ===========================================================================
\r
5 * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
\r
6 * * Copyright © 2017 Amdocs
\r
7 * * ===========================================================================
\r
8 * * Licensed under the Apache License, Version 2.0 (the "License");
\r
9 * * you may not use this file except in compliance with the License.
\r
10 * * You may obtain a copy of the License at
\r
12 * * http://www.apache.org/licenses/LICENSE-2.0
\r
14 * * Unless required by applicable law or agreed to in writing, software
\r
15 * * distributed under the License is distributed on an "AS IS" BASIS,
\r
16 * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
17 * * See the License for the specific language governing permissions and
\r
18 * * limitations under the License.
\r
19 * * ============LICENSE_END====================================================
\r
21 * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
\r
23 ******************************************************************************/
\r
24 package com.att.cadi;
\r
26 import java.io.IOException;
\r
27 import java.io.InputStream;
\r
29 import javax.servlet.ServletInputStream;
\r
32 * BufferedServletInputStream
\r
34 * There are cases in brain-dead middleware (SOAP) where they store routing information in the content.
\r
36 * In HTTP, this requires reading the content from the InputStream which, of course, cannot be re-read.
\r
38 * BufferedInputStream exists to implement the "Mark" protocols for Streaming, which will enable being
\r
39 * re-read. Unfortunately, J2EE chose to require a "ServletInputStream" as an abstract class, rather than
\r
40 * an interface, which requires we create a delegating pattern, rather than the preferred inheriting pattern.
\r
42 * Unfortunately, the standard "BufferedInputStream" cannot be used, because it simply creates a byte array
\r
43 * in the "mark(int)" method of that size. This is not appropriate for this application, because the Header
\r
44 * can be potentially huge, and if a buffer was allocated to accommodate all possibilities, the cost of memory
\r
45 * allocation would be too large for high performance transactions.
\r
50 public class BufferedServletInputStream extends ServletInputStream {
\r
51 private static final int NONE = 0;
\r
52 private static final int STORE = 1;
\r
53 private static final int READ = 2;
\r
55 private InputStream is;
\r
56 private int state = NONE;
\r
57 private Capacitor capacitor;
\r
59 public BufferedServletInputStream(InputStream is) {
\r
66 public int read() throws IOException {
\r
68 if(capacitor==null) {
\r
75 capacitor.put((byte)value);
\r
79 value = capacitor.read();
\r
82 capacitor=null; // all done with buffer
\r
91 public int read(byte[] b) throws IOException {
\r
92 return read(b,0,b.length);
\r
97 public int read(byte[] b, int off, int len) throws IOException {
\r
99 if(capacitor==null) {
\r
100 count = is.read(b,off,len);
\r
104 count = is.read(b, off, len);
\r
106 capacitor.put(b, off, count);
\r
110 count = capacitor.read(b, off, len);
\r
111 // System.out.println("Capacitor read " + count);
\r
114 capacitor=null; // all done with buffer
\r
117 int temp = is.read(b, count, len-count);
\r
118 // System.out.println("Capacitor done, stream read " + temp);
\r
119 if(temp>0) { // watch for -1
\r
122 if(count<=0)count = temp; // must account for Stream coming back -1
\r
128 // System.out.println("read reports " + count);
\r
133 public long skip(long n) throws IOException {
\r
134 long skipped = capacitor.skip(n);
\r
136 skipped += is.skip(n-skipped);
\r
143 public int available() throws IOException {
\r
144 int count = is.available();
\r
145 if(capacitor!=null)count+=capacitor.available();
\r
150 * Return just amount buffered (for debugging purposes, mostly)
\r
153 public int buffered() {
\r
154 return capacitor.available();
\r
159 public void close() throws IOException {
\r
160 if(capacitor!=null) {
\r
169 * Note: Readlimit is ignored in this implementation, because the need was for unknown buffer size which wouldn't
\r
170 * require allocating and dumping huge chunks of memory every use, or risk overflow.
\r
173 public synchronized void mark(int readlimit) {
\r
176 capacitor = new Capacitor();
\r
181 // ignore case STORE:
\r
190 * Calling this twice is not supported in typical Stream situations, but it is allowed in this service. The caveat is that it can only reset
\r
191 * the data read in since Mark has been called. The data integrity is only valid if you have not continued to read past what is stored.
\r
195 public synchronized void reset() throws IOException {
\r
198 capacitor.setForRead();
\r
204 // throw new IOException("InputStream is already in READ state");
\r
206 throw new IOException("InputStream has not been marked");
\r
212 public boolean markSupported() {
\r