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 GenericFileMessage<T> msg = commonBindToExchange(exchange); 117 populateHeaders(msg, false); 118 } 119 120 /** 121 * Bind this GenericFile to an Exchange 122 */ 123 public void bindToExchange(Exchange exchange, boolean isProbeContentTypeFromEndpoint) { 124 GenericFileMessage<T> msg = commonBindToExchange(exchange); 125 populateHeaders(msg, isProbeContentTypeFromEndpoint); 126 } 127 128 private GenericFileMessage<T> commonBindToExchange(Exchange exchange) { 129 Map<String, Object> headers; 130 131 exchange.setProperty(FileComponent.FILE_EXCHANGE_FILE, this); 132 GenericFileMessage<T> msg = new GenericFileMessage<T>(this); 133 if (exchange.hasOut()) { 134 headers = exchange.getOut().hasHeaders() ? exchange.getOut().getHeaders() : null; 135 exchange.setOut(msg); 136 } else { 137 headers = exchange.getIn().hasHeaders() ? exchange.getIn().getHeaders() : null; 138 exchange.setIn(msg); 139 } 140 141 // preserve any existing (non file) headers, before we re-populate headers 142 if (headers != null) { 143 msg.setHeaders(headers); 144 // remove any file related headers, as we will re-populate file headers 145 msg.removeHeaders("CamelFile*"); 146 } 147 return msg; 148 } 149 150 /** 151 * Populates the {@link GenericFileMessage} relevant headers 152 * 153 * @param message the message to populate with headers 154 */ 155 public void populateHeaders(GenericFileMessage<T> message, boolean isProbeContentTypeFromEndpoint) { 156 if (message != null) { 157 message.setHeader(Exchange.FILE_NAME_ONLY, getFileNameOnly()); 158 message.setHeader(Exchange.FILE_NAME, getFileName()); 159 message.setHeader(Exchange.FILE_NAME_CONSUMED, getFileName()); 160 message.setHeader("CamelFileAbsolute", isAbsolute()); 161 message.setHeader("CamelFileAbsolutePath", getAbsoluteFilePath()); 162 163 if (extendedAttributes != null) { 164 message.setHeader("CamelFileExtendedAttributes", extendedAttributes); 165 } 166 167 if ((isProbeContentTypeFromEndpoint || probeContentType) && file instanceof File) { 168 File f = (File) file; 169 Path path = f.toPath(); 170 try { 171 message.setHeader(Exchange.FILE_CONTENT_TYPE, Files.probeContentType(path)); 172 } catch (Throwable e) { 173 // just ignore the exception 174 } 175 } 176 177 if (isAbsolute()) { 178 message.setHeader(Exchange.FILE_PATH, getAbsoluteFilePath()); 179 } else { 180 // we must normalize path according to protocol if we build our own paths 181 String path = normalizePathToProtocol(getEndpointPath() + File.separator + getRelativeFilePath()); 182 message.setHeader(Exchange.FILE_PATH, path); 183 } 184 185 message.setHeader("CamelFileRelativePath", getRelativeFilePath()); 186 message.setHeader(Exchange.FILE_PARENT, getParent()); 187 188 if (getFileLength() >= 0) { 189 message.setHeader(Exchange.FILE_LENGTH, getFileLength()); 190 } 191 if (getLastModified() > 0) { 192 message.setHeader(Exchange.FILE_LAST_MODIFIED, getLastModified()); 193 } 194 } 195 } 196 197 protected boolean isAbsolute(String name) { 198 return FileUtil.isAbsolute(new File(name)); 199 } 200 201 protected String normalizePath(String name) { 202 return FileUtil.normalizePath(name); 203 } 204 205 /** 206 * Changes the name of this remote file. This method alters the absolute and 207 * relative names as well. 208 * 209 * @param newName the new name 210 */ 211 public void changeFileName(String newName) { 212 LOG.trace("Changing name to: {}", newName); 213 214 // Make sure the names is normalized. 215 String newFileName = FileUtil.normalizePath(newName); 216 String newEndpointPath = FileUtil.normalizePath(endpointPath.endsWith("" + File.separatorChar) ? endpointPath : endpointPath + File.separatorChar); 217 218 LOG.trace("Normalized endpointPath: {}", newEndpointPath); 219 LOG.trace("Normalized newFileName: ()", newFileName); 220 221 File file = new File(newFileName); 222 if (!absolute) { 223 // for relative then we should avoid having the endpoint path duplicated so clip it 224 if (ObjectHelper.isNotEmpty(newEndpointPath) && newFileName.startsWith(newEndpointPath)) { 225 // clip starting endpoint in case it was added 226 // use File.separatorChar as the normalizePath uses this as path separator so we should use the same 227 // in this logic here 228 if (newEndpointPath.endsWith("" + File.separatorChar)) { 229 newFileName = ObjectHelper.after(newFileName, newEndpointPath); 230 } else { 231 newFileName = ObjectHelper.after(newFileName, newEndpointPath + File.separatorChar); 232 } 233 234 // reconstruct file with clipped name 235 file = new File(newFileName); 236 } 237 } 238 239 // store the file name only 240 setFileNameOnly(file.getName()); 241 setFileName(file.getName()); 242 243 // relative path 244 if (file.getParent() != null) { 245 setRelativeFilePath(file.getParent() + getFileSeparator() + file.getName()); 246 } else { 247 setRelativeFilePath(file.getName()); 248 } 249 250 // absolute path 251 if (isAbsolute(newFileName)) { 252 setAbsolute(true); 253 setAbsoluteFilePath(newFileName); 254 } else { 255 setAbsolute(false); 256 // construct a pseudo absolute filename that the file operations uses even for relative only 257 String path = ObjectHelper.isEmpty(endpointPath) ? "" : endpointPath + getFileSeparator(); 258 setAbsoluteFilePath(path + getRelativeFilePath()); 259 } 260 261 if (LOG.isTraceEnabled()) { 262 LOG.trace("FileNameOnly: {}", getFileNameOnly()); 263 LOG.trace("FileName: {}", getFileName()); 264 LOG.trace("Absolute: {}", isAbsolute()); 265 LOG.trace("Relative path: {}", getRelativeFilePath()); 266 LOG.trace("Absolute path: {}", getAbsoluteFilePath()); 267 LOG.trace("Name changed to: {}", this); 268 } 269 } 270 271 public String getRelativeFilePath() { 272 return relativeFilePath; 273 } 274 275 public void setRelativeFilePath(String relativeFilePath) { 276 this.relativeFilePath = normalizePathToProtocol(relativeFilePath); 277 } 278 279 public String getFileName() { 280 return fileName; 281 } 282 283 public void setFileName(String fileName) { 284 this.fileName = normalizePathToProtocol(fileName); 285 } 286 287 public long getFileLength() { 288 return fileLength; 289 } 290 291 public void setFileLength(long fileLength) { 292 this.fileLength = fileLength; 293 } 294 295 public long getLastModified() { 296 return lastModified; 297 } 298 299 public void setLastModified(long lastModified) { 300 this.lastModified = lastModified; 301 } 302 303 public String getCharset() { 304 return charset; 305 } 306 307 public void setCharset(String charset) { 308 this.charset = charset; 309 } 310 311 public Map<String, Object> getExtendedAttributes() { 312 return extendedAttributes; 313 } 314 315 public void setExtendedAttributes(Map<String, Object> extendedAttributes) { 316 this.extendedAttributes = extendedAttributes; 317 } 318 319 @Override 320 public T getFile() { 321 return file; 322 } 323 324 public void setFile(T file) { 325 this.file = file; 326 } 327 328 public Object getBody() { 329 return getBinding().getBody(this); 330 } 331 332 public void setBody(Object os) { 333 getBinding().setBody(this, os); 334 } 335 336 public String getParent() { 337 String parent; 338 if (isAbsolute()) { 339 String name = getAbsoluteFilePath(); 340 File path = new File(name); 341 parent = path.getParent(); 342 } else { 343 String name = getRelativeFilePath(); 344 File path; 345 if (name != null) { 346 path = new File(endpointPath, name); 347 } else { 348 path = new File(endpointPath); 349 } 350 parent = path.getParent(); 351 } 352 return normalizePathToProtocol(parent); 353 } 354 355 public GenericFileBinding<T> getBinding() { 356 if (binding == null) { 357 binding = new GenericFileDefaultBinding<T>(); 358 } 359 return binding; 360 } 361 362 public void setBinding(GenericFileBinding<T> binding) { 363 this.binding = binding; 364 } 365 366 public void setAbsoluteFilePath(String absoluteFilePath) { 367 this.absoluteFilePath = normalizePathToProtocol(absoluteFilePath); 368 } 369 370 public String getAbsoluteFilePath() { 371 return absoluteFilePath; 372 } 373 374 public boolean isAbsolute() { 375 return absolute; 376 } 377 378 public void setAbsolute(boolean absolute) { 379 this.absolute = absolute; 380 } 381 382 public String getEndpointPath() { 383 return endpointPath; 384 } 385 386 public void setEndpointPath(String endpointPath) { 387 this.endpointPath = normalizePathToProtocol(endpointPath); 388 } 389 390 public String getFileNameOnly() { 391 return fileNameOnly; 392 } 393 394 public void setFileNameOnly(String fileNameOnly) { 395 this.fileNameOnly = fileNameOnly; 396 } 397 398 public boolean isDirectory() { 399 return directory; 400 } 401 402 public void setDirectory(boolean directory) { 403 this.directory = directory; 404 } 405 406 public String getCopyFromAbsoluteFilePath() { 407 return copyFromAbsoluteFilePath; 408 } 409 410 public void setCopyFromAbsoluteFilePath(String copyFromAbsoluteFilePath) { 411 this.copyFromAbsoluteFilePath = copyFromAbsoluteFilePath; 412 } 413 414 /** 415 * Fixes the path separator to be according to the protocol 416 */ 417 protected String normalizePathToProtocol(String path) { 418 if (ObjectHelper.isEmpty(path)) { 419 return path; 420 } 421 path = path.replace('/', getFileSeparator()); 422 path = path.replace('\\', getFileSeparator()); 423 return path; 424 } 425 426 @Override 427 public String toString() { 428 return "GenericFile[" + (absolute ? absoluteFilePath : relativeFilePath) + "]"; 429 } 430}