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