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.io.IOException; 021 import java.lang.reflect.Method; 022 import java.util.HashMap; 023 import java.util.Map; 024 025 import org.apache.camel.Consumer; 026 import org.apache.camel.ExchangePattern; 027 import org.apache.camel.Expression; 028 import org.apache.camel.Message; 029 import org.apache.camel.Processor; 030 import org.apache.camel.Producer; 031 import org.apache.camel.impl.ScheduledPollEndpoint; 032 import org.apache.camel.language.simple.FileLanguage; 033 import org.apache.camel.util.FactoryFinder; 034 import org.apache.camel.util.ObjectHelper; 035 import org.apache.camel.util.UuidGenerator; 036 import org.apache.commons.logging.Log; 037 import org.apache.commons.logging.LogFactory; 038 039 /** 040 * A <a href="http://activemq.apache.org/camel/file.html">File Endpoint</a> for 041 * working with file systems 042 * 043 * @version $Revision: 65775 $ 044 */ 045 public class FileEndpoint extends ScheduledPollEndpoint<FileExchange> { 046 public static final transient String DEFAULT_LOCK_FILE_POSTFIX = ".camelLock"; 047 048 private static final transient Log LOG = LogFactory.getLog(FileEndpoint.class); 049 private static final transient String DEFAULT_STRATEGYFACTORY_CLASS = 050 "org.apache.camel.component.file.strategy.FileProcessStrategyFactory"; 051 052 private File file; 053 private FileProcessStrategy fileProcessStrategy; 054 private boolean autoCreate = true; 055 private boolean lock = true; 056 private boolean delete; 057 private boolean noop; 058 private boolean append = true; 059 private String moveNamePrefix; 060 private String moveNamePostfix; 061 private String[] excludedNamePrefixes; 062 private String[] excludedNamePostfixes; 063 private String preMoveNamePrefix; 064 private String preMoveNamePostfix; 065 private String excludedNamePrefix; 066 private String excludedNamePostfix; 067 private int bufferSize = 128 * 1024; 068 private boolean ignoreFileNameHeader; 069 private Expression expression; 070 private Expression preMoveExpression; 071 072 protected FileEndpoint(File file, String endpointUri, FileComponent component) { 073 super(endpointUri, component); 074 this.file = file; 075 } 076 077 public FileEndpoint(String endpointUri, File file) { 078 super(endpointUri); 079 this.file = file; 080 } 081 082 public FileEndpoint(File file) { 083 this.file = file; 084 } 085 086 public FileEndpoint() { 087 } 088 089 public Producer<FileExchange> createProducer() throws Exception { 090 Producer<FileExchange> result = new FileProducer(this); 091 return result; 092 } 093 094 public Consumer<FileExchange> createConsumer(Processor processor) throws Exception { 095 Consumer<FileExchange> result = new FileConsumer(this, processor); 096 097 if (isDelete() && (getMoveNamePrefix() != null || getMoveNamePostfix() != null || getExpression() != null)) { 098 throw new IllegalArgumentException("You cannot set delet and a moveNamePrefix, moveNamePostfix or expression option"); 099 } 100 101 configureConsumer(result); 102 return result; 103 } 104 105 /** 106 * Create a new exchange for communicating with this endpoint 107 * 108 * @param file the file 109 * @return the created exchange 110 */ 111 public FileExchange createExchange(File file) { 112 return new FileExchange(getCamelContext(), getExchangePattern(), file); 113 } 114 115 @Override 116 public FileExchange createExchange() { 117 return createExchange(getFile()); 118 } 119 120 @Override 121 public FileExchange createExchange(ExchangePattern pattern) { 122 return new FileExchange(getCamelContext(), pattern, file); 123 } 124 125 /** 126 * Return the file name that will be auto-generated for the given message if none is provided 127 */ 128 public String getGeneratedFileName(Message message) { 129 return getFileFriendlyMessageId(message.getMessageId()); 130 } 131 132 /** 133 * Configures the given message with the file which sets the body to the file object 134 * and sets the {@link FileComponent#HEADER_FILE_NAME} header. 135 */ 136 public void configureMessage(File file, Message message) { 137 message.setBody(file); 138 String relativePath = file.getPath().substring(getFile().getPath().length()); 139 if (relativePath.startsWith(File.separator) || relativePath.startsWith("/")) { 140 relativePath = relativePath.substring(1); 141 } 142 message.setHeader(FileComponent.HEADER_FILE_NAME, relativePath); 143 } 144 145 public File getFile() { 146 ObjectHelper.notNull(file, "file"); 147 if (autoCreate && !file.exists()) { 148 file.mkdirs(); 149 } 150 return file; 151 } 152 153 public void setFile(File file) { 154 this.file = file; 155 } 156 157 public boolean isSingleton() { 158 return true; 159 } 160 161 public boolean isAutoCreate() { 162 return this.autoCreate; 163 } 164 165 public void setAutoCreate(boolean autoCreate) { 166 this.autoCreate = autoCreate; 167 } 168 169 public FileProcessStrategy getFileStrategy() { 170 if (fileProcessStrategy == null) { 171 fileProcessStrategy = createFileStrategy(); 172 LOG.debug("Using file process strategy: " + fileProcessStrategy); 173 } 174 return fileProcessStrategy; 175 } 176 177 /** 178 * Sets the strategy to be used when the file has been processed such as 179 * deleting or renaming it etc. 180 * 181 * @param fileProcessStrategy the new strategy to use 182 */ 183 public void setFileStrategy(FileProcessStrategy fileProcessStrategy) { 184 this.fileProcessStrategy = fileProcessStrategy; 185 } 186 187 public boolean isDelete() { 188 return delete; 189 } 190 191 public void setDelete(boolean delete) { 192 this.delete = delete; 193 } 194 195 public boolean isLock() { 196 return lock; 197 } 198 199 public void setLock(boolean lock) { 200 this.lock = lock; 201 } 202 203 public String getMoveNamePostfix() { 204 return moveNamePostfix; 205 } 206 207 /** 208 * Sets the name postfix appended to moved files. For example to rename all 209 * the files from <tt>*</tt> to <tt>*.done</tt> set this value to <tt>.done</tt> 210 */ 211 public void setMoveNamePostfix(String moveNamePostfix) { 212 this.moveNamePostfix = moveNamePostfix; 213 } 214 215 public String getMoveNamePrefix() { 216 return moveNamePrefix; 217 } 218 219 /** 220 * Sets the name prefix appended to moved files. For example to move 221 * processed files into a hidden directory called <tt>.camel</tt> set this value to 222 * <tt>.camel/</tt> 223 */ 224 public void setMoveNamePrefix(String moveNamePrefix) { 225 this.moveNamePrefix = moveNamePrefix; 226 } 227 228 public String[] getExcludedNamePrefixes() { 229 return excludedNamePrefixes; 230 } 231 232 /** 233 * Sets the excluded file name prefixes, such as <tt>"."</tt> for hidden files which 234 * are excluded by default 235 * 236 * @deprecated use ExcludedNamePrefix. Will be removed in Camel 2.0. 237 */ 238 public void setExcludedNamePrefixes(String[] excludedNamePrefixes) { 239 this.excludedNamePrefixes = excludedNamePrefixes; 240 } 241 242 public String[] getExcludedNamePostfixes() { 243 return excludedNamePostfixes; 244 } 245 246 /** 247 * Sets the excluded file name postfixes, such as {@link FileEndpoint#DEFAULT_LOCK_FILE_POSTFIX} 248 * to ignore lock files by default. 249 * 250 * @deprecated use ExcludedNamePostfix. Will be removed in Camel 2.0. 251 */ 252 public void setExcludedNamePostfixes(String[] excludedNamePostfixes) { 253 this.excludedNamePostfixes = excludedNamePostfixes; 254 } 255 256 public String getPreMoveNamePrefix() { 257 return preMoveNamePrefix; 258 } 259 260 public void setPreMoveNamePrefix(String preMoveNamePrefix) { 261 this.preMoveNamePrefix = preMoveNamePrefix; 262 } 263 264 /** 265 * Sets the name prefix appended to pre moved files. For example to move 266 * files before processing into a inprogress directory called <tt>.inprogress</tt> set this value to 267 * <tt>.inprogress/</tt> 268 */ 269 public String getPreMoveNamePostfix() { 270 return preMoveNamePostfix; 271 } 272 273 /** 274 * Sets the name postfix appended to pre moved files. For example to rename 275 * files before processing from <tt>*</tt> to <tt>*.inprogress</tt> set this value to <tt>.inprogress</tt> 276 */ 277 public void setPreMoveNamePostfix(String preMoveNamePostfix) { 278 this.preMoveNamePostfix = preMoveNamePostfix; 279 } 280 281 public boolean isNoop() { 282 return noop; 283 } 284 285 /** 286 * If set to true then the default {@link FileProcessStrategy} will be to use the 287 * {@link org.apache.camel.component.file.strategy.NoOpFileProcessStrategy NoOpFileProcessStrategy} 288 * to not move or copy processed files 289 */ 290 public void setNoop(boolean noop) { 291 this.noop = noop; 292 } 293 294 public boolean isAppend() { 295 return append; 296 } 297 298 /** 299 * When writing do we append to the end of the file, or replace it? 300 * The default is to append 301 */ 302 public void setAppend(boolean append) { 303 this.append = append; 304 } 305 306 public int getBufferSize() { 307 return bufferSize; 308 } 309 310 /** 311 * Sets the buffer size used to read/write files 312 */ 313 public void setBufferSize(int bufferSize) { 314 this.bufferSize = bufferSize; 315 } 316 317 public boolean isIgnoreFileNameHeader() { 318 return ignoreFileNameHeader; 319 } 320 321 /** 322 * If this flag is enabled then producers will ignore the {@link FileComponent#HEADER_FILE_NAME} 323 * header and generate a new dynamic file 324 */ 325 public void setIgnoreFileNameHeader(boolean ignoreFileNameHeader) { 326 this.ignoreFileNameHeader = ignoreFileNameHeader; 327 } 328 329 public String getExcludedNamePrefix() { 330 return excludedNamePrefix; 331 } 332 333 public void setExcludedNamePrefix(String excludedNamePrefix) { 334 this.excludedNamePrefix = excludedNamePrefix; 335 } 336 337 public String getExcludedNamePostfix() { 338 return excludedNamePostfix; 339 } 340 341 public void setExcludedNamePostfix(String excludedNamePostfix) { 342 this.excludedNamePostfix = excludedNamePostfix; 343 } 344 345 public Expression getExpression() { 346 return expression; 347 } 348 349 public void setExpression(Expression expression) { 350 this.expression = expression; 351 } 352 353 /** 354 * Sets the expression based on {@link FileLanguage} 355 */ 356 public void setExpression(String fileLanguageExpression) { 357 this.expression = FileLanguage.file(fileLanguageExpression); 358 } 359 360 public Expression getPreMoveExpression() { 361 return preMoveExpression; 362 } 363 364 public void setPreMoveExpression(Expression expression) { 365 this.preMoveExpression = expression; 366 } 367 368 /** 369 * Sets the pre move expression based on {@link FileLanguage} 370 */ 371 public void setPreMoveExpression(String fileLanguageExpression) { 372 this.preMoveExpression = FileLanguage.file(fileLanguageExpression); 373 } 374 375 /** 376 * A strategy method to lazily create the file strategy 377 */ 378 protected FileProcessStrategy createFileStrategy() { 379 Class<?> factory = null; 380 try { 381 FactoryFinder finder = getCamelContext().createFactoryFinder("META-INF/services/org/apache/camel/component/"); 382 factory = finder.findClass("file", "strategy.factory."); 383 } catch (ClassNotFoundException e) { 384 LOG.debug("'strategy.factory.class' not found", e); 385 } catch (IOException e) { 386 LOG.debug("No strategy factory defined in 'META-INF/services/org/apache/camel/component/file'", e); 387 } 388 389 if (factory == null) { 390 // use default 391 factory = ObjectHelper.loadClass(DEFAULT_STRATEGYFACTORY_CLASS); 392 if (factory == null) { 393 throw new TypeNotPresentException("FileProcessStrategyFactory class not found", null); 394 } 395 } 396 397 try { 398 Method factoryMethod = factory.getMethod("createFileProcessStrategy", Map.class); 399 return (FileProcessStrategy) ObjectHelper.invokeMethod(factoryMethod, null, getParamsAsMap()); 400 } catch (NoSuchMethodException e) { 401 throw new TypeNotPresentException(factory.getSimpleName() 402 + ".createFileProcessStrategy(Properties params) method not found", e); 403 } 404 } 405 406 protected Map<String, Object> getParamsAsMap() { 407 Map<String, Object> params = new HashMap<String, Object>(); 408 409 if (isNoop()) { 410 params.put("noop", Boolean.toString(true)); 411 } 412 if (isDelete()) { 413 params.put("delete", Boolean.toString(true)); 414 } 415 if (isAppend()) { 416 params.put("append", Boolean.toString(true)); 417 } 418 if (isLock()) { 419 params.put("lock", Boolean.toString(true)); 420 } 421 if (moveNamePrefix != null) { 422 params.put("moveNamePrefix", moveNamePrefix); 423 } 424 if (moveNamePostfix != null) { 425 params.put("moveNamePostfix", moveNamePostfix); 426 } 427 if (preMoveNamePrefix != null) { 428 params.put("preMoveNamePrefix", preMoveNamePrefix); 429 } 430 if (preMoveNamePostfix != null) { 431 params.put("preMoveNamePostfix", preMoveNamePostfix); 432 } 433 if (expression != null) { 434 params.put("expression", expression); 435 } 436 if (preMoveExpression != null) { 437 params.put("preMoveExpression", preMoveExpression); 438 } 439 440 return params; 441 } 442 443 @Override 444 protected String createEndpointUri() { 445 return "file://" + getFile().getAbsolutePath(); 446 } 447 448 protected String getFileFriendlyMessageId(String id) { 449 return UuidGenerator.generateSanitizedId(id); 450 } 451 }