001/* 002 * Copyright (c) 2006-2014 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 * 009 * Contributors: 010 * Nuxeo - initial API and implementation 011 * 012 */ 013 014package org.nuxeo.ecm.core.api; 015 016import java.io.BufferedInputStream; 017import java.io.ByteArrayInputStream; 018import java.io.File; 019import java.io.FileInputStream; 020import java.io.FileOutputStream; 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.ObjectInputStream; 024import java.io.ObjectOutputStream; 025import java.io.OutputStream; 026import java.io.Serializable; 027 028import org.apache.commons.io.IOUtils; 029import org.nuxeo.runtime.api.Framework; 030 031/** 032 * A serializable input stream. 033 * <p> 034 * Note: The stream is closed after the object is serialized. 035 * 036 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 037 */ 038public class SerializableInputStream extends InputStream implements Serializable { 039 040 public static final int IN_MEM_LIMIT = 1024 * 64; 041 042 private static final long serialVersionUID = -2816387281878881614L; 043 044 private transient File file; 045 046 private transient InputStream in; 047 048 public SerializableInputStream(InputStream in) { 049 this.in = in; 050 } 051 052 public SerializableInputStream(byte[] content) { 053 in = new ByteArrayInputStream(content); 054 } 055 056 public SerializableInputStream(String content) { 057 this(content.getBytes()); 058 } 059 060 @Override 061 public int available() throws IOException { 062 return in.available(); 063 } 064 065 @Override 066 public void close() throws IOException { 067 in.close(); 068 } 069 070 @Override 071 public synchronized void mark(int readlimit) { 072 in.mark(readlimit); 073 } 074 075 @Override 076 public boolean markSupported() { 077 return in.markSupported(); 078 } 079 080 @Override 081 public int read() throws IOException { 082 return in.read(); 083 } 084 085 @Override 086 public int read(byte[] b, int off, int len) throws IOException { 087 return in.read(b, off, len); 088 } 089 090 @Override 091 public int read(byte[] b) throws IOException { 092 return in.read(b); 093 } 094 095 @Override 096 public synchronized void reset() throws IOException { 097 in.reset(); 098 } 099 100 public File getTempFile() { 101 return file; 102 } 103 104 public InputStream reopen() throws IOException { 105 if (!canReopen()) { 106 throw new IOException("Cannot reopen non persistent stream"); 107 } 108 return new BufferedInputStream(new FileInputStream(file)); 109 } 110 111 public boolean canReopen() { 112 return file != null && file.isFile(); 113 } 114 115 @Override 116 public long skip(long n) throws IOException { 117 return in.skip(n); 118 } 119 120 private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException { 121 // always perform the default de-serialization first 122 in.defaultReadObject(); 123 // create a temp file where we will put the blob content 124 file = File.createTempFile("SerializableIS-", ".tmp"); 125 Framework.trackFile(file, file); 126 OutputStream out = null; 127 try { 128 out = new FileOutputStream(file); 129 byte[] buffer = new byte[IN_MEM_LIMIT]; 130 int read; 131 int bytes = in.readInt(); 132 while (bytes > -1 && (read = in.read(buffer, 0, bytes)) != -1) { 133 out.write(buffer, 0, read); 134 bytes -= read; 135 if (bytes == 0) { 136 bytes = in.readInt(); 137 } 138 } 139 } finally { 140 if (out != null) { 141 out.close(); 142 } 143 if (file.isFile()) { 144 this.in = new BufferedInputStream(new FileInputStream(file)); 145 } 146 } 147 } 148 149 private void writeObject(ObjectOutputStream out) throws IOException { 150 out.defaultWriteObject(); 151 // write content 152 if (in == null) { 153 return; 154 } 155 try { 156 int read; 157 byte[] buf = new byte[IN_MEM_LIMIT]; 158 while ((read = in.read(buf)) != -1) { 159 // next follows a chunk of 'read' bytes 160 out.writeInt(read); 161 out.write(buf, 0, read); 162 } 163 out.writeInt(-1); // EOF 164 } finally { 165 if (in != null) { 166 in.close(); 167 } 168 } 169 } 170 171 @Override 172 protected void finalize() throws Throwable { 173 IOUtils.closeQuietly(in); 174 if (file != null) { 175 file.delete(); 176 } 177 super.finalize(); 178 } 179 180}