001: /*
002: * The Apache Software License, Version 1.1
003: *
004: * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution, if
019: * any, must include the following acknowlegement:
020: * "This product includes software developed by the
021: * Caucho Technology (http://www.caucho.com/)."
022: * Alternately, this acknowlegement may appear in the software itself,
023: * if and wherever such third-party acknowlegements normally appear.
024: *
025: * 4. The names "Hessian", "Resin", and "Caucho" must not be used to
026: * endorse or promote products derived from this software without prior
027: * written permission. For written permission, please contact
028: * info@caucho.com.
029: *
030: * 5. Products derived from this software may not be called "Resin"
031: * nor may "Resin" appear in their names without prior written
032: * permission of Caucho Technology.
033: *
034: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
035: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
036: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
037: * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS
038: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
039: * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
040: * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
041: * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
042: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
043: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
044: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
045: *
046: * @author Sam
047: */
048:
049: package com.caucho.portal.generic.context;
050:
051: import com.caucho.portal.generic.BufferFactory;
052: import com.caucho.portal.generic.PortletByteBuffer;
053: import com.caucho.portal.generic.PortletCharBuffer;
054:
055: import java.io.IOException;
056: import java.io.OutputStream;
057: import java.io.PrintWriter;
058: import java.util.ArrayList;
059: import java.util.Iterator;
060: import java.util.LinkedHashMap;
061: import java.util.Map;
062: import java.util.logging.Logger;
063:
064: public class BufferedResponseHandler extends AbstractResponseHandler {
065: protected static final Logger log = Logger
066: .getLogger(BufferedResponseHandler.class.getName());
067:
068: private static final int NOT_ALLOCATED = Integer.MIN_VALUE;
069:
070: private BufferFactory _bufferFactory;
071:
072: // size of buffer to allocate when Writer or OutputStream is obtained
073: private int _initialBufferSize = -1;
074:
075: private LinkedHashMap<String, Object> _propertiesMap;
076:
077: private int _bufferSize = NOT_ALLOCATED; // size allocated
078: private PortletCharBuffer _charBuffer;
079: private PortletByteBuffer _byteBuffer;
080: private PrintWriter _writer;
081: private OutputStream _outputStream;
082: private boolean _isCommitted;
083:
084: public BufferedResponseHandler(ResponseHandler responseHandler,
085: BufferFactory bufferFactory, int initialBufferSize) {
086: open(responseHandler, bufferFactory, initialBufferSize);
087: }
088:
089: public void open(ResponseHandler responseHandler,
090: BufferFactory bufferFactory, int initialBufferSize) {
091: super .open(responseHandler);
092: _bufferFactory = bufferFactory;
093: _initialBufferSize = initialBufferSize;
094: }
095:
096: public void finish() throws IOException {
097: if (!isError())
098: flushBuffer();
099:
100: freeBuffers(true);
101:
102: if (_propertiesMap != null)
103: _propertiesMap.clear();
104:
105: _isCommitted = false;
106: _writer = null;
107: _outputStream = null;
108: _bufferSize = NOT_ALLOCATED;
109: _initialBufferSize = -1;
110: _bufferFactory = null;
111:
112: super .finish();
113: }
114:
115: public void setBufferSize(int bufferSize) {
116: if (_bufferSize != NOT_ALLOCATED) {
117: if (bufferSize > _bufferSize) {
118: if (!_isCommitted) {
119: freeBuffers(true);
120: _initialBufferSize = bufferSize;
121: } else {
122: if (_bufferSize == 0 && _initialBufferSize != 0)
123: throw new IllegalStateException(
124: "buffer already committed to `"
125: + _bufferSize + "'");
126: else
127: throw new IllegalStateException(
128: "buffer already committed");
129: }
130: }
131: } else
132: _initialBufferSize = bufferSize;
133: }
134:
135: public int getBufferSize() {
136: if (_bufferSize != NOT_ALLOCATED)
137: return _bufferSize;
138: else if (_initialBufferSize != -1)
139: return _initialBufferSize;
140: else
141: return _bufferFactory.getDefaultBufferSize();
142: }
143:
144: public void setProperty(String name, String value) {
145: if (_isCommitted)
146: throw new IllegalStateException("response committed");
147:
148: if (_propertiesMap == null)
149: _propertiesMap = new LinkedHashMap<String, Object>();
150:
151: _propertiesMap.put(name, value);
152: }
153:
154: public void addProperty(String name, String value) {
155: if (_isCommitted)
156: throw new IllegalStateException("response committed");
157:
158: Object existingValue = null;
159:
160: if (_propertiesMap == null)
161: _propertiesMap = new LinkedHashMap<String, Object>();
162: else
163: existingValue = _propertiesMap.get(name);
164:
165: if (existingValue == null) {
166: _propertiesMap.put(name, value);
167: } else if (existingValue instanceof ArrayList) {
168: ((ArrayList<String>) existingValue).add(value);
169: } else {
170: ArrayList<String> valueList = new ArrayList<String>();
171: valueList.add((String) existingValue);
172: valueList.add(value);
173: }
174: }
175:
176: public boolean isCommitted() {
177: return _isCommitted;
178: }
179:
180: private void allocateCharBufferIfNeeded() {
181: if (_byteBuffer != null)
182: throw new IllegalStateException(
183: "cannot allocate char buffer, byte buffer already allocated");
184:
185: if (_bufferSize == NOT_ALLOCATED) {
186: if (_initialBufferSize != 0) {
187: _charBuffer = _bufferFactory
188: .allocateCharBuffer(_initialBufferSize);
189: _bufferSize = _charBuffer.getCapacity();
190: } else
191: _bufferSize = 0;
192: }
193: }
194:
195: private void allocateByteBufferIfNeeded() {
196: if (_charBuffer != null)
197: throw new IllegalStateException(
198: "cannot allocate byte buffer, char buffer already allocated");
199:
200: if (_bufferSize == NOT_ALLOCATED) {
201: if (_initialBufferSize != 0) {
202: _byteBuffer = _bufferFactory
203: .allocateByteBuffer(_initialBufferSize);
204: _bufferSize = _byteBuffer.getCapacity();
205: } else
206: _bufferSize = 0;
207: }
208: }
209:
210: public PrintWriter getWriter() throws IOException {
211: checkErrorOrFail();
212:
213: if (_writer != null)
214: return _writer;
215:
216: return _writer = super .getWriter();
217: }
218:
219: public OutputStream getOutputStream() throws IOException {
220: checkErrorOrFail();
221:
222: if (_outputStream != null)
223: return _outputStream;
224:
225: return _outputStream = super .getOutputStream();
226: }
227:
228: public void reset() {
229: resetBuffer();
230:
231: if (_propertiesMap != null)
232: _propertiesMap.clear();
233: }
234:
235: public void resetBuffer() {
236: if (_isCommitted)
237: throw new IllegalStateException(
238: "response is already committed");
239:
240: if (_byteBuffer != null)
241: _byteBuffer.reset();
242:
243: if (_charBuffer != null)
244: _charBuffer.reset();
245:
246: freeBuffers(true);
247: }
248:
249: /**
250: * @param isAllocateAgain allow them to be allocated again on next write
251: */
252: private void freeBuffers(boolean isAllocateAgain) {
253: PortletCharBuffer charBuffer = _charBuffer;
254: PortletByteBuffer byteBuffer = _byteBuffer;
255:
256: _charBuffer = null;
257: _byteBuffer = null;
258:
259: if (charBuffer != null) {
260: if (charBuffer.size() != 0)
261: throw new IllegalStateException(
262: "still content in buffer");
263:
264: charBuffer.finish();
265: }
266:
267: if (byteBuffer != null) {
268: if (byteBuffer.size() != 0)
269: throw new IllegalStateException(
270: "still content in buffer");
271:
272: byteBuffer.finish();
273: }
274:
275: if (isAllocateAgain)
276: _bufferSize = NOT_ALLOCATED;
277: }
278:
279: public void flushBuffer() throws IOException {
280: checkErrorOrFail();
281:
282: try {
283: flushProperties();
284: flushBufferOnly();
285: } catch (Exception ex) {
286: setError(ex);
287: }
288: }
289:
290: private void flushProperties() {
291: if (_propertiesMap != null && _propertiesMap.size() > 0) {
292:
293: _isCommitted = true;
294:
295: Iterator<Map.Entry<String, Object>> iter = _propertiesMap
296: .entrySet().iterator();
297:
298: do {
299: Map.Entry<String, Object> entry = iter.next();
300: String name = entry.getKey();
301: Object value = entry.getValue();
302:
303: if (value instanceof ArrayList) {
304: ArrayList<String> valueList = (ArrayList<String>) value;
305:
306: for (int i = 0; i < valueList.size(); i++) {
307: super .addProperty(name, valueList.get(i));
308: }
309: } else {
310: super .setProperty(name, (String) value);
311: }
312:
313: iter.remove();
314: } while (iter.hasNext());
315: }
316: }
317:
318: private void flushBufferOnly() throws IOException {
319: if (_charBuffer != null && _charBuffer.size() > 0) {
320: _isCommitted = true;
321: _charBuffer.flush(super .getUnderlyingWriter());
322: }
323:
324: if (_byteBuffer != null && _byteBuffer.size() > 0) {
325: _isCommitted = true;
326: _byteBuffer.flush(super .getUnderlyingOutputStream());
327: }
328:
329: // after they are flushed once, don't need them anymore
330:
331: freeBuffers(false);
332: }
333:
334: protected void print(char buf[], int off, int len)
335: throws IOException {
336: if (len == 0)
337: return;
338:
339: allocateCharBufferIfNeeded();
340:
341: checkErrorOrFail();
342:
343: if (_charBuffer == null) {
344: _isCommitted = true;
345: super .print(buf, off, len);
346: } else {
347: if (!_charBuffer.print(buf, off, len)) {
348: flushBuffer();
349: _isCommitted = true;
350: super .print(buf, off, len);
351: }
352: }
353: }
354:
355: protected void print(String str, int off, int len)
356: throws IOException {
357: if (len == 0)
358: return;
359:
360: allocateCharBufferIfNeeded();
361:
362: checkErrorOrFail();
363:
364: if (_charBuffer == null) {
365: _isCommitted = true;
366: super .print(str, off, len);
367: } else {
368: if (!_charBuffer.print(str, off, len)) {
369: flushBuffer();
370: _isCommitted = true;
371: super .print(str, off, len);
372: }
373: }
374: }
375:
376: protected void print(char c) throws IOException {
377: allocateCharBufferIfNeeded();
378:
379: checkErrorOrFail();
380:
381: if (_charBuffer == null) {
382: _isCommitted = true;
383: super .print(c);
384: } else {
385: if (!_charBuffer.print(c)) {
386: flushBuffer();
387: _isCommitted = true;
388: super .print(c);
389: }
390: }
391: }
392:
393: protected void write(byte[] buf, int off, int len)
394: throws IOException {
395: if (len == 0)
396: return;
397:
398: allocateByteBufferIfNeeded();
399:
400: checkErrorOrFail();
401:
402: if (_byteBuffer == null) {
403: _isCommitted = true;
404: super .write(buf, off, len);
405: } else {
406: if (!_byteBuffer.write(buf, off, len)) {
407: flushBuffer();
408: _isCommitted = true;
409: super .write(buf, off, len);
410: }
411: }
412: }
413:
414: protected void write(byte b) throws IOException {
415: checkErrorOrFail();
416:
417: allocateByteBufferIfNeeded();
418:
419: if (_byteBuffer == null) {
420: _isCommitted = true;
421: super .write(b);
422: } else {
423: if (!_byteBuffer.write(b)) {
424: flushBuffer();
425: _isCommitted = true;
426: super.write(b);
427: }
428: }
429: }
430: }
|