001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.commons.io;
020
021import java.io.ByteArrayInputStream;
022import java.io.File;
023import java.io.IOException;
024import java.io.InputStream;
025import java.io.OutputStream;
026
027import lucee.commons.io.res.Resource;
028import lucee.commons.io.res.util.ResourceUtil;
029import lucee.runtime.engine.ThreadLocalPageContext;
030
031public final class TemporaryStream extends OutputStream {
032
033        private static final int MAX_MEMORY = 1024*1024;
034        private static int index=1;
035        private static Resource tempFile;
036        
037        private Resource persis;
038        private long count=0;
039        private OutputStream os;
040        public boolean memoryMode=true;
041        public boolean available=false;
042        
043        /**
044         * Constructor of the class
045         */
046        public TemporaryStream() {
047                do {
048                this.persis=getTempDirectory().getRealResource("temporary-stream-"+(index++));
049                }
050                while(persis.exists());
051                os=new java.io.ByteArrayOutputStream();
052        }
053        
054        @Override
055        public void write(int b) throws IOException {
056                count++;
057                check();
058                os.write(b);
059        }
060        
061        @Override
062        public void write(byte[] b, int off, int len) throws IOException {
063                count+=len;
064                check();
065                os.write(b, off, len);
066        }
067
068        @Override
069        public void write(byte[] b) throws IOException {
070                count+=b.length;
071                check();
072                os.write(b);
073        }
074
075        private void check() throws IOException {
076                if(memoryMode && count>=MAX_MEMORY && os instanceof java.io.ByteArrayOutputStream) {
077                        memoryMode=false;
078                        OutputStream nos = persis.getOutputStream();
079                        nos.write(((java.io.ByteArrayOutputStream)os).toByteArray());
080                        os=nos;
081                }
082        }
083
084
085        @Override
086        public void close() throws IOException {
087                os.close();
088                available=true;
089        }
090
091        @Override
092        public void flush() throws IOException {
093                os.flush();
094        }
095
096        public InputStream getInputStream() throws IOException {
097                return new InpuStreamWrap(this);
098        }
099        
100        class InpuStreamWrap extends InputStream {
101
102                private TemporaryStream ts;
103                private InputStream is;
104
105                public InpuStreamWrap(TemporaryStream ts) throws IOException {
106                        this.ts=ts;
107                        if(ts.os instanceof java.io.ByteArrayOutputStream) {
108                                is=new ByteArrayInputStream(((java.io.ByteArrayOutputStream)ts.os).toByteArray());
109                        }
110                        else if(ts.available) {
111                                ts.available=false;
112                                try {
113                                        is=ts.persis.getInputStream();
114                                } catch (IOException e) {
115                                        ts.persis.delete();
116                                        throw e;
117                                }
118                        }
119                        else 
120                                throw new IOException("InputStream no longer available");
121                }
122                
123                @Override
124                public int read() throws IOException {
125                        return is.read();
126                }
127
128                @Override
129                public int available() throws IOException {
130                        return is.available();
131                }
132
133                @Override
134                public void close() throws IOException {
135                        ts.persis.delete();
136                        is.close();
137                }
138
139                @Override
140                public synchronized void mark(int readlimit) {
141                        is.mark(readlimit);
142                }
143
144                @Override
145                public boolean markSupported() {
146                        return is.markSupported();
147                }
148
149                @Override
150                public int read(byte[] b, int off, int len) throws IOException {
151                        return is.read(b, off, len);
152                }
153
154                @Override
155                public int read(byte[] b) throws IOException {
156                        return is.read(b);
157                }
158
159                @Override
160                public synchronized void reset() throws IOException {
161                        is.reset();
162                }
163
164                @Override
165                public long skip(long n) throws IOException {
166                        return is.skip(n);
167                }
168        }
169
170        public long length() {
171                return count;
172        }
173        
174        public static Resource getTempDirectory() {
175        if(tempFile!=null) return tempFile;
176        String tmpStr = System.getProperty("java.io.tmpdir");
177        if(tmpStr!=null) {
178                
179                tempFile=ResourceUtil.toResourceNotExisting(ThreadLocalPageContext.get(), tmpStr);
180                //tempFile=CFMLEngineFactory.getInstance().getCastUtil().toResource(tmpStr,null);
181            
182            if(tempFile!=null && tempFile.exists()) {
183                tempFile=getCanonicalResourceEL(tempFile);
184                return tempFile;
185            }
186        }
187        File tmp =null;
188        try {
189                tmp = File.createTempFile("a","a");
190                tempFile=ResourceUtil.toResourceNotExisting(ThreadLocalPageContext.get(), tmp.getParent());
191                //tempFile=CFMLEngineFactory.getInstance().getCastUtil().toResource(tmp.getParent(),null);
192            tempFile=getCanonicalResourceEL(tempFile);   
193        }
194        catch(IOException ioe) {}
195        finally {
196                if(tmp!=null)tmp.delete();
197        }
198        return tempFile;
199    }
200        
201        private static Resource getCanonicalResourceEL(Resource res) {
202                try {
203                        return res.getCanonicalResource();
204                } catch (IOException e) {
205                        return res.getAbsoluteResource();
206                }
207        }
208}