001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.converter.stream;
018
019import java.io.BufferedInputStream;
020import java.io.File;
021import java.io.FileInputStream;
022import java.io.FileNotFoundException;
023import java.io.IOException;
024import java.io.InputStream;
025import java.io.OutputStream;
026import java.nio.channels.Channels;
027import java.nio.channels.FileChannel;
028import java.nio.channels.WritableByteChannel;
029import javax.crypto.CipherInputStream;
030
031import org.apache.camel.RuntimeCamelException;
032import org.apache.camel.StreamCache;
033import org.apache.camel.util.IOHelper;
034
035/**
036 * A {@link StreamCache} for {@link File}s
037 */
038public final class FileInputStreamCache extends InputStream implements StreamCache {
039    private InputStream stream;
040    private final File file;
041    private final CipherPair ciphers;
042    private final long length;
043
044    public FileInputStreamCache(File file) throws FileNotFoundException {
045        this(file, null);
046    }
047    
048    FileInputStreamCache(File file, CipherPair ciphers) throws FileNotFoundException {
049        this.file = file;
050        this.stream = null;
051        this.ciphers = ciphers;
052        this.length = file.length();
053    }
054    
055    @Override
056    public void close() {
057        if (stream != null) {
058            IOHelper.close(stream);
059        }
060    }
061
062    @Override
063    public void reset() {
064        // reset by closing and creating a new stream based on the file
065        close();
066        // reset by creating a new stream based on the file
067        stream = null;
068        
069        if (!file.exists()) {
070            throw new RuntimeCamelException("Cannot reset stream from file " + file);
071        }
072    }
073
074    public void writeTo(OutputStream os) throws IOException {
075        if (stream == null && ciphers == null) {
076            FileInputStream s = new FileInputStream(file);
077            long len = file.length();
078            WritableByteChannel out;
079            if (os instanceof WritableByteChannel) {
080                out = (WritableByteChannel)os;
081            } else {
082                out = Channels.newChannel(os);
083            }
084            FileChannel fc = s.getChannel();
085            long pos = 0;
086            while (pos < len) {
087                long i = fc.transferTo(pos, len - pos, out);
088                pos += i;
089            }
090            s.close();
091            fc.close();
092        } else {
093            IOHelper.copy(getInputStream(), os);
094        }
095    }
096
097    public boolean inMemory() {
098        return false;
099    }
100
101    public long length() {
102        return length;
103    }
104
105    @Override
106    public int available() throws IOException {
107        return getInputStream().available();
108    }
109
110    @Override
111    public int read() throws IOException {
112        return getInputStream().read();
113    }
114
115    protected InputStream getInputStream() throws IOException {
116        if (stream == null) {
117            stream = createInputStream(file);
118        }
119        return stream;
120    }
121
122    private InputStream createInputStream(File file) throws IOException {
123        InputStream in = new BufferedInputStream(new FileInputStream(file));
124        if (ciphers != null) {
125            in = new CipherInputStream(in, ciphers.getDecryptor()) {
126                boolean closed;
127                public void close() throws IOException {
128                    if (!closed) {
129                        super.close();
130                        closed = true;
131                    }
132                }
133            };
134        }
135        return in;
136    }
137
138}