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