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