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 }