Merge "Sonar fix: Perm.java"
[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 }