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     */
017    package org.apache.camel.component.file;
018    
019    import java.io.File;
020    import java.io.FileInputStream;
021    import java.io.FileOutputStream;
022    import java.io.IOException;
023    import java.io.InputStream;
024    import java.io.RandomAccessFile;
025    import java.nio.ByteBuffer;
026    import java.nio.channels.FileChannel;
027    
028    import org.apache.camel.Exchange;
029    import org.apache.camel.Expression;
030    import org.apache.camel.Message;
031    import org.apache.camel.impl.DefaultProducer;
032    import org.apache.camel.language.simple.FileLanguage;
033    import org.apache.camel.util.ExchangeHelper;
034    import org.apache.camel.util.ObjectHelper;
035    import org.apache.commons.logging.Log;
036    import org.apache.commons.logging.LogFactory;
037    
038    /**
039     * For producing files.
040     *
041     * @version $Revision: 47012 $
042     */
043    public class FileProducer extends DefaultProducer {
044        private static final transient Log LOG = LogFactory.getLog(FileProducer.class);
045        private FileEndpoint endpoint;
046    
047        public FileProducer(FileEndpoint endpoint) {
048            super(endpoint);
049            this.endpoint = endpoint;
050        }
051    
052        /**
053         * @deprecated will be removed in Camel 2.0.
054         */
055        public FileEndpoint getEndpoint() {
056            return endpoint;
057        }
058    
059        public void process(Exchange exchange) throws Exception {
060            FileExchange fileExchange = endpoint.createExchange(exchange);
061            process(fileExchange);
062            ExchangeHelper.copyResults(exchange, fileExchange);
063        }
064    
065        public void process(FileExchange exchange) throws Exception {
066            boolean fileSource = exchange.getIn().getBody() instanceof File;
067            File target = createFileName(exchange.getIn());
068            buildDirectory(target);
069    
070            if (LOG.isDebugEnabled()) {
071                LOG.debug("About to write to: " + target + " from exchange: " + exchange);
072            }
073    
074            if (fileSource) {
075                File source = ExchangeHelper.getMandatoryInBody(exchange, File.class);
076                writeFileByFile(source, target);
077            } else {
078                InputStream in = ExchangeHelper.getMandatoryInBody(exchange, InputStream.class);
079                writeFileByStream(in, target);
080            }
081        }
082    
083        private void writeFileByFile(File source, File target) throws IOException {
084            FileChannel in = new FileInputStream(source).getChannel();
085            FileChannel out = null;
086            try {
087                out = prepareOutputFileChannel(target, out);
088    
089                if (LOG.isTraceEnabled()) {
090                    LOG.trace("Using FileChannel to transfer from: " + in + " to: " + out);
091                }
092                in.transferTo(0, in.size(), out);
093            } finally {
094                ObjectHelper.close(in, source.getName(), LOG);
095                ObjectHelper.close(out, source.getName(), LOG);
096            }
097        }
098    
099        private void writeFileByStream(InputStream in, File target) throws IOException {
100            FileChannel out = null;
101            try {
102                out = prepareOutputFileChannel(target, out);
103    
104                if (LOG.isTraceEnabled()) {
105                    LOG.trace("Using InputStream to transfer from: " + in + " to: " + out);
106                }
107                int size = endpoint.getBufferSize();
108                byte[] buffer = new byte[size];
109                ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
110                while (true) {
111                    int count = in.read(buffer);
112                    if (count <= 0) {
113                        break;
114                    } else if (count < size) {
115                        byteBuffer = ByteBuffer.wrap(buffer, 0, count);
116                        out.write(byteBuffer);
117                        break;
118                    } else {
119                        out.write(byteBuffer);
120                        byteBuffer.clear();
121                    }
122                }
123            } finally {
124                ObjectHelper.close(in, target.getName(), LOG);
125                ObjectHelper.close(out, target.getName(), LOG);
126            }
127        }
128    
129        /**
130         * Creates and prepares the output file channel. Will position itself in correct position if eg. it should append
131         * or override any existing content.
132         */
133        private FileChannel prepareOutputFileChannel(File target, FileChannel out) throws IOException {
134            if (endpoint.isAppend()) {
135                out = new RandomAccessFile(target, "rw").getChannel();
136                out = out.position(out.size());
137            } else {
138                out = new FileOutputStream(target).getChannel();
139            }
140            return out;
141        }
142    
143        protected File createFileName(Message message) {
144            File answer;
145    
146            String name = null;
147            if (!endpoint.isIgnoreFileNameHeader()) {
148                name = message.getHeader(FileComponent.HEADER_FILE_NAME, String.class);
149            }
150    
151            // expression support
152            Expression expression = endpoint.getExpression();
153            if (name != null) {
154                // the header name can be an expression too, that should override whatever configured on the endpoint
155                if (name.indexOf("${") > -1) {
156                    if (LOG.isDebugEnabled()) {
157                        LOG.debug(FileComponent.HEADER_FILE_NAME + " contains a FileLanguage expression: " + name);
158                    }
159                    expression = FileLanguage.file(name);
160                }
161            }
162            if (expression != null) {
163                if (LOG.isDebugEnabled()) {
164                    LOG.debug("Filename evaluated as expression: " + expression);
165                }
166                Object result = expression.evaluate(message.getExchange());
167                name = message.getExchange().getContext().getTypeConverter().convertTo(String.class, result);
168            }
169    
170            File endpointFile = endpoint.getFile();
171            if (endpointFile.isDirectory()) {
172                if (name != null) {
173                    answer = new File(endpointFile, name);
174                    if (answer.isDirectory()) {
175                        answer = new File(answer, endpoint.getGeneratedFileName(message));
176                    }
177                } else {
178                    answer = new File(endpointFile, endpoint.getGeneratedFileName(message));
179                }
180            } else {
181                if (name == null) {
182                    answer = endpointFile;
183                } else {
184                    answer = new File(endpointFile, name);
185                }
186            }
187    
188            // lets store the name we really used in the header, so end-users can retrieve it
189            message.setHeader(FileComponent.HEADER_FILE_NAME_PRODUCED, answer.getAbsolutePath());
190    
191            return answer;
192        }
193    
194        private void buildDirectory(File file) {
195            String dirName = file.getAbsolutePath();
196            int index = dirName.lastIndexOf(File.separatorChar);
197            if (index > 0) {
198                dirName = dirName.substring(0, index);
199                File dir = new File(dirName);
200                dir.mkdirs();
201            }
202        }
203    
204    }