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.util.Map;
021    
022    import org.apache.camel.Exchange;
023    import org.apache.camel.WrappedFile;
024    import org.apache.camel.util.FileUtil;
025    import org.apache.camel.util.ObjectHelper;
026    import org.slf4j.Logger;
027    import org.slf4j.LoggerFactory;
028    
029    /**
030     * Generic File. Specific implementations of a file based endpoint need to
031     * provide a File for transfer.
032     */
033    public class GenericFile<T> implements WrappedFile<T>  {
034        private static final Logger LOG = LoggerFactory.getLogger(GenericFile.class);
035    
036        private String endpointPath;
037        private String fileName;
038        private String fileNameOnly;
039        private String relativeFilePath;
040        private String absoluteFilePath;
041        private long fileLength;
042        private long lastModified;
043        private T file;
044        private GenericFileBinding<T> binding;
045        private boolean absolute;
046        private boolean directory;
047        private String charset;
048    
049        public char getFileSeparator() {
050            return File.separatorChar;
051        }
052    
053        /**
054         * Creates a copy based on the source
055         *
056         * @param source the source
057         * @return a copy of the source
058         */
059        @SuppressWarnings("unchecked")
060        public GenericFile<T> copyFrom(GenericFile<T> source) {
061            GenericFile<T> result;
062            try {
063                result = source.getClass().newInstance();
064            } catch (Exception e) {
065                throw ObjectHelper.wrapRuntimeCamelException(e);
066            }
067            result.setEndpointPath(source.getEndpointPath());
068            result.setAbsolute(source.isAbsolute());
069            result.setDirectory(source.isDirectory());
070            result.setAbsoluteFilePath(source.getAbsoluteFilePath());
071            result.setRelativeFilePath(source.getRelativeFilePath());
072            result.setFileName(source.getFileName());
073            result.setFileNameOnly(source.getFileNameOnly());
074            result.setFileLength(source.getFileLength());
075            result.setLastModified(source.getLastModified());
076            result.setFile(source.getFile());
077            result.setBody(source.getBody());
078            result.setBinding(source.getBinding());
079            result.setCharset(source.getCharset());
080    
081            copyFromPopulateAdditional(source, result);
082            return result;
083        }
084    
085        /**
086         * Copies additional information from the source to the result.
087         * <p/>
088         * Inherited classes can override this method and copy their specific data.
089         *
090         * @param source  the source
091         * @param result  the result
092         */
093        public void copyFromPopulateAdditional(GenericFile<T> source, GenericFile<T> result) {
094            // noop
095        }
096    
097        /**
098         * Bind this GenericFile to an Exchange
099         */
100        public void bindToExchange(Exchange exchange) {
101            Map<String, Object> headers;
102    
103            exchange.setProperty(FileComponent.FILE_EXCHANGE_FILE, this);
104            GenericFileMessage<T> msg = new GenericFileMessage<T>(this);
105            if (exchange.hasOut()) {
106                headers = exchange.getOut().hasHeaders() ? exchange.getOut().getHeaders() : null;
107                exchange.setOut(msg);
108            } else {
109                headers = exchange.getIn().hasHeaders() ? exchange.getIn().getHeaders() : null;
110                exchange.setIn(msg);
111            }
112    
113            // preserve any existing (non file) headers, before we re-populate headers
114            if (headers != null) {
115                msg.setHeaders(headers);
116                // remove any file related headers, as we will re-populate file headers
117                msg.removeHeaders("CamelFile*");
118            }
119            populateHeaders(msg);
120        }
121    
122        /**
123         * Populates the {@link GenericFileMessage} relevant headers
124         *
125         * @param message the message to populate with headers
126         */
127        public void populateHeaders(GenericFileMessage<T> message) {
128            if (message != null) {
129                message.setHeader(Exchange.FILE_NAME_ONLY, getFileNameOnly());
130                message.setHeader(Exchange.FILE_NAME, getFileName());
131                message.setHeader(Exchange.FILE_NAME_CONSUMED, getFileName());
132                message.setHeader("CamelFileAbsolute", isAbsolute());
133                message.setHeader("CamelFileAbsolutePath", getAbsoluteFilePath());
134        
135                if (isAbsolute()) {
136                    message.setHeader(Exchange.FILE_PATH, getAbsoluteFilePath());
137                } else {
138                    // we must normalize path according to protocol if we build our own paths
139                    String path = normalizePathToProtocol(getEndpointPath() + File.separator + getRelativeFilePath());
140                    message.setHeader(Exchange.FILE_PATH, path);
141                }
142        
143                message.setHeader("CamelFileRelativePath", getRelativeFilePath());
144                message.setHeader(Exchange.FILE_PARENT, getParent());
145        
146                if (getFileLength() >= 0) {
147                    message.setHeader(Exchange.FILE_LENGTH, getFileLength());
148                }
149                if (getLastModified() > 0) {
150                    message.setHeader(Exchange.FILE_LAST_MODIFIED, getLastModified());
151                }
152            }
153        }
154        
155        protected boolean isAbsolute(String name) {
156            return FileUtil.isAbsolute(new File(name));
157        }
158        
159        protected String normalizePath(String name) {
160            return FileUtil.normalizePath(name);
161        }
162       
163        /**
164         * Changes the name of this remote file. This method alters the absolute and
165         * relative names as well.
166         *
167         * @param newName the new name
168         */
169        public void changeFileName(String newName) {
170            LOG.trace("Changing name to: {}", newName);
171    
172            // Make sure the names is normalized.
173            String newFileName = FileUtil.normalizePath(newName);
174            String newEndpointPath = FileUtil.normalizePath(endpointPath);
175    
176            LOG.trace("Normalized endpointPath: {}", newEndpointPath);
177            LOG.trace("Normalized newFileName: ()", newFileName);
178    
179            File file = new File(newFileName);
180            if (!absolute) {
181                // for relative then we should avoid having the endpoint path duplicated so clip it
182                if (ObjectHelper.isNotEmpty(newEndpointPath) && newFileName.startsWith(newEndpointPath)) {
183                    // clip starting endpoint in case it was added
184                    // use File.separatorChar as the normalizePath uses this as path separator so we should use the same
185                    // in this logic here
186                    if (newEndpointPath.endsWith("" + File.separatorChar)) {
187                        newFileName = ObjectHelper.after(newFileName, newEndpointPath);
188                    } else {
189                        newFileName = ObjectHelper.after(newFileName, newEndpointPath + File.separatorChar);
190                    }
191    
192                    // reconstruct file with clipped name
193                    file = new File(newFileName);
194                }
195            }
196    
197            // store the file name only
198            setFileNameOnly(file.getName());
199            setFileName(file.getName());
200    
201            // relative path
202            if (file.getParent() != null) {
203                setRelativeFilePath(file.getParent() + getFileSeparator() + file.getName());
204            } else {
205                setRelativeFilePath(file.getName());
206            }
207    
208            // absolute path
209            if (isAbsolute(newFileName)) {
210                setAbsolute(true);
211                setAbsoluteFilePath(newFileName);
212            } else {
213                setAbsolute(false);
214                // construct a pseudo absolute filename that the file operations uses even for relative only
215                String path = ObjectHelper.isEmpty(endpointPath) ? "" : endpointPath + getFileSeparator();
216                setAbsoluteFilePath(path + getRelativeFilePath());
217            }
218    
219            if (LOG.isTraceEnabled()) {
220                LOG.trace("FileNameOnly: {}", getFileNameOnly());
221                LOG.trace("FileName: {}", getFileName());
222                LOG.trace("Absolute: {}", isAbsolute());
223                LOG.trace("Relative path: {}", getRelativeFilePath());
224                LOG.trace("Absolute path: {}", getAbsoluteFilePath());
225                LOG.trace("Name changed to: {}", this);
226            }
227        }
228    
229        public String getRelativeFilePath() {
230            return relativeFilePath;
231        }
232    
233        public void setRelativeFilePath(String relativeFilePath) {
234            this.relativeFilePath = normalizePathToProtocol(relativeFilePath);
235        }
236    
237        public String getFileName() {
238            return fileName;
239        }
240    
241        public void setFileName(String fileName) {
242            this.fileName = normalizePathToProtocol(fileName);
243        }
244    
245        public long getFileLength() {
246            return fileLength;
247        }
248    
249        public void setFileLength(long fileLength) {
250            this.fileLength = fileLength;
251        }
252    
253        public long getLastModified() {
254            return lastModified;
255        }
256    
257        public void setLastModified(long lastModified) {
258            this.lastModified = lastModified;
259        }
260    
261        public String getCharset() {
262            return charset;
263        }
264    
265        public void setCharset(String charset) {
266            this.charset = charset;
267        }
268    
269        @Override
270        public T getFile() {
271            return file;
272        }
273    
274        public void setFile(T file) {
275            this.file = file;
276        }
277    
278        public Object getBody() {
279            return getBinding().getBody(this);
280        }
281    
282        public void setBody(Object os) {
283            getBinding().setBody(this, os);
284        }
285    
286        public String getParent() {
287            String parent;
288            if (isAbsolute()) {
289                String name = getAbsoluteFilePath();
290                File path = new File(name);
291                parent = path.getParent();
292            } else {
293                String name = getRelativeFilePath();
294                File path;
295                if (name != null) {
296                    path = new File(endpointPath, name);
297                } else {
298                    path = new File(endpointPath);
299                }
300                parent = path.getParent();
301            }
302            return normalizePathToProtocol(parent);
303        }
304    
305        public GenericFileBinding<T> getBinding() {
306            if (binding == null) {
307                binding = new GenericFileDefaultBinding<T>();
308            }
309            return binding;
310        }
311    
312        public void setBinding(GenericFileBinding<T> binding) {
313            this.binding = binding;
314        }
315    
316        public void setAbsoluteFilePath(String absoluteFilePath) {
317            this.absoluteFilePath = normalizePathToProtocol(absoluteFilePath);
318        }
319    
320        public String getAbsoluteFilePath() {
321            return absoluteFilePath;
322        }
323    
324        public boolean isAbsolute() {
325            return absolute;
326        }
327    
328        public void setAbsolute(boolean absolute) {
329            this.absolute = absolute;
330        }
331    
332        public String getEndpointPath() {
333            return endpointPath;
334        }
335    
336        public void setEndpointPath(String endpointPath) {
337            this.endpointPath = normalizePathToProtocol(endpointPath);
338        }
339    
340        public String getFileNameOnly() {
341            return fileNameOnly;
342        }
343    
344        public void setFileNameOnly(String fileNameOnly) {
345            this.fileNameOnly = fileNameOnly;
346        }
347    
348        public boolean isDirectory() {
349            return directory;
350        }
351    
352        public void setDirectory(boolean directory) {
353            this.directory = directory;
354        }
355    
356        /**
357         * Fixes the path separator to be according to the protocol
358         */
359        protected String normalizePathToProtocol(String path) {
360            if (ObjectHelper.isEmpty(path)) {
361                return path;
362            }
363            path = path.replace('/', getFileSeparator());
364            path = path.replace('\\', getFileSeparator());
365            return path;
366        }
367    
368        @Override
369        public String toString() {
370            return "GenericFile[" + (absolute ? absoluteFilePath : relativeFilePath) + "]";
371        }
372    }