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.Properties; 023 024 import org.apache.camel.Consumer; 025 import org.apache.camel.ExchangePattern; 026 import org.apache.camel.Message; 027 import org.apache.camel.Processor; 028 import org.apache.camel.Producer; 029 import org.apache.camel.component.file.strategy.FileProcessStrategySupport; 030 import org.apache.camel.impl.ScheduledPollEndpoint; 031 import org.apache.camel.util.FactoryFinder; 032 import org.apache.camel.util.ObjectHelper; 033 import org.apache.camel.util.UuidGenerator; 034 import org.apache.commons.logging.Log; 035 import org.apache.commons.logging.LogFactory; 036 037 /** 038 * A <a href="http://activemq.apache.org/camel/file.html">File Endpoint</a> for 039 * working with file systems 040 * 041 * @version $Revision: 664624 $ 042 */ 043 public class FileEndpoint extends ScheduledPollEndpoint<FileExchange> { 044 private static final transient Log LOG = LogFactory.getLog(FileEndpoint.class); 045 private static final String DEFAULT_STRATEGYFACTORY_CLASS = 046 "org.apache.camel.component.file.strategy.FileProcessStrategyFactory"; 047 048 private File file; 049 private FileProcessStrategy fileProcessStrategy; 050 private boolean autoCreate = true; 051 private boolean lock = true; 052 private boolean delete; 053 private boolean noop; 054 private boolean append = true; 055 private String moveNamePrefix; 056 private String moveNamePostfix; 057 private String[] excludedNamePrefixes = {"."}; 058 private String[] excludedNamePostfixes = {FileProcessStrategySupport.DEFAULT_LOCK_FILE_POSTFIX}; 059 private int bufferSize = 128 * 1024; 060 private boolean ignoreFileNameHeader; 061 062 protected FileEndpoint(File file, String endpointUri, FileComponent component) { 063 super(endpointUri, component); 064 this.file = file; 065 } 066 067 public FileEndpoint(String endpointUri, File file) { 068 super(endpointUri); 069 this.file = file; 070 } 071 072 public FileEndpoint(File file) { 073 this.file = file; 074 } 075 076 public FileEndpoint() { 077 } 078 079 public Producer<FileExchange> createProducer() throws Exception { 080 Producer<FileExchange> result = new FileProducer(this); 081 return result; 082 } 083 084 public Consumer<FileExchange> createConsumer(Processor processor) throws Exception { 085 Consumer<FileExchange> result = new FileConsumer(this, processor); 086 configureConsumer(result); 087 return result; 088 } 089 090 /** 091 * Create a new exchange for communicating with this endpoint 092 * 093 * @param file the file 094 * @return the created exchange 095 */ 096 public FileExchange createExchange(File file) { 097 return new FileExchange(getCamelContext(), getExchangePattern(), file); 098 } 099 100 @Override 101 public FileExchange createExchange() { 102 return createExchange(getFile()); 103 } 104 105 @Override 106 public FileExchange createExchange(ExchangePattern pattern) { 107 return new FileExchange(getCamelContext(), pattern, file); 108 } 109 110 111 /** 112 * Return the file name that will be auto-generated for the given message if none is provided 113 */ 114 public String getGeneratedFileName(Message message) { 115 return getFileFriendlyMessageId(message.getMessageId()); 116 } 117 118 /** 119 * Configures the given message with the file which sets the body to the file object 120 * and sets the {@link FileComponent#HEADER_FILE_NAME} header. 121 */ 122 public void configureMessage(File file, Message message) { 123 message.setBody(file); 124 String relativePath = file.getPath().substring(getFile().getPath().length()); 125 if (relativePath.startsWith(File.separator) || relativePath.startsWith("/")) { 126 relativePath = relativePath.substring(1); 127 } 128 message.setHeader(FileComponent.HEADER_FILE_NAME, relativePath); 129 } 130 131 public File getFile() { 132 ObjectHelper.notNull(file, "file"); 133 if (autoCreate && !file.exists()) { 134 file.mkdirs(); 135 } 136 return file; 137 } 138 139 public void setFile(File file) { 140 this.file = file; 141 } 142 143 public boolean isSingleton() { 144 return true; 145 } 146 147 public boolean isAutoCreate() { 148 return this.autoCreate; 149 } 150 151 public void setAutoCreate(boolean autoCreate) { 152 this.autoCreate = autoCreate; 153 } 154 155 public FileProcessStrategy getFileStrategy() { 156 if (fileProcessStrategy == null) { 157 fileProcessStrategy = createFileStrategy(); 158 LOG.debug("Using file process strategy: " + fileProcessStrategy); 159 } 160 return fileProcessStrategy; 161 } 162 163 /** 164 * Sets the strategy to be used when the file has been processed such as 165 * deleting or renaming it etc. 166 * 167 * @param fileProcessStrategy the new strategy to use 168 */ 169 public void setFileStrategy(FileProcessStrategy fileProcessStrategy) { 170 this.fileProcessStrategy = fileProcessStrategy; 171 } 172 173 public boolean isDelete() { 174 return delete; 175 } 176 177 public void setDelete(boolean delete) { 178 this.delete = delete; 179 } 180 181 public boolean isLock() { 182 return lock; 183 } 184 185 public void setLock(boolean lock) { 186 this.lock = lock; 187 } 188 189 public String getMoveNamePostfix() { 190 return moveNamePostfix; 191 } 192 193 /** 194 * Sets the name postfix appended to moved files. For example to rename all 195 * the files from <tt>*</tt> to <tt>*.done</tt> set this value to <tt>.done</tt> 196 */ 197 public void setMoveNamePostfix(String moveNamePostfix) { 198 this.moveNamePostfix = moveNamePostfix; 199 } 200 201 public String getMoveNamePrefix() { 202 return moveNamePrefix; 203 } 204 205 /** 206 * Sets the name prefix appended to moved files. For example to move 207 * processed files into a hidden directory called <tt>.camel</tt> set this value to 208 * <tt>.camel/</tt> 209 */ 210 public void setMoveNamePrefix(String moveNamePrefix) { 211 this.moveNamePrefix = moveNamePrefix; 212 } 213 214 public String[] getExcludedNamePrefixes() { 215 return excludedNamePrefixes; 216 } 217 218 /** 219 * Sets the excluded file name prefixes, such as <tt>"."</tt> for hidden files which 220 * are excluded by default 221 */ 222 public void setExcludedNamePrefixes(String[] excludedNamePrefixes) { 223 this.excludedNamePrefixes = excludedNamePrefixes; 224 } 225 226 public String[] getExcludedNamePostfixes() { 227 return excludedNamePostfixes; 228 } 229 230 /** 231 * Sets the excluded file name postfixes, such as {@link FileProcessStrategySupport#DEFAULT_LOCK_FILE_POSTFIX} 232 * to ignore lock files by default. 233 */ 234 public void setExcludedNamePostfixes(String[] excludedNamePostfixes) { 235 this.excludedNamePostfixes = excludedNamePostfixes; 236 } 237 238 public boolean isNoop() { 239 return noop; 240 } 241 242 /** 243 * If set to true then the default {@link FileProcessStrategy} will be to use the 244 * {@link org.apache.camel.component.file.strategy.NoOpFileProcessStrategy NoOpFileProcessStrategy} 245 * to not move or copy processed files 246 */ 247 public void setNoop(boolean noop) { 248 this.noop = noop; 249 } 250 251 public boolean isAppend() { 252 return append; 253 } 254 255 /** 256 * When writing do we append to the end of the file, or replace it? 257 * The default is to append 258 */ 259 public void setAppend(boolean append) { 260 this.append = append; 261 } 262 263 public int getBufferSize() { 264 return bufferSize; 265 } 266 267 /** 268 * Sets the buffer size used to read/write files 269 */ 270 public void setBufferSize(int bufferSize) { 271 this.bufferSize = bufferSize; 272 } 273 274 public boolean isIgnoreFileNameHeader() { 275 return ignoreFileNameHeader; 276 } 277 278 /** 279 * If this flag is enabled then producers will ignore the {@link FileComponent#HEADER_FILE_NAME} 280 * header and generate a new dynamic file 281 */ 282 public void setIgnoreFileNameHeader(boolean ignoreFileNameHeader) { 283 this.ignoreFileNameHeader = ignoreFileNameHeader; 284 } 285 286 /** 287 * A strategy method to lazily create the file strategy 288 */ 289 protected FileProcessStrategy createFileStrategy() { 290 Class<?> factory = null; 291 try { 292 FactoryFinder finder = new FactoryFinder("META-INF/services/org/apache/camel/component/"); 293 factory = finder.findClass("file", "strategy.factory."); 294 } catch (ClassNotFoundException e) { 295 LOG.debug("'strategy.factory.class' not found", e); 296 } catch (IOException e) { 297 LOG.debug("No strategy factory defined in 'META-INF/services/org/apache/camel/component/file'", e); 298 } 299 300 if (factory == null) { 301 // use default 302 factory = ObjectHelper.loadClass(DEFAULT_STRATEGYFACTORY_CLASS); 303 if (factory == null) { 304 throw new TypeNotPresentException("FileProcessStrategyFactory class not found", null); 305 } 306 } 307 308 try { 309 Method factoryMethod = factory.getMethod("createFileProcessStrategy", Properties.class); 310 return (FileProcessStrategy) ObjectHelper.invokeMethod(factoryMethod, null, getParamsAsProperties()); 311 } catch (NoSuchMethodException e) { 312 throw new TypeNotPresentException(factory.getSimpleName() 313 + ".createFileProcessStrategy(Properties params) method not found", e); 314 } 315 } 316 317 protected Properties getParamsAsProperties() { 318 Properties params = new Properties(); 319 if (isNoop()) { 320 params.setProperty("noop", Boolean.toString(true)); 321 } 322 if (isDelete()) { 323 params.setProperty("delete", Boolean.toString(true)); 324 } 325 if (isAppend()) { 326 params.setProperty("append", Boolean.toString(true)); 327 } 328 if (isLock()) { 329 params.setProperty("lock", Boolean.toString(true)); 330 } 331 if (moveNamePrefix != null) { 332 params.setProperty("moveNamePrefix", moveNamePrefix); 333 } 334 if (moveNamePostfix != null) { 335 params.setProperty("moveNamePostfix", moveNamePostfix); 336 } 337 return params; 338 } 339 340 @Override 341 protected String createEndpointUri() { 342 return "file://" + getFile().getAbsolutePath(); 343 } 344 345 protected String getFileFriendlyMessageId(String id) { 346 return UuidGenerator.generateSanitizedId(id); 347 } 348 }