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 */ 017package org.apache.camel.component.file; 018 019import java.io.File; 020import java.nio.file.Files; 021import java.nio.file.Path; 022import java.util.Map; 023 024import org.apache.camel.Exchange; 025import org.apache.camel.WrappedFile; 026import org.apache.camel.util.FileUtil; 027import org.apache.camel.util.ObjectHelper; 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030 031/** 032 * Generic File. Specific implementations of a file based endpoint need to 033 * provide a File for transfer. 034 */ 035public class GenericFile<T> implements WrappedFile<T> { 036 private static final Logger LOG = LoggerFactory.getLogger(GenericFile.class); 037 038 private final boolean probeContentType; 039 040 private String copyFromAbsoluteFilePath; 041 private String endpointPath; 042 private String fileName; 043 private String fileNameOnly; 044 private String relativeFilePath; 045 private String absoluteFilePath; 046 private long fileLength; 047 private long lastModified; 048 private T file; 049 private GenericFileBinding<T> binding; 050 private boolean absolute; 051 private boolean directory; 052 private String charset; 053 private Map<String, Object> extendedAttributes; 054 055 public GenericFile() { 056 this(false); 057 } 058 059 public GenericFile(boolean probeContentType) { 060 this.probeContentType = probeContentType; 061 } 062 063 public char getFileSeparator() { 064 return File.separatorChar; 065 } 066 067 /** 068 * Creates a copy based on the source 069 * 070 * @param source the source 071 * @return a copy of the source 072 */ 073 @SuppressWarnings("unchecked") 074 public GenericFile<T> copyFrom(GenericFile<T> source) { 075 GenericFile<T> result; 076 try { 077 result = source.getClass().newInstance(); 078 } catch (Exception e) { 079 throw ObjectHelper.wrapRuntimeCamelException(e); 080 } 081 result.setCopyFromAbsoluteFilePath(source.getAbsoluteFilePath()); 082 result.setEndpointPath(source.getEndpointPath()); 083 result.setAbsolute(source.isAbsolute()); 084 result.setDirectory(source.isDirectory()); 085 result.setAbsoluteFilePath(source.getAbsoluteFilePath()); 086 result.setRelativeFilePath(source.getRelativeFilePath()); 087 result.setFileName(source.getFileName()); 088 result.setFileNameOnly(source.getFileNameOnly()); 089 result.setFileLength(source.getFileLength()); 090 result.setLastModified(source.getLastModified()); 091 result.setFile(source.getFile()); 092 result.setBody(source.getBody()); 093 result.setBinding(source.getBinding()); 094 result.setCharset(source.getCharset()); 095 096 copyFromPopulateAdditional(source, result); 097 return result; 098 } 099 100 /** 101 * Copies additional information from the source to the result. 102 * <p/> 103 * Inherited classes can override this method and copy their specific data. 104 * 105 * @param source the source 106 * @param result the result 107 */ 108 public void copyFromPopulateAdditional(GenericFile<T> source, GenericFile<T> result) { 109 // noop 110 } 111 112 /** 113 * Bind this GenericFile to an Exchange 114 */ 115 public void bindToExchange(Exchange exchange) { 116 Map<String, Object> headers; 117 118 exchange.setProperty(FileComponent.FILE_EXCHANGE_FILE, this); 119 GenericFileMessage<T> msg = new GenericFileMessage<T>(this); 120 if (exchange.hasOut()) { 121 headers = exchange.getOut().hasHeaders() ? exchange.getOut().getHeaders() : null; 122 exchange.setOut(msg); 123 } else { 124 headers = exchange.getIn().hasHeaders() ? exchange.getIn().getHeaders() : null; 125 exchange.setIn(msg); 126 } 127 128 // preserve any existing (non file) headers, before we re-populate headers 129 if (headers != null) { 130 msg.setHeaders(headers); 131 // remove any file related headers, as we will re-populate file headers 132 msg.removeHeaders("CamelFile*"); 133 } 134 populateHeaders(msg); 135 } 136 137 /** 138 * Populates the {@link GenericFileMessage} relevant headers 139 * 140 * @param message the message to populate with headers 141 */ 142 public void populateHeaders(GenericFileMessage<T> message) { 143 if (message != null) { 144 message.setHeader(Exchange.FILE_NAME_ONLY, getFileNameOnly()); 145 message.setHeader(Exchange.FILE_NAME, getFileName()); 146 message.setHeader(Exchange.FILE_NAME_CONSUMED, getFileName()); 147 message.setHeader("CamelFileAbsolute", isAbsolute()); 148 message.setHeader("CamelFileAbsolutePath", getAbsoluteFilePath()); 149 150 if (extendedAttributes != null) { 151 message.setHeader("CamelFileExtendedAttributes", extendedAttributes); 152 } 153 154 if (probeContentType && file instanceof File) { 155 File f = (File) file; 156 Path path = f.toPath(); 157 try { 158 message.setHeader(Exchange.FILE_CONTENT_TYPE, Files.probeContentType(path)); 159 } catch (Throwable e) { 160 // just ignore the exception 161 } 162 } 163 164 if (isAbsolute()) { 165 message.setHeader(Exchange.FILE_PATH, getAbsoluteFilePath()); 166 } else { 167 // we must normalize path according to protocol if we build our own paths 168 String path = normalizePathToProtocol(getEndpointPath() + File.separator + getRelativeFilePath()); 169 message.setHeader(Exchange.FILE_PATH, path); 170 } 171 172 message.setHeader("CamelFileRelativePath", getRelativeFilePath()); 173 message.setHeader(Exchange.FILE_PARENT, getParent()); 174 175 if (getFileLength() >= 0) { 176 message.setHeader(Exchange.FILE_LENGTH, getFileLength()); 177 } 178 if (getLastModified() > 0) { 179 message.setHeader(Exchange.FILE_LAST_MODIFIED, getLastModified()); 180 } 181 } 182 } 183 184 protected boolean isAbsolute(String name) { 185 return FileUtil.isAbsolute(new File(name)); 186 } 187 188 protected String normalizePath(String name) { 189 return FileUtil.normalizePath(name); 190 } 191 192 /** 193 * Changes the name of this remote file. This method alters the absolute and 194 * relative names as well. 195 * 196 * @param newName the new name 197 */ 198 public void changeFileName(String newName) { 199 LOG.trace("Changing name to: {}", newName); 200 201 // Make sure the names is normalized. 202 String newFileName = FileUtil.normalizePath(newName); 203 String newEndpointPath = FileUtil.normalizePath(endpointPath.endsWith("" + File.separatorChar) ? endpointPath : endpointPath + File.separatorChar); 204 205 LOG.trace("Normalized endpointPath: {}", newEndpointPath); 206 LOG.trace("Normalized newFileName: ()", newFileName); 207 208 File file = new File(newFileName); 209 if (!absolute) { 210 // for relative then we should avoid having the endpoint path duplicated so clip it 211 if (ObjectHelper.isNotEmpty(newEndpointPath) && newFileName.startsWith(newEndpointPath)) { 212 // clip starting endpoint in case it was added 213 // use File.separatorChar as the normalizePath uses this as path separator so we should use the same 214 // in this logic here 215 if (newEndpointPath.endsWith("" + File.separatorChar)) { 216 newFileName = ObjectHelper.after(newFileName, newEndpointPath); 217 } else { 218 newFileName = ObjectHelper.after(newFileName, newEndpointPath + File.separatorChar); 219 } 220 221 // reconstruct file with clipped name 222 file = new File(newFileName); 223 } 224 } 225 226 // store the file name only 227 setFileNameOnly(file.getName()); 228 setFileName(file.getName()); 229 230 // relative path 231 if (file.getParent() != null) { 232 setRelativeFilePath(file.getParent() + getFileSeparator() + file.getName()); 233 } else { 234 setRelativeFilePath(file.getName()); 235 } 236 237 // absolute path 238 if (isAbsolute(newFileName)) { 239 setAbsolute(true); 240 setAbsoluteFilePath(newFileName); 241 } else { 242 setAbsolute(false); 243 // construct a pseudo absolute filename that the file operations uses even for relative only 244 String path = ObjectHelper.isEmpty(endpointPath) ? "" : endpointPath + getFileSeparator(); 245 setAbsoluteFilePath(path + getRelativeFilePath()); 246 } 247 248 if (LOG.isTraceEnabled()) { 249 LOG.trace("FileNameOnly: {}", getFileNameOnly()); 250 LOG.trace("FileName: {}", getFileName()); 251 LOG.trace("Absolute: {}", isAbsolute()); 252 LOG.trace("Relative path: {}", getRelativeFilePath()); 253 LOG.trace("Absolute path: {}", getAbsoluteFilePath()); 254 LOG.trace("Name changed to: {}", this); 255 } 256 } 257 258 public String getRelativeFilePath() { 259 return relativeFilePath; 260 } 261 262 public void setRelativeFilePath(String relativeFilePath) { 263 this.relativeFilePath = normalizePathToProtocol(relativeFilePath); 264 } 265 266 public String getFileName() { 267 return fileName; 268 } 269 270 public void setFileName(String fileName) { 271 this.fileName = normalizePathToProtocol(fileName); 272 } 273 274 public long getFileLength() { 275 return fileLength; 276 } 277 278 public void setFileLength(long fileLength) { 279 this.fileLength = fileLength; 280 } 281 282 public long getLastModified() { 283 return lastModified; 284 } 285 286 public void setLastModified(long lastModified) { 287 this.lastModified = lastModified; 288 } 289 290 public String getCharset() { 291 return charset; 292 } 293 294 public void setCharset(String charset) { 295 this.charset = charset; 296 } 297 298 public Map<String, Object> getExtendedAttributes() { 299 return extendedAttributes; 300 } 301 302 public void setExtendedAttributes(Map<String, Object> extendedAttributes) { 303 this.extendedAttributes = extendedAttributes; 304 } 305 306 @Override 307 public T getFile() { 308 return file; 309 } 310 311 public void setFile(T file) { 312 this.file = file; 313 } 314 315 public Object getBody() { 316 return getBinding().getBody(this); 317 } 318 319 public void setBody(Object os) { 320 getBinding().setBody(this, os); 321 } 322 323 public String getParent() { 324 String parent; 325 if (isAbsolute()) { 326 String name = getAbsoluteFilePath(); 327 File path = new File(name); 328 parent = path.getParent(); 329 } else { 330 String name = getRelativeFilePath(); 331 File path; 332 if (name != null) { 333 path = new File(endpointPath, name); 334 } else { 335 path = new File(endpointPath); 336 } 337 parent = path.getParent(); 338 } 339 return normalizePathToProtocol(parent); 340 } 341 342 public GenericFileBinding<T> getBinding() { 343 if (binding == null) { 344 binding = new GenericFileDefaultBinding<T>(); 345 } 346 return binding; 347 } 348 349 public void setBinding(GenericFileBinding<T> binding) { 350 this.binding = binding; 351 } 352 353 public void setAbsoluteFilePath(String absoluteFilePath) { 354 this.absoluteFilePath = normalizePathToProtocol(absoluteFilePath); 355 } 356 357 public String getAbsoluteFilePath() { 358 return absoluteFilePath; 359 } 360 361 public boolean isAbsolute() { 362 return absolute; 363 } 364 365 public void setAbsolute(boolean absolute) { 366 this.absolute = absolute; 367 } 368 369 public String getEndpointPath() { 370 return endpointPath; 371 } 372 373 public void setEndpointPath(String endpointPath) { 374 this.endpointPath = normalizePathToProtocol(endpointPath); 375 } 376 377 public String getFileNameOnly() { 378 return fileNameOnly; 379 } 380 381 public void setFileNameOnly(String fileNameOnly) { 382 this.fileNameOnly = fileNameOnly; 383 } 384 385 public boolean isDirectory() { 386 return directory; 387 } 388 389 public void setDirectory(boolean directory) { 390 this.directory = directory; 391 } 392 393 public String getCopyFromAbsoluteFilePath() { 394 return copyFromAbsoluteFilePath; 395 } 396 397 public void setCopyFromAbsoluteFilePath(String copyFromAbsoluteFilePath) { 398 this.copyFromAbsoluteFilePath = copyFromAbsoluteFilePath; 399 } 400 401 /** 402 * Fixes the path separator to be according to the protocol 403 */ 404 protected String normalizePathToProtocol(String path) { 405 if (ObjectHelper.isEmpty(path)) { 406 return path; 407 } 408 path = path.replace('/', getFileSeparator()); 409 path = path.replace('\\', getFileSeparator()); 410 return path; 411 } 412 413 @Override 414 public String toString() { 415 return "GenericFile[" + (absolute ? absoluteFilePath : relativeFilePath) + "]"; 416 } 417}