2df01cda1325f36d6c2270d8f7b6962a20b3da55
[aaf/authz.git] / cadi / core / src / main / java / org / onap / aaf / cadi / BufferedServletInputStream.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6  * ===========================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END====================================================
19  *
20  */
21
22 package org.onap.aaf.cadi;
23
24 import java.io.IOException;
25 import java.io.InputStream;
26
27 import javax.servlet.ServletInputStream;
28
29 /**
30  * BufferedServletInputStream
31  * 
32  * There are cases in brain-dead middleware (SOAP) where they store routing information in the content.
33  * 
34  * In HTTP, this requires reading the content from the InputStream which, of course, cannot be re-read.
35  * 
36  * BufferedInputStream exists to implement the "Mark" protocols for Streaming, which will enable being 
37  * re-read.  Unfortunately, J2EE chose to require a "ServletInputStream" as an abstract class, rather than
38  * an interface, which requires we create a delegating pattern, rather than the preferred inheriting pattern. 
39  * 
40  * Unfortunately, the standard "BufferedInputStream" cannot be used, because it simply creates a byte array
41  * in the "mark(int)" method of that size.  This is not appropriate for this application, because the Header 
42  * can be potentially huge, and if a buffer was allocated to accommodate all possibilities, the cost of memory 
43  * allocation would be too large for high performance transactions.
44  *
45  * 
46  * @author Jonathan
47  *
48  */
49 public class BufferedServletInputStream extends ServletInputStream {
50         private static final int NONE = 0;
51         private static final int STORE = 1;
52         private static final int READ = 2;
53         
54         private InputStream is;
55         private int state = NONE;
56         private Capacitor capacitor;
57
58         public BufferedServletInputStream(InputStream is) {
59                 this.is = is;
60                 capacitor = null;
61         }
62
63
64         public int read() throws IOException {
65                 int value=-1;
66                 if(capacitor==null) {
67                         value=is.read();
68                 } else {
69                         switch(state) {
70                                 case STORE:
71                                         value = is.read();
72                                         if(value>=0) {
73                                                 capacitor.put((byte)value);
74                                         }
75                                         break;
76                                 case READ:
77                                         value = capacitor.read();
78                                         if(value<0) {
79                                                 capacitor.done();
80                                                 capacitor=null; // all done with buffer
81                                                 value = is.read();
82                                         }
83                         }
84                 } 
85                 return value;
86         }
87
88         public int read(byte[] b) throws IOException {
89                 return read(b,0,b.length);
90         }
91
92
93         public int read(byte[] b, int off, int len) throws IOException {
94                 int count = -1;
95                 if(capacitor==null) {
96                         count = is.read(b,off,len);
97                 } else {
98                         switch(state) {
99                                 case STORE:
100                                         count = is.read(b, off, len);
101                                         if(count>0) {
102                                                 capacitor.put(b, off, count);
103                                         }
104                                         break;
105                                 case READ:
106                                         count = capacitor.read(b, off, len);
107                                         if(count<=0) {
108                                                 capacitor.done();
109                                                 capacitor=null; // all done with buffer
110                                         }
111                                         if(count<len) {
112                                                 int temp = is.read(b, count, len-count);
113                                                 if(temp>0) { // watch for -1
114                                                         count+=temp;
115                                                 } else if(count<=0) {
116                             count = temp; // must account for Stream coming back -1  
117                                                 }
118                                         }
119                                         break;
120                         }
121                 }
122                 return count;
123         }
124
125         public long skip(long n) throws IOException {
126                 long skipped = capacitor.skip(n);
127                 if(skipped<n) {
128                         skipped += is.skip(n-skipped);
129                 }
130                 return skipped;
131         }
132
133
134         public int available() throws IOException {
135                 int count = is.available();
136                 if(capacitor!=null)count+=capacitor.available();
137                 return count;           
138         }
139         
140         /**
141          * Return just amount buffered (for debugging purposes, mostly)
142          * @return
143          */
144         public int buffered() {
145                 return capacitor.available();
146         }
147
148
149         public void close() throws IOException {
150                 if(capacitor!=null) {
151                         capacitor.done();
152                         capacitor=null;
153                 }
154                 is.close();
155         }
156
157
158         /**
159          * Note: Readlimit is ignored in this implementation, because the need was for unknown buffer size which wouldn't 
160          * require allocating and dumping huge chunks of memory every use, or risk overflow.
161          */
162         public synchronized void mark(int readlimit) {
163                 switch(state) {
164                         case NONE:
165                                 capacitor = new Capacitor();
166                                 break;
167                         case READ:
168                                 capacitor.done();
169                                 break;
170                 }
171                 state = STORE;
172         }
173
174
175         /**
176          * Reset Stream
177          * 
178          * 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
179          * 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.
180          *  
181          */
182         public synchronized void reset() throws IOException {
183                 switch(state) {
184                         case STORE:
185                                 capacitor.setForRead();
186                                 state = READ;
187                                 break;
188                         case READ:
189                                 capacitor.reset();
190                                 break;
191                         case NONE: 
192                                 throw new IOException("InputStream has not been marked");
193                 }
194         }
195
196
197         public boolean markSupported() {
198                 return true;
199         }
200 }