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