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