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