[AAF-21] Initial code import
[aaf/cadi.git] / core / src / main / java / com / att / cadi / BufferedServletInputStream.java
1 /*******************************************************************************\r
2  * ============LICENSE_START====================================================\r
3  * * org.onap.aai\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
11  * * \r
12  *  *      http://www.apache.org/licenses/LICENSE-2.0\r
13  * * \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
20  * *\r
21  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
22  * *\r
23  ******************************************************************************/\r
24 package com.att.cadi;\r
25 \r
26 import java.io.IOException;\r
27 import java.io.InputStream;\r
28 \r
29 import javax.servlet.ServletInputStream;\r
30 \r
31 /**\r
32  * BufferedServletInputStream\r
33  * \r
34  * There are cases in brain-dead middleware (SOAP) where they store routing information in the content.\r
35  * \r
36  * In HTTP, this requires reading the content from the InputStream which, of course, cannot be re-read.\r
37  * \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
41  * \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
46  *\r
47  * \r
48  *\r
49  */\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
54         \r
55         private InputStream is;\r
56         private int state = NONE;\r
57         private Capacitor capacitor;\r
58 \r
59         public BufferedServletInputStream(InputStream is) {\r
60                 this.is = is;\r
61                 capacitor = null;\r
62         }\r
63 \r
64 \r
65         // @Override\r
66         public int read() throws IOException {\r
67                 int value=-1;\r
68                 if(capacitor==null) {\r
69                         value=is.read();\r
70                 } else {\r
71                         switch(state) {\r
72                                 case STORE:\r
73                                         value = is.read();\r
74                                         if(value>=0) {\r
75                                                 capacitor.put((byte)value);\r
76                                         }\r
77                                         break;\r
78                                 case READ:\r
79                                         value = capacitor.read();\r
80                                         if(value<0) {\r
81                                                 capacitor.done();\r
82                                                 capacitor=null; // all done with buffer\r
83                                                 value = is.read();\r
84                                         }\r
85                         }\r
86                 } \r
87                 return value;\r
88         }\r
89 \r
90         // @Override\r
91         public int read(byte[] b) throws IOException {\r
92                 return read(b,0,b.length);\r
93         }\r
94 \r
95 \r
96         // @Override\r
97         public int read(byte[] b, int off, int len) throws IOException {\r
98                 int count = -1;\r
99                 if(capacitor==null) {\r
100                         count = is.read(b,off,len);\r
101                 } else {\r
102                         switch(state) {\r
103                                 case STORE:\r
104                                         count = is.read(b, off, len);\r
105                                         if(count>0) {\r
106                                                 capacitor.put(b, off, count);\r
107                                         }\r
108                                         break;\r
109                                 case READ:\r
110                                         count = capacitor.read(b, off, len);\r
111 //                                      System.out.println("Capacitor read " + count);\r
112                                         if(count<=0) {\r
113                                                 capacitor.done();\r
114                                                 capacitor=null; // all done with buffer\r
115                                         }\r
116                                         if(count<len) {\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
120                                                         count+=temp;\r
121                                                 } else {\r
122                                                         if(count<=0)count = temp; // must account for Stream coming back -1  \r
123                                                 }\r
124                                         }\r
125                                         break;\r
126                         }\r
127                 }\r
128 //              System.out.println("read reports " + count);\r
129                 return count;\r
130         }\r
131 \r
132         // @Override\r
133         public long skip(long n) throws IOException {\r
134                 long skipped = capacitor.skip(n);\r
135                 if(skipped<n) {\r
136                         skipped += is.skip(n-skipped);\r
137                 }\r
138                 return skipped;\r
139         }\r
140 \r
141 \r
142         // @Override\r
143         public int available() throws IOException {\r
144                 int count = is.available();\r
145                 if(capacitor!=null)count+=capacitor.available();\r
146                 return count;           \r
147         }\r
148         \r
149         /**\r
150          * Return just amount buffered (for debugging purposes, mostly)\r
151          * @return\r
152          */\r
153         public int buffered() {\r
154                 return capacitor.available();\r
155         }\r
156 \r
157 \r
158         // @Override\r
159         public void close() throws IOException {\r
160                 if(capacitor!=null) {\r
161                         capacitor.done();\r
162                         capacitor=null;\r
163                 }\r
164                 is.close();\r
165         }\r
166 \r
167 \r
168         /**\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
171          */\r
172         // @Override\r
173         public synchronized void mark(int readlimit) {\r
174                 switch(state) {\r
175                         case NONE:\r
176                                 capacitor = new Capacitor();\r
177                                 break;\r
178                         case READ:\r
179                                 capacitor.done();\r
180                                 break;\r
181                         // ignore case STORE:\r
182                 }\r
183                 state = STORE;\r
184         }\r
185 \r
186 \r
187         /**\r
188          * Reset Stream\r
189          * \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
192          *  \r
193          */\r
194         // @Override\r
195         public synchronized void reset() throws IOException {\r
196                 switch(state) {\r
197                         case STORE:\r
198                                 capacitor.setForRead();\r
199                                 state = READ;\r
200                                 break;\r
201                         case READ:\r
202                                 capacitor.reset();\r
203                                 break;\r
204 //                              throw new IOException("InputStream is already in READ state");\r
205                         case NONE: \r
206                                 throw new IOException("InputStream has not been marked");\r
207                 }\r
208         }\r
209 \r
210 \r
211         // @Override\r
212         public boolean markSupported() {\r
213                 return true;\r
214         }\r
215 }\r