001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */package org.apache.cxf.io;
019:
020: import java.io.BufferedOutputStream;
021: import java.io.ByteArrayInputStream;
022: import java.io.ByteArrayOutputStream;
023: import java.io.File;
024: import java.io.FileInputStream;
025: import java.io.FileNotFoundException;
026: import java.io.FileOutputStream;
027: import java.io.IOException;
028: import java.io.InputStream;
029: import java.io.OutputStream;
030: import java.io.OutputStreamWriter;
031: import java.io.PipedInputStream;
032: import java.io.PipedOutputStream;
033: import java.util.ArrayList;
034: import java.util.Collections;
035: import java.util.List;
036:
037: import org.apache.cxf.common.util.Base64Utility;
038:
039: public class CachedOutputStream extends OutputStream {
040:
041: protected OutputStream currentStream;
042:
043: private long threshold = 64 * 1024;
044:
045: private int totalLength;
046:
047: private boolean inmem;
048:
049: private File tempFile;
050:
051: private File outputDir;
052:
053: private List<CachedOutputStreamCallback> callbacks;
054:
055: public CachedOutputStream(PipedInputStream stream)
056: throws IOException {
057: currentStream = new PipedOutputStream(stream);
058: inmem = true;
059: }
060:
061: public CachedOutputStream() {
062: currentStream = new ByteArrayOutputStream(2048);
063: inmem = true;
064: }
065:
066: public void registerCallback(CachedOutputStreamCallback cb) {
067: if (null == callbacks) {
068: callbacks = new ArrayList<CachedOutputStreamCallback>();
069: }
070: callbacks.add(cb);
071: }
072:
073: public void deregisterCallback(CachedOutputStreamCallback cb) {
074: if (null != callbacks) {
075: callbacks.remove(cb);
076: }
077: }
078:
079: public List<CachedOutputStreamCallback> getCallbacks() {
080: return callbacks == null ? null : Collections
081: .unmodifiableList(callbacks);
082: }
083:
084: /**
085: * Perform any actions required on stream flush (freeze headers, reset
086: * output stream ... etc.)
087: */
088: protected void doFlush() throws IOException {
089:
090: }
091:
092: public void flush() throws IOException {
093: currentStream.flush();
094: if (null != callbacks) {
095: for (CachedOutputStreamCallback cb : callbacks) {
096: cb.onFlush(this );
097: }
098: }
099: doFlush();
100: }
101:
102: /**
103: * Perform any actions required on stream closure (handle response etc.)
104: */
105: protected void doClose() throws IOException {
106:
107: }
108:
109: public void close() throws IOException {
110: currentStream.flush();
111: if (null != callbacks) {
112: for (CachedOutputStreamCallback cb : callbacks) {
113: cb.onClose(this );
114: }
115: }
116:
117: currentStream.close();
118: dispose();
119: doClose();
120: }
121:
122: public boolean equals(Object obj) {
123: return currentStream.equals(obj);
124: }
125:
126: /**
127: * Replace the original stream with the new one, optionally copying the content of the old one
128: * into the new one.
129: * When with Attachment, needs to replace the xml writer stream with the stream used by
130: * AttachmentSerializer or copy the cached output stream to the "real"
131: * output stream, i.e. onto the wire.
132: *
133: * @param out the new output stream
134: * @param copyOldContent flag indicating if the old content should be copied
135: * @throws IOException
136: */
137: public void resetOut(OutputStream out, boolean copyOldContent)
138: throws IOException {
139: if (currentStream instanceof CachedOutputStream) {
140: CachedOutputStream ac = (CachedOutputStream) currentStream;
141: InputStream in = ac.getInputStream();
142: copyStream(in, out, (int) threshold);
143: } else {
144: if (inmem) {
145: if (currentStream instanceof ByteArrayOutputStream) {
146: ByteArrayOutputStream byteOut = (ByteArrayOutputStream) currentStream;
147: if (copyOldContent && byteOut.size() > 0) {
148: byteOut.writeTo(out);
149: }
150: } else if (currentStream instanceof PipedOutputStream) {
151: PipedOutputStream pipeOut = (PipedOutputStream) currentStream;
152: copyStream(new PipedInputStream(pipeOut), out,
153: (int) threshold);
154: } else {
155: throw new IOException(
156: "Unknown format of currentStream");
157: }
158: } else {
159: // read the file
160: currentStream.close();
161: FileInputStream fin = new FileInputStream(tempFile);
162: if (copyOldContent) {
163: copyStream(fin, out, (int) threshold);
164: }
165: }
166: }
167: currentStream = out;
168: }
169:
170: public static void copyStream(InputStream in, OutputStream out,
171: int bufferSize) throws IOException {
172: byte[] buffer = new byte[bufferSize];
173: try {
174: int n = in.read(buffer);
175: while (n > 0) {
176: out.write(buffer, 0, n);
177: n = in.read(buffer);
178: }
179: } finally {
180: in.close();
181: }
182: }
183:
184: public static void copyStreamWithBase64Encoding(InputStream in,
185: OutputStream out, int bufferSize) throws Exception {
186: OutputStreamWriter osw = new OutputStreamWriter(out);
187: byte[] buffer = new byte[bufferSize];
188: try {
189: int n = in.read(buffer, 0, bufferSize);
190: while (n > 0) {
191: Base64Utility.encode(buffer, 0, n, osw);
192: n = in.read(buffer, 0, bufferSize);
193: }
194: } finally {
195: in.close();
196: }
197: }
198:
199: /**
200: * @return the underlying output stream
201: */
202: public OutputStream getOut() {
203: return currentStream;
204: }
205:
206: public int hashCode() {
207: return currentStream.hashCode();
208: }
209:
210: public String toString() {
211: return new StringBuilder().append("[").append(super .toString())
212: .append(" Content: ").append(currentStream.toString())
213: .append("]").toString();
214: }
215:
216: protected void onWrite() throws IOException {
217:
218: }
219:
220: public void write(byte[] b, int off, int len) throws IOException {
221: onWrite();
222: this .totalLength += len;
223: if (inmem && totalLength > threshold
224: && currentStream instanceof ByteArrayOutputStream) {
225: createFileOutputStream();
226: }
227: currentStream.write(b, off, len);
228: }
229:
230: public void write(byte[] b) throws IOException {
231: onWrite();
232: this .totalLength += b.length;
233: if (inmem && totalLength > threshold
234: && currentStream instanceof ByteArrayOutputStream) {
235: createFileOutputStream();
236: }
237: currentStream.write(b);
238: }
239:
240: public void write(int b) throws IOException {
241: onWrite();
242: this .totalLength++;
243: if (inmem && totalLength > threshold
244: && currentStream instanceof ByteArrayOutputStream) {
245: createFileOutputStream();
246: }
247: currentStream.write(b);
248: }
249:
250: private void createFileOutputStream() throws IOException {
251: byte[] bytes = ((ByteArrayOutputStream) currentStream)
252: .toByteArray();
253: if (outputDir == null) {
254: tempFile = File.createTempFile("att", "tmp");
255: } else {
256: tempFile = File.createTempFile("att", "tmp", outputDir);
257: }
258: tempFile.deleteOnExit();
259: currentStream = new BufferedOutputStream(new FileOutputStream(
260: tempFile));
261: currentStream.write(bytes);
262: inmem = false;
263: }
264:
265: public File getTempFile() {
266: return tempFile;
267: }
268:
269: public InputStream getInputStream() throws IOException {
270: flush();
271: if (inmem) {
272: if (currentStream instanceof ByteArrayOutputStream) {
273: return new ByteArrayInputStream(
274: ((ByteArrayOutputStream) currentStream)
275: .toByteArray());
276: } else if (currentStream instanceof PipedOutputStream) {
277: return new PipedInputStream(
278: (PipedOutputStream) currentStream);
279: } else {
280: return null;
281: }
282: } else {
283: try {
284: return new FileInputStream(tempFile) {
285: public void close() throws IOException {
286: super .close();
287: tempFile.delete();
288: currentStream = new ByteArrayOutputStream();
289: inmem = true;
290: }
291: };
292: } catch (FileNotFoundException e) {
293: throw new IOException("Cached file was deleted, "
294: + e.toString());
295: }
296: }
297: }
298:
299: public void dispose() {
300: if (!inmem) {
301: tempFile.delete();
302: }
303: }
304:
305: public void setOutputDir(File outputDir) throws IOException {
306: this .outputDir = outputDir;
307: createFileOutputStream();
308: }
309:
310: public void setThreshold(long threshold) {
311: this.threshold = threshold;
312: }
313: }
|