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 }