File 컴포넌트
File 컴포넌트는 파일 시스템에 접근하게 해주는 컴포넌트예요. 파일을 다른 Camel 컴포넌트가 처리하게 하거나, 다른 컴포넌트에서 온 메시지를 디스크에 저장할 수 있게 해줘요. Camel 1.0부터 제공됐고 producer와 consumer를 모두 지원합니다.
출처: 문서
본문
Camel 1.0부터 지원
producer와 consumer 모두 지원
File 컴포넌트는 파일 시스템에 접근하게 해줘요. 파일을 다른 Camel 컴포넌트가 처리하게 할 수 있고, 다른 컴포넌트에서 온 메시지를 디스크에 저장할 수도 있어요.
URI 형식
file:directoryName[?options]
directoryName은 기반이 되는 파일 디렉터리예요.
디렉터리만 가능
Camel은 시작 디렉터리로 설정된 엔드포인트만 지원해요. 그래서 directoryName은 반드시 디렉터리여야 해요. 파일 하나만 소비하고 싶다면 fileName 옵션을 쓰면 됩니다. 예컨대 fileName=thefilename처럼 설정하는 식이에요. 또한 시작 디렉터리에는 ${ } 플레이스홀더가 들어간 동적 표현식을 쓸 수 없어요. 파일 이름의 동적 부분은 역시 fileName 옵션으로 지정하세요.
경고 다른 애플리케이션이 쓰고 있는 파일 읽기 피하기
JDK File IO API는 다른 애플리케이션이 현재 파일을 쓰거나 복사하는 중인지 탐지하는 능력이 좀 부족해요. 게다가 구현은 OS 플랫폼마다 다를 수 있어요. 이 때문에 Camel이 그 파일을 다른 프로세스가 잠그지 않았다고 판단하고 소비를 시작해 버릴 수 있어요. 그래서 자기 환경에 맞는 방법은 직접 확인해야 해요. 이를 돕기 위해 Camel은 여러
readLock옵션과doneFileName옵션을 제공해요. 다른 애플리케이션이 파일을 직접 놓는 폴더에서 파일 소비하기도 참고하세요.
옵션 설정
Camel 컴포넌트는 두 단계로 나눠 설정합니다:
-
컴포넌트(component) 레벨
-
엔드포인트(endpoint) 레벨
컴포넌트 옵션 설정
컴포넌트 레벨에서는 엔드포인트들이 물려받는 공통·일반 설정을 정해요. 설정 계층 중 가장 위에 해당합니다. 예를 들어 컴포넌트에는 보안 설정, 인증용 자격 증명, 네트워크 연결 URL 같은 것들이 들어가죠.
컴포넌트마다 옵션이 몇 개뿐인 경우도 있고 많은 경우도 있어요. 컴포넌트는 보통 널리 쓰는 기본값을 이미 갖고 있어서, 옵션을 몇 개만 설정하거나 아예 안 건드려도 되는 경우가 많습니다.
컴포넌트 설정 방법은 이렇게 있어요:
-
설정 파일(
application.properties,*.yaml파일 등). -
Java 코드에서 직접.
엔드포인트 옵션 설정
엔드포인트는 옵션이 많아서 설정에 시간을 더 쓰게 됩니다. 이 옵션들로 엔드포인트가 어떻게 동작할지 원하는 대로 조정할 수 있어요. 옵션은 엔드포인트를 consumer(from)로 쓰는지, producer(to)로 쓰는지, 둘 다 쓰는지에 따라 나눠집니다.
엔드포인트 설정은 보통 엔드포인트 URI에 _path_와 query 파라미터로 직접 하는 경우가 많아요. Java에서 엔드포인트와 데이터 포맷을 _타입 세이프_하게 설정하려면 Endpoint DSL과 DataFormat DSL을 쓸 수도 있습니다. 옵션을 설정할 때 좋은 습관은 프로퍼티 플레이스홀더를 쓰는 거예요. 플레이스홀더를 쓰면 이런 점이 좋아요:
-
URL, 포트 번호, 민감한 정보 등을 코드에 하드코딩하는 걸 막아줘요.
-
설정을 코드 밖으로 분리할 수 있어요.
-
코드가 더 유연해지고 재사용하기 좋아집니다.
아래 두 섹션에서 모든 옵션을 컴포넌트 먼저, 그다음 엔드포인트 순으로 나열해요.
컴포넌트 옵션
File 컴포넌트가 지원하는 옵션은 아래와 같아요.
| Name | Description | Default | Type |
|---|---|---|---|
| bridgeErrorHandler (consumer) | 컨슈머를 Camel 라우팅 에러 핸들러(Error Handler)에 브리지할 수 있게 해줘요. 즉 Camel 컨슈머가 들어오는 메시지를 집어 올리는 동안 등에서 (가능한 경우) 예외가 발생하면, 그 예외를 메시지로 처리해서 라우팅 에러 핸들러가 다루게 돼요. 중요: 서드파티 컴포넌트가 예외가 던져졌을 때 Camel에 알릴 수 있도록 허용하는 경우에만 가능해요. 일부 컴포넌트는 내부에서만 처리하기 때문에 bridgeErrorHandler를 쓸 수 없어요. 다른 상황에서는 이를 가능하게 하려고 Camel 컴포넌트가 서드파티 컴포넌트에 연결(hook)하도록 개선될 수도 있어요. 기본으로 컨슈머는 org.apache.camel.spi.ExceptionHandler로 예외를 처리하는데, WARN 또는 ERROR 레벨로 로그를 남기고 무시해요. | false | boolean |
| lazyStartProducer (producer) | producer를 lazy(첫 메시지 시점)로 시작할지 여부예요. lazy 시작을 쓰면 producer가 시작 중 실패해서 라우트 시작이 실패할 수 있는 상황에서도 CamelContext와 라우트가 시작될 수 있고, 시작 실패 처리를 Camel의 라우팅 에러 핸들러로 넘길 수 있어요. 다만 첫 메시지를 처리하는 시점에 producer를 만들고 시작하는 데 시간이 좀 걸려 전체 처리 시간이 길어질 수 있는 점은 유의하세요. | false | boolean |
| autowiredEnabled (advanced) | 오토와이어링 사용 여부예요. 레지스트리에서 일치하는 타입의 인스턴스가 하나만 있는지 찾아 컴포넌트에 자동으로 설정하는(옵션이 autowired로 표시돼 있어야 함) 자동 오토와이어링에 쓰입니다. JDBC 데이터 소스, JMS 커넥션 팩토리, AWS 클라이언트 등을 자동 설정할 때 유용해요. | true | boolean |
| healthCheckConsumerEnabled (health) | 이 컴포넌트의 consumer 기반 헬스 체크를 모두 켜고 끄는 데 써요. | true | boolean |
| healthCheckProducerEnabled (health) | 이 컴포넌트의 producer 기반 헬스 체크를 모두 켜고 끄는 데 써요. 참고: Camel은 기본으로 producer 기반 헬스 체크를 모두 꺼두고, camel.health.producersEnabled=true로 설정하면 전역으로 켤 수 있어요. | true | boolean |
엔드포인트 옵션
File 엔드포인트는 다음 URI 문법으로 설정해요:
file:directoryName
다음 _path_와 query 파라미터를 씁니다:
Path 파라미터
| Name | Description | Default | Type |
|---|---|---|---|
| directoryName (common) | 필수 시작 디렉터리예요. | File |
Query 파라미터
| Name | Description | Default | Type |
|---|---|---|---|
| charset (common) | 파일의 인코딩을 지정하는 옵션이에요. consumer에서는 파일의 인코딩을 지정해서, 파일 내용에 접근할 때 Camel이 어떤 문자셋으로 내용을 불러와야 하는지 알 수 있게 해줘요. 마찬가지로 파일을 쓸 때도 이 옵션으로 어떤 문자셋으로 쓸지 지정할 수 있어요. 다만 파일을 쓸 때는 설정한 문자셋으로 데이터를 변환하려고 메시지 내용을 메모리로 읽어 들여야 할 수 있으니, 큰 메시지에는 이 옵션을 쓰지 마세요. | String | |
| doneFileName (common) | producer: 설정하면 원본 파일을 쓴 뒤 두 번째 done 파일을 써요. done 파일은 비어 있어요. 이 옵션으로 그 파일 이름을 정하는데, 고정 이름을 지정하거나 동적 플레이스홀더를 쓸 수 있어요. done 파일은 항상 원본 파일과 같은 폴더에 쓰입니다. consumer: 설정하면 done 파일이 존재할 때만 파일을 소비해요. 이 옵션으로 그 파일 이름을 정하고, 고정 이름이나 동적 플레이스홀더를 쓸 수 있어요. done 파일은 항상 원본 파일과 같은 폴더에 있는 걸로 기대해요. 동적 플레이스홀더는 ${file.name}과 ${file.name.next}만 지원해요. | String | |
| fileName (common) | File Language 같은 Expression으로 파일 이름을 동적으로 설정해요. consumer에서는 파일 이름 필터로, producer에서는 쓸 파일 이름을 평가하는 데 써요. 표현식이 설정되면 CamelFileName 헤더보다 우선해요(참고: 헤더 자체도 Expression일 수 있어요). 표현식 옵션은 String과 Expression 타입을 모두 지원하는데, String이면 항상 File Language로 평가하고 Expression 타입이면 지정한 Expression 타입을 써서(예: OGNL 표현식도 가능) 평가해요. consumer에서는 파일 이름을 걸러 내는 데 쓸 수 있어서, 예컨대 File Language 문법 mydata-${date:now:yyyyMMdd}.txt로 오늘 파일을 소비할 수 있어요. producer는 CamelOverruleFileName 헤더를 지원하는데, 기존 CamelFileName 헤더보다 우선하며 한 번만 쓰이고 나면 제거돼서 CamelFileName을 임시로 저장했다가 복원하는 번거로움을 피하게 해줘요. | String | |
| jailStartingDirectory (common) | 파일 쓰기를 시작 디렉터리(및 하위)로만 제한(jail)하는 데 쓰여요. 기본으로 켜져 있어서 Camel이 바깥 디렉터리에 파일을 쓰지 못하게 막아요(기본 설정이 더 안전해서예요). 끄면 부모나 루트 폴더 같은 시작 디렉터리 밖의 디렉터리에 파일을 쓸 수 있어요. localWorkDirectory를 쓰는 consumer에서는 다운로드한 파일도 설정한 localWorkDirectory 안에 머물도록 제한해요. | true | boolean |
| delete (consumer) | true면 파일이 성공적으로 처리된 뒤 삭제돼요. | false | boolean |
| moveFailed (consumer) | Simple 언어 기반으로 move 실패 표현식을 설정해요. 예컨대 파일을 .error 하위 디렉터리로 옮기려면 .error로 설정해요. 참고: 파일을 실패 위치로 옮길 때 Camel이 오류를 처리하고 그 파일을 다시 집어 올리지는 않아요. | String | |
| noop (consumer) | true면 파일을 옮기거나 삭제하지 않아요. 읽기 전용 데이터나 ETL 방식 요구에 적합해요. noop=true면 같은 파일을 반복해서 소비하지 않도록 idempotent=true도 함께 켜져요. | false | boolean |
| preMove (consumer) | 처리 전에 파일을 옮길 때 파일 이름을 동적으로 정하는 데 쓰는 표현식(File Language 같은)이에요. 예컨대 처리 중인 파일을 order 디렉터리로 옮기려면 이 값을 order로 설정해요. | String | |
| preSort (consumer) |
pre-sort가 켜져 있으면 consumer가 파일 시스템에서 가져온 파일과 디렉터리 이름을 폴링 중에 정렬해요. 파일을 정렬된 순서로 다뤄야 할 때 필요할 수 있어요. pre-sort는 consumer가 파일을 걸러서 Camel이 처리할 파일로 받아들이기 전에 실행돼요. 이 옵션은 기본 false로 꺼져 있어요. 지원 값은 이래요: name(파일 이름 정렬), modified(마지막 수정 타임스탬프 정렬), size(파일 크기 정렬). 내림차순(역순)으로 정렬하려면 값 앞에 마이너스 기호를 붙이세요(예: -modified는 최신 파일 먼저). true는 name의 별칭이에요(하위 호환).
Enum values:
-
true
-
false
-
name
-
-name
-
modified
-
-modified
-
size
-
-size
| | String | | recursive (consumer) | 디렉터리라면 모든 하위 디렉터리의 파일도 찾아요. | false | boolean | | sendEmptyMessageWhenIdle (consumer) | 폴링 consumer가 파일을 하나도 폴링하지 못했을 때 이 옵션을 켜면 빈 메시지(본문 없음)를 대신 보내요. | false | boolean | | bridgeErrorHandler (consumer (advanced)) | 컨슈머를 Camel 라우팅 에러 핸들러(Error Handler)에 브리지할 수 있게 해줘요. 즉 Camel 컨슈머가 들어오는 메시지를 집어 올리는 동안 등에서 (가능한 경우) 예외가 발생하면, 그 예외를 메시지로 처리해서 라우팅 에러 핸들러가 다루게 돼요. 중요: 서드파티 컴포넌트가 예외가 던져졌을 때 Camel에 알릴 수 있도록 허용하는 경우에만 가능해요. 일부 컴포넌트는 내부에서만 처리하기 때문에 bridgeErrorHandler를 쓸 수 없어요. 다른 상황에서는 이를 가능하게 하려고 Camel 컴포넌트가 서드파티 컴포넌트에 연결(hook)하도록 개선될 수도 있어요. 기본으로 컨슈머는 org.apache.camel.spi.ExceptionHandler로 예외를 처리하는데, WARN 또는 ERROR 레벨로 로그를 남기고 무시해요. | false | boolean | | directoryMustExist (consumer (advanced)) | startingDirectoryMustExist 옵션과 비슷하지만, 폴링 중(consumer 시작 후)에 적용돼요. | false | boolean | | exceptionHandler (consumer (advanced)) | consumer가 커스텀 ExceptionHandler를 쓰게 해줘요. bridgeErrorHandler 옵션이 켜져 있으면 이 옵션은 쓰이지 않아요. 기본으로 컨슈머는 예외를 WARN 또는 ERROR 레벨로 기록하고 무시해요. | | ExceptionHandler | | exchangePattern (consumer (advanced)) |
consumer가 exchange를 만들 때 교환 패턴을 설정해요.
Enum values:
-
InOnly
-
InOut
| | ExchangePattern | | extendedAttributes (consumer (advanced)) | 관심 있는 파일 속성을 정의해요. posix:permissions,posix:owner,basic:lastAccessTime 같은 식이고, posix:, basic:lastAccessTime 같은 기본적인 와일드카드도 지원해요. | | String | | includeHiddenDirs (consumer (advanced)) | 숨김 디렉터리를 받아들일지 여부예요. 이름이 점으로 시작하는 디렉터리는 숨김 디렉터리로 보고 기본으로는 포함하지 않아요. 이 옵션을 true로 하면 파일 consumer에 숨김 디렉터리도 포함돼요. | false | boolean | | includeHiddenFiles (consumer (advanced)) | 숨김 파일을 받아들일지 여부예요. 이름이 점으로 시작하는 파일은 숨김 파일로 보고 기본으로는 포함하지 않아요. 이 옵션을 true로 하면 파일 consumer에 숨김 파일도 포함돼요. | false | boolean | | inProgressRepository (consumer (advanced)) | 플러그 가능한 in-progress 리포지터리(org.apache.camel.spi.IdempotentRepository)예요. in-progress 리포지터리는 현재 소비 중인 파일을 기록하는 데 쓰여요. 기본은 메모리 기반 리포지터리예요. | | IdempotentRepository | | localWorkDirectory (consumer (advanced)) | 소비할 때 로컬 작업 디렉터리를 쓰면 원격 파일 내용을 메모리로 불러오지 않고 바로 로컬 파일에 저장할 수 있어요. 아주 큰 원격 파일을 소비할 때 메모리를 아낄 수 있어서 유익해요. | | String | | onCompletionExceptionHandler (consumer (advanced)) | consumer가 commit 또는 rollback을 수행하는 파일 onCompletion 과정에서 던져지는 예외를 처리하려고 커스텀 org.apache.camel.spi.ExceptionHandler를 쓰게 해줘요. 기본 구현은 예외를 WARN 레벨로 기록하고 무시해요. | | ExceptionHandler | | pollStrategy (consumer (advanced)) | org.apache.camel.PollingConsumerPollingStrategy를 플러그인으로 제공해서, Exchange가 만들어져 Camel에서 라우팅되기 전에 폴링 작업 중에 주로 일어나는 오류 처리를 직접 제어할 수 있어요. | | PollingConsumerPollStrategy | | probeContentType (consumer (advanced)) | content type 탐지를 켤지 여부예요. 켜면 consumer가 Files#probeContentType(java.nio.file.Path)로 파일의 content-type을 정해서, Exchange#FILE_CONTENT_TYPE 키로 Message에 헤더로 저장해요. | false | boolean | | processStrategy (consumer (advanced)) | org.apache.camel.component.file.GenericFileProcessStrategy를 플러그인으로 제공해서 자신만의 readLock 옵션 등을 구현할 수 있어요. 특별한 ready 파일이 존재해야 한다 같은 특수 조건을 충족해야 파일을 소비할 수 있을 때도 쓸 수 있어요. 이 옵션을 설정하면 readLock 옵션은 적용되지 않아요. | | GenericFileProcessStrategy | | startingDirectoryMustExist (consumer (advanced)) | 시작 디렉터리가 반드시 존재해야 하는지 여부예요. autoCreate 옵션이 기본으로 켜져 있어서 보통은 시작 디렉터리가 없으면 자동으로 만들어져요. autoCreate를 끄고 이 옵션을 켜면 시작 디렉터리가 반드시 존재해야 해요. 디렉터리가 없으면 예외를 던져요. | false | boolean | | startingDirectoryMustHaveAccess (consumer (advanced)) | 시작 디렉터리에 접근 권한이 있는지 여부예요. 디렉터리 존재 확인을 위해서는 startingDirectoryMustExist 파라미터를 true로 설정해야 해요. 읽기·쓰기 권한이 없으면 예외를 던져요. | false | boolean | | appendChars (producer) | 파일을 쓴 뒤 문자(텍스트)를 덧붙이는 데 써요. 새 파일을 쓰거나 기존 파일에 덧붙일 때 새 줄이나 다른 구분자를 추가하는 용도로 쓸 수 있죠. 새 줄(slash-n 또는 slash-r)이나 탭(slash-t) 문자를 지정하려면 슬래시를 하나 더 붙여 이스케이프하세요(예: slash-slash-n). | | String | | checksumFileAlgorithm (producer) |
설정하면 Camel이 쓴 파일로 체크섬을 계산해서 그 결과를 CamelFileChecksum 헤더에 저장해요.
Enum values:
-
MD2
-
MD5
-
SHA_1
-
SHA_224
-
SHA_256
-
SHA_384
-
SHA_512
-
SHA_512_224
-
SHA_512_256
-
SHA3_224
-
SHA3_256
-
SHA3_384
-
SHA3_512
| | String | | checksumWriteFile (producer) | checksumFileAlgorithm이 설정돼 있을 때 체크섬 파일도 함께 쓸지를 이 옵션이 정해요. 체크섬 파일은 항상 원본 파일과 같은 폴더에 쓰여요. | true | boolean | | fileExist (producer) |
같은 이름의 파일이 이미 있을 때 어떻게 할지 정해요. 기본인 Override는 기존 파일을 대체해요. - Append - 기존 파일에 내용을 추가해요. - Fail - 이미 파일이 존재한다는 뜻의 GenericFileOperationException을 던져요. - Ignore - 문제를 조용히 무시하고 기존 파일을 대체하지 않지만 모두 잘 된 것으로 간주해요. - Move - moveExisting 옵션도 함께 설정해야 해요. eagerDeleteTargetFile 옵션으로 파일을 옮길 때 기존 파일이 이미 있을 때의 동작을 제어할 수 있고, 설정하지 않으면 move 작업이 실패해요. Move 옵션은 대상 파일을 쓰기 전에 기존 파일들을 옮겨요. - TryRename은 tempFileName 옵션을 쓸 때만 적용돼요. 존재 검사 없이 임시 이름에서 실제 이름으로 이름 바꾸기를 시도하게 해줘요. 이 검사는 일부 파일 시스템, 특히 FTP 서버에서 더 빠를 수 있어요.
Enum values:
-
Override
-
Append
-
Fail
-
Ignore
-
Move
-
TryRename
| Override | GenericFileExist | | flatten (producer) | 파일 이름 경로를 평탄화해서 선행 경로를 벗기고 파일 이름만 남겨요. 이러면 하위 디렉터리까지 재귀적으로 소비하면서도, 다른 디렉터리에 쓸 때는 파일이 한 디렉터리에 담기게 돼요. producer에서 true로 하면 CamelFileName 헤더의 어떤 파일 이름에서도 선행 경로가 제거돼요. | false | boolean | | moveExisting (producer) | fileExist=Move가 설정됐을 때 옮길 파일 이름을 계산하는 데 쓰는 표현식(File Language 같은)이에요. 파일을 backup 하위 디렉터리로 옮기려면 그냥 backup만 입력하면 돼요. 이 옵션은 file:name, file:name.ext, file:name.noext, file:onlyname, file:onlyname.noext, file:ext, file:parent File Language 토큰만 지원해요. FTP 컴포넌트는 현재 디렉터리를 기준으로 한 상대 디렉터리로만 기존 파일을 옮길 수 있어서 file:parent는 지원하지 않는 점에 유의하세요. | | String | | tempFileName (producer) | tempPrefix 옵션과 같지만 File Language를 쓰기 때문에 임시 파일 이름을 더 세밀하게 제어할 수 있어요. tempFilename의 위치는 기본 URI의 대상 디렉터리가 아니라 'fileName' 옵션의 최종 파일 위치를 기준으로 해요. 예컨대 fileName 옵션에 dir/finalFilename처럼 디렉터리 접두어가 들어 있으면 tempFileName은 그 하위 디렉터리 dir 기준이에요. | | String | | tempPrefix (producer) | 임시 이름으로 파일을 쓰고, 쓰기가 끝난 뒤 실제 이름으로 이름을 바꾸는 옵션이에요. 쓰기 중인 파일을 식별하는 데 쓸 수 있고, (배타적 읽기 잠금을 쓰지 않는) consumer가 쓰기 중인 파일을 읽는 걸 피하게 해줘요. 큰 파일을 업로드할 때 FTP에서 자주 쓰는 방식이에요. | | String | | allowNullBody (producer (advanced)) | 파일 쓰기 중 null 본문을 허용할지 지정해요. true면 빈 파일이 만들어지고, false인 상태로 null 본문을 파일 컴포넌트에 보내려 하면 'Cannot write null body to file.'이라는 GenericFileWriteException이 던져져요. fileExist 옵션이 'Override'면 파일이 잘리고(truncate), append면 파일은 그대로 유지돼요. | false | boolean | | chmod (producer (advanced)) | producer가 보내는 파일 권한을 지정해요. chmod 값은 000에서 777 사이여야 하고, 0755처럼 선행 숫자가 있으면 무시해요. | | String | | chmodDirectory (producer (advanced)) | producer가 없는 디렉터리를 만들 때 쓰는 디렉터리 권한을 지정해요. chmod 값은 000에서 777 사이여야 하고, 0755처럼 선행 숫자가 있으면 무시해요. | | String | | eagerDeleteTargetFile (producer (advanced)) | 기존 대상 파일을 미리(eagerly) 삭제할지 여부예요. fileExists=Override와 tempFileName 옵션을 함께 쓸 때만 적용돼요. 임시 파일을 쓰기 전에 대상 파일을 삭제하지 않으려면(false로 설정) 이걸 쓰세요. 예컨대 큰 파일을 쓰면서 임시 파일을 쓰는 동안 대상 파일이 남아 있길 원할 수 있죠. 이러면 대상 파일은 임시 파일이 대상 파일 이름으로 바뀌기 직전, 아주 마지막 순간까지 삭제되지 않아요. fileExist=Move가 켜져 있고 기존 파일이 있을 때 기존 파일을 삭제할지도 이 옵션이 제어해요. 이 옵션의 copyAndDeleteOnRenameFails가 false면 기존 파일이 있을 때 예외가 던져지고, true면 move 작업 전에 기존 파일이 삭제돼요. | true | boolean | | forceWrites (producer (advanced)) | 지원 중단됨 파일 시스템에 쓰기를 강제로 동기화할지 여부예요. 이 정도의 보장이 필요 없다면(예: 로그/감사 로그에 쓸 때) 끌 수 있어요. 그러면 성능이 더 좋아져요. | false | boolean | | keepLastModified (producer (advanced)) | 원본 파일의 마지막 수정 타임스탬프가 있으면 그걸 유지해요. 타임스탬프를 찾으려고 FileConstants.FILE_LAST_MODIFIED 헤더를 쓰는데, 이 헤더는 java.util.Date나 타임스탬프를 담은 long을 가질 수 있어요. 타임스탬프가 존재하고 이 옵션이 켜져 있으면 쓴 파일에 그 타임스탬프를 설정해요. 참고: 이 옵션은 파일 producer에만 적용되고 FTP 계열 producer에는 쓸 수 없어요. | false | boolean | | lazyStartProducer (producer (advanced)) | producer를 lazy(첫 메시지 시점)로 시작할지 여부예요. lazy 시작을 쓰면 producer가 시작 중 실패해서 라우트 시작이 실패할 수 있는 상황에서도 CamelContext와 라우트가 시작될 수 있고, 시작 실패 처리를 Camel의 라우팅 에러 핸들러로 넘길 수 있어요. 다만 첫 메시지를 처리하는 시점에 producer를 만들고 시작하는 데 시간이 좀 걸려 전체 처리 시간이 길어질 수 있는 점은 유의하세요. | false | boolean | | moveExistingFileStrategy (producer (advanced)) | fileExist=Move가 설정됐을 때 특수 이름 토큰으로 파일을 옮기는 데 쓰는 전략(Custom Strategy)이에요. 커스텀 전략을 제공하지 않으면 기본 구현이 쓰여요. | | FileMoveExistingStrategy | | autoCreate (advanced) | 파일 경로에 없는 디렉터리를 자동으로 만들어요. 파일 consumer에서는 시작 디렉터리 만들기를, 파일 producer에서는 파일이 쓰일 디렉터리 만들기를 뜻해요. | true | boolean | | autoCreateStepwise (advanced) | 디렉터리를 자동으로 만들 때 각 하위 디렉터리를 하나씩 단계적으로 만들지 여부예요. 일부 파일 공유의 보안 문제 때문에 필요할 수 있어요. | false | boolean | | browseLimit (advanced) | 브라우징용으로 메모리에 유지할 최대 메시지 수예요. 무제한은 0. | 100 | int | | bufferSize (advanced) | 파일을 쓸 때(FTP의 경우 파일을 업/다운로드할 때) 쓰는 버퍼 크기(바이트)예요. | 131072 | int | | copyAndDeleteOnRenameFail (advanced) | 파일 이름을 직접 바꾸지 못했을 때 복사 후 삭제로 폴백할지 여부예요. FTP 컴포넌트에는 이 옵션이 없어요. | true | boolean | | renameUsingCopy (advanced) | 이름 바꾸기 작업을 복사 후 삭제 전략으로 수행해요. 일반 rename 작업이 불안정한 환경(예: 서로 다른 파일 시스템이나 네트워크 사이)에서 주로 써요. 이 옵션이 copyAndDeleteOnRenameFail 파라미터보다 우선하는데, 그 파라미터는 추가 지연 후에 자동으로 복사·삭제 전략으로 폴백해요. | false | boolean | | synchronous (advanced) | 동기 처리를 엄격하게 쓸지 설정해요. | false | boolean | | antExclude (filter) | Ant 스타일 필터 제외예요. antInclude와 antExclude를 둘 다 쓰면 antExclude가 antInclude보다 우선해요. 여러 개는 쉼표로 구분해서 지정할 수 있어요. | | String | | antFilterCaseSensitive (filter) | ant 필터에 대소문자 구분 플래그를 설정해요. | true | boolean | | antInclude (filter) | Ant 스타일 필터 포함이에요. 여러 개는 쉼표로 구분해서 지정할 수 있어요. | | String | | eagerMaxMessagesPerPoll (filter) | maxMessagesPerPoll의 한도를 이른(eager) 방식으로 적용할지 제어해요. 이른 방식이면 파일을 스캔하는 동안 한도를 적용하고, false면 모든 파일을 스캔한 뒤 정렬해요. false로 하면 모든 파일을 먼저 정렬한 다음 폴링 한도를 적용할 수 있어요. 다만 정렬을 위해 모든 파일 정보가 메모리에 있어야 하니 메모리 사용량이 더 커진다는 점은 유의하세요. | true | boolean | | exclude (filter) | 파일 이름이 정규식 패턴과 매칭되면(대소문자 무시) 파일을 제외하는 데 써요. 플러스 기호 같은 기호를 쓴다면 엔드포인트 URI로 설정할 때 RAW() 문법으로 설정해야 해요. 자세한 내용은 configuring endpoint uris 문서를 보세요. | | String | | excludeExt (filter) | 파일 확장자 이름으로(대소문자 무시) 파일을 제외하는 데 써요. 예컨대 bak 파일을 제외하려면 excludeExt=bak으로 해요. 확장자는 쉼표로 여러 개 쓸 수 있어서, bak과 dat 파일을 제외하려면 excludeExt=bak,dat으로 해요. 파일 확장자는 모든 부분을 포함한다는 점에 유의하세요. 예컨대 mydata.tar.gz 파일의 확장자는 tar.gz이에요. 더 유연하게 쓰려면 include/exclude 옵션을 쓰세요. | | String | | filter (filter) | org.apache.camel.component.file.GenericFileFilter 클래스로 제공하는 플러그 가능 필터예요. accept() 메서드가 false를 반환하면 파일을 건너뛰어요. | | GenericFileFilter | | filterDirectory (filter) | Simple 언어 기반으로 디렉터리를 필터링해요. 예컨대 현재 날짜로 필터링하려면 ${date:now:yyyMMdd} 같은 간단한 날짜 패턴을 쓸 수 있어요. | | String | | filterFile (filter) | Simple 언어 기반으로 파일을 필터링해요. 예컨대 파일 크기로 필터링하려면 ${file:size} 5000처럼 쓸 수 있어요. | | String | | idempotent (filter) | Idempotent Consumer EIP 패턴을 써서 Camel이 이미 처리한 파일은 건너뛰게 하는 옵션이에요. 기본으로 1000개 항목을 담는 메모리 기반 LRUCache를 써요. noop=true면 같은 파일을 반복해서 소비하지 않도록 idempotent도 함께 켜져요. | false | Boolean | | idempotentEager (filter) | 파일 이름을 idempotent 리포지터리에 즉시(eagerly) 추가할지, exchange가 끝날 때까지 기다릴지 설정해요. | true | Boolean | | idempotentKey (filter) | 커스텀 idempotent 키를 쓰게 해줘요. 기본은 파일의 절대 경로예요. File Language를 쓸 수 있어서, 예컨대 파일 이름과 파일 크기를 키로 쓰려면 idempotentKey=${file:name}-${file:size}처럼 하면 돼요. | | String | | idempotentRepository (filter) | 플러그 가능한 리포지터리(org.apache.camel.spi.IdempotentRepository)예요. 지정하지 않고 idempotent가 true면 기본으로 MemoryIdempotentRepository를 써요. | | IdempotentRepository | | include (filter) | 파일 이름이 정규식 패턴과 매칭되면(대소문자 무시) 파일을 포함하는 데 써요. 플러스 기호 같은 기호를 쓴다면 엔드포인트 URI로 설정할 때 RAW() 문법으로 설정해야 해요. 자세한 내용은 configuring endpoint uris 문서를 보세요. | | String | | includeExt (filter) | 파일 확장자 이름으로(대소문자 무시) 파일을 포함하는 데 써요. 예컨대 txt 파일을 포함하려면 includeExt=txt으로 해요. 확장자는 쉼표로 여러 개 쓸 수 있어서, txt와 xml 파일을 포함하려면 includeExt=txt,xml으로 해요. 파일 확장자는 모든 부분을 포함한다는 점에 유의하세요. 예컨대 mydata.tar.gz 파일의 확장자는 tar.gz이에요. 더 유연하게 쓰려면 include/exclude 옵션을 쓰세요. | | String | | maxDepth (filter) | 디렉터리를 재귀적으로 처리할 때 탐색할 최대 깊이예요. | 2147483647 | int | | maxMessagesPerPoll (filter) | 폴링마다 모을 최대 메시지 수를 정의해요. 기본은 무제한이에요. 서버 시작 시 수천 개 파일이 있는 상황을 피하려고 예컨대 1000으로 한도를 정하는 데 쓸 수 있어요. 0이나 음수는 끄는 거예요. 참고: 이 옵션을 쓰면 File과 FTP 컴포넌트가 정렬 전에 한도를 적용해요. 예컨대 100000개 파일에 maxMessagesPerPoll=500을 쓰면 먼저 500개 파일만 집어 올린 다음 정렬해요. eagerMaxMessagesPerPoll 옵션을 false로 설정하면 모든 파일을 먼저 스캔한 다음 정렬하게 할 수 있어요. | | int | | minDepth (filter) | 디렉터리를 재귀적으로 처리할 때 처리를 시작할 최소 깊이예요. minDepth=1은 기본 디렉터리, minDepth=2는 첫 하위 디렉터리를 뜻해요. | | int | | move (filter) | 처리 후에 파일을 옮길 때 파일 이름을 동적으로 정하는 데 쓰는 표현식(Simple Language 같은)이에요. 파일을 .done 하위 디렉터리로 옮기려면 그냥 .done만 입력하면 돼요. | | String | | exclusiveReadLockStrategy (lock) | org.apache.camel.component.file.GenericFileExclusiveReadLockStrategy 구현으로 제공하는 플러그 가능 읽기 잠금(read-lock)이에요. | | GenericFileExclusiveReadLockStrategy | | readLock (lock) |
consumer가 파일에 배타적 읽기 잠금을 쥐고 있을 때만(즉 파일이 처리 중이거나 쓰이는 중이 아닐 때) 파일을 폴링하는 데 써요. Camel은 파일 잠금이 허용될 때까지 기다려요. 이 옵션이 제공하는 내장 전략은 이래요: - none - 읽기 잠금을 쓰지 않아요. - markerFile - Camel이 마커 파일(fileName.camelLock)을 만들고 그 파일을 잠가요. 이 옵션은 FTP 컴포넌트에서는 쓸 수 없어요. - changed - 파일 길이/수정 타임스탬프로 파일이 현재 복사 중인지 탐지해요. 판단에 최소 1초를 쓰기 때문에 다른 방식만큼 빠르게 파일을 소비할 수는 없지만, JDK IO API가 파일이 다른 프로세스에 쓰이는 중인지 늘 정확히 알 수 있는 건 아니라서 더 신뢰할 수 있을 때가 있어요. readLockCheckInterval 옵션으로 검사 빈도를 정할 수 있어요. - fileLock - java.nio.channels.FileLock을 쓰는 방식이에요. Windows OS와 FTP 컴포넌트에서는 쓸 수 없어요. mount/share로 원격 파일 시스템에 접근할 때는 그 파일 시스템이 분산 파일 잠금을 지원하지 않는 한 피해야 해요. - rename - 파일 이름 바꾸기를 시도해서 배타적 읽기 잠금을 얻을 수 있는지 검사하는 방식이에요. - idempotent - (file 컴포넌트 전용) idempotentRepository를 읽기 잠금으로 쓰는 방식이에요. idempotent 리포지터리 구현이 지원한다면 클러스터링을 지원하는 읽기 잠금을 쓸 수 있어요. - idempotent-changed - (file 컴포넌트 전용) idempotentRepository와 changed를 조합한 읽기 잠금이에요. idempotent 리포지터리 구현이 지원한다면 클러스터링을 지원하는 읽기 잠금을 쓸 수 있어요. - idempotent-rename - (file 컴포넌트 전용) idempotentRepository와 rename을 조합한 읽기 잠금이에요. idempotent 리포지터리 구현이 지원한다면 클러스터링을 지원하는 읽기 잠금을 쓸 수 있어요. 참고: 여러 읽기 잠금이 모두 클러스터 모드에 적합한 건 아니에요. 공유 파일 시스템에서 서로 다른 노드의 동시 consumer가 같은 파일을 두고 경쟁하는 상황에서는 말이죠. markerFile은 거의 원자적 작업으로 빈 마커 파일을 만들지만 클러스터에서 동작한다고 보장하긴 어려워요. fileLock이 더 나을 수 있지만 그러려면 파일 시스템이 분산 파일 잠금을 지원해야 하고요. idempotent 읽기 잠금은 idempotent 리포지터리가 Hazelcast Component나 Infinispan 같은 클러스터링을 지원하면 클러스터링을 지원할 수 있어요.
Enum values:
-
none
-
markerFile
-
fileLock
-
rename
-
changed
-
idempotent
-
idempotent-changed
-
idempotent-rename
| none | String | | readLockCheckInterval (lock) | 읽기 잠금이 지원하는 경우 읽기 잠금의 간격(밀리초)이에요. 이 간격으로 읽기 잠금 획득 시도 사이에 잠들어요. 예컨대 changed 읽기 잠금을 쓸 때 느린 쓰기에 대응하려고 간격을 더 길게 설정할 수 있어요. 기본 1초는 producer가 파일을 아주 느리게 쓰는 경우 너무 빠를 수 있어요. 참고: FTP의 기본 readLockCheckInterval은 5000이에요. readLockTimeout 값은 readLockCheckInterval보다 커야 하고, 대략 timeout이 check interval의 2배 이상이 되도록 잡는 게 경험칙이에요. 타임아웃에 걸리기 전에 읽기 잠금 과정이 잠금을 잡을 충분한 시간을 갖도록 하기 위해서예요. | 1000 | long | | readLockDeleteOrphanLockFiles (lock) | 마커 파일을 쓰는 읽기 잠금에서, Camel이 제대로 종료되지 않았을 때(JVM 크래시 같은) 파일 시스템에 남을 수 있는 고아(orphan) 읽기 잠금 파일을 시작 시 삭제할지 여부예요. false로 하면 고아 잠금 파일이 남아 있는 파일은 Camel이 집어 올리려 하지 않아요. 이는 다른 노드가 같은 공유 디렉터리에서 동시에 파일을 읽고 있는 경우에도 생길 수 있어요. | true | boolean | | readLockIdempotentReleaseAsync (lock) | 지연 해제(release) 작업을 동기로 할지 비동기로 할지 여부예요. 자세한 내용은 readLockIdempotentReleaseDelay 옵션을 보세요. | false | boolean | | readLockIdempotentReleaseAsyncPoolSize (lock) | 비동기 해제 작업을 쓸 때 예약 스레드 풀의 스레드 수예요. 기본 1개 코어 스레드로도 거의 모든 사용 사례에 충분해서, idempotent 리포지터리 갱신이 느리거나 처리할 파일이 아주 많은 경우가 아니면 더 크게 설정할 필요가 없어요. readLockIdempotentReleaseExecutorService 옵션으로 공유 스레드 풀을 설정하면 이 옵션은 쓰이지 않아요. 자세한 내용은 readLockIdempotentReleaseDelay 옵션을 보세요. | | int | | readLockIdempotentReleaseDelay (lock) | 해제 작업을 일정 밀리초 동안 지연할지 여부예요. 공유 idempotent 리포지터리를 쓰는 active/active 클러스터 시나리오에서 경합 조건 때문에 다른 노드가 같은 파일을 스캔해서 획득하지 못하게, 파일이 읽기 잠금 상태로 간주되는 시간 창을 넓히려고 해제 작업을 지연하는 데 쓸 수 있어요. 해제 작업의 시간 창을 넓히면 이런 상황을 예방하는 데 도움이 돼요. 참고로 지연은 readLockRemoveOnCommit을 true로 설정한 경우에만 필요해요. | | int | | readLockIdempotentReleaseExecutorService (lock) | 비동기 해제 작업에 커스텀 공유 스레드 풀을 쓰게 해줘요. 자세한 내용은 readLockIdempotentReleaseDelay 옵션을 보세요. | | ScheduledExecutorService | | readLockLoggingLevel (lock) |
읽기 잠금을 획득하지 못했을 때 쓰는 로깅 레벨이에요. 기본은 DEBUG예요. 이 레벨을 바꿀 수 있어서, 예컨대 OFF로 하면 로그를 아예 남기지 않아요. 이 옵션은 changed, fileLock, idempotent, idempotent-changed, idempotent-rename, rename 타입의 readLock에만 적용돼요.
Enum values:
-
TRACE
-
DEBUG
-
INFO
-
WARN
-
ERROR
-
OFF
| DEBUG | LoggingLevel | | readLockMarkerFile (lock) | changed, rename 또는 배타적 읽기 잠금 타입에서 마커 파일을 쓸지 여부예요. 기본으로 마커 파일도 써서 다른 프로세스가 같은 파일을 집어 올리지 않게 막아요. Camel 애플리케이션이 파일 시스템에 마커 파일을 쓰지 않게 하고 싶은 등의 경우 이 옵션을 false로 하면 이 동작을 끌 수 있어요. | true | boolean | | readLockMinAge (lock) | readLock=changed일 때만 적용되는 옵션이에요. 읽기 잠금을 시도하기 전에 파일이 지녀야 하는 최소 나이(경과 시간)를 지정할 수 있어요. 예컨대 readLockMinAge=300s로 하면 파일이 최소 5분은 되어야 해요. 조건을 만족하는 파일에만 잠금을 시도하니 changed 읽기 잠금이 빨라질 수 있어요. | 0 | long | | readLockMinLength (lock) | readLock=changed일 때만 적용되는 옵션이에요. 최소 파일 길이를 설정할 수 있어요. 기본으로 Camel은 파일에 데이터가 있으리라 기대하기 때문에 기본값은 1이에요. 이 옵션을 0으로 하면 길이가 0인 파일도 소비할 수 있어요. | 1 | long | | readLockRemoveOnCommit (lock) | readLock=idempotent일 때만 적용되는 옵션이에요. 파일 처리가 성공하고 commit이 일어났을 때 idempotent 리포지터리에서 파일 이름 항목을 제거할지 지정해요. 기본은 제거하지 않는 거예요. 그래야 경합 조건이 생기지 않아서 다른 활성 노드가 그 파일을 획득하려 하지 않아요. 대신 idempotent 리포지터리가 축(eviction) 전략을 지원한다면 X분 후에 파일 이름 항목을 축출하도록 설정할 수 있어요. 이러면 경합 조건 문제가 없어요. 자세한 내용은 readLockIdempotentReleaseDelay 옵션을 보세요. | false | boolean | | readLockRemoveOnRollback (lock) | readLock=idempotent일 때만 적용되는 옵션이에요. 파일 처리가 실패하고 rollback이 일어났을 때 idempotent 리포지터리에서 파일 이름 항목을 제거할지 지정해요. false면 파일 이름 항목이 확인(commit한 것처럼)돼요. | true | boolean | | readLockTimeout (lock) | 읽기 잠금이 지원하는 경우 읽기 잠금의 타임아웃(밀리초) 옵션이에요. 읽기 잠금을 얻지 못하고 타임아웃이 발생하면 Camel은 그 파일을 건너뛰고, 다음 폴링 때 다시 시도해서 이번엔 잠금을 얻을 수도 있어요. 0 이하 값은 영원히 기다린다는 뜻이에요. 현재 fileLock, changed, rename이 타임아웃을 지원해요. 참고: FTP의 기본 readLockTimeout은 10000이 아니라 20000이에요. readLockTimeout 값은 readLockCheckInterval보다 커야 하고, 대략 timeout이 check interval의 2배 이상이 되도록 잡는 게 경험칙이에요. 타임아웃에 걸리기 전에 읽기 잠금 과정이 잠금을 잡을 충분한 시간을 갖도록 하기 위해서예요. | 10000 | long | | backoffErrorThreshold (scheduler) | backoffMultiplier가 작동하기 전에 연속으로 몇 번의 오류 폴링(오류로 실패한 폴링)이 일어나야 하는지예요. | | int | | backoffIdleThreshold (scheduler) | backoffMultiplier가 작동하기 전에 연속으로 몇 번의 유휴(idle) 폴링이 일어나야 하는지예요. | | int | | backoffMultiplier (scheduler) | 연속으로 유휴/오류 폴링이 여러 번 일어나면 예약 폴링 consumer가 백오프하게 해줘요. 승수(multiplier)는 다음 실제 시도 전에 건너뛸 폴링 횟수예요. 이 옵션을 쓸 때는 backoffIdleThreshold와(또는) backoffErrorThreshold도 함께 설정해야 해요. | | int | | delay (scheduler) | 다음 폴링 전의 대기 시간(밀리초)이에요. | 500 | long | | greedy (scheduler) | greedy가 켜져 있으면 이전 실행이 1개 이상의 메시지를 폴링했을 때 ScheduledPollConsumer가 바로 다시 실행돼요. | false | boolean | | initialDelay (scheduler) | 첫 폴링이 시작되기 전의 대기 시간(밀리초)이에요. | 1000 | long | | repeatCount (scheduler) | 실행(fire) 횟수의 최대 한도를 지정해요. 1로 하면 스케줄러가 한 번만 실행되고, 5로 하면 다섯 번만 실행돼요. 0이나 음수는 영원히 실행한다는 뜻이에요. | 0 | long | | runLoggingLevel (scheduler) |
consumer가 폴링할 때 시작/완료 로그 한 줄을 남겨요. 이 옵션으로 그 로그의 레벨을 설정할 수 있어요.
Enum values:
-
TRACE
-
DEBUG
-
INFO
-
WARN
-
ERROR
-
OFF
| TRACE | LoggingLevel | | scheduledExecutorService (scheduler) | consumer가 쓸 커스텀/공유 스레드 풀을 설정하게 해줘요. 기본으로 각 consumer는 자기만의 단일 스레드 풀을 가져요. | | ScheduledExecutorService | | scheduler (scheduler) | camel-spring이나 camel-quartz 컴포넌트의 cron 스케줄러를 쓰게 해줘요. 내장 스케줄러는 spring 또는 quartz 값을 쓰세요. | none | Object | | schedulerProperties (scheduler) | 커스텀 스케줄러나 Quartz, Spring 기반 스케줄러를 쓸 때 추가 프로퍼티를 설정해요. 접두어가 scheduler인 다중 값 옵션이에요. | | Map | | startScheduler (scheduler) | 스케줄러를 자동으로 시작할지 여부예요. | true | boolean | | timeUnit (scheduler) |
initialDelay와 delay 옵션의 시간 단위예요.
Enum values:
-
NANOSECONDS
-
MICROSECONDS
-
MILLISECONDS
-
SECONDS
-
MINUTES
-
HOURS
-
DAYS
| MILLISECONDS | TimeUnit | | useFixedDelay (scheduler) | fixed delay를 쓸지 fixed rate를 쓸지 제어해요. 자세한 내용은 JDK의 ScheduledExecutorService를 보세요. | true | boolean | | shuffle (sort) | 파일 목록을 섞어요(무작위 순서로 정렬). | false | boolean | | sortBy (sort) | File Language로 내장 정렬을 써요. 중첩 정렬을 지원해서 파일 이름으로 정렬하고 두 번째 그룹으로 수정 날짜로 정렬하는 식이 가능해요. | | String | | sorter (sort) | java.util.Comparator 클래스로 제공하는 플러그 가능 정렬기예요. | | Comparator |
메시지 헤더
File 컴포넌트가 지원하는 메시지 헤더는 아래와 같아요:
| Name | Description | Default | Type |
|---|---|---|---|
CamelFileLength (consumer) Constant: FILE_LENGTH |
파일 크기를 담은 long 값이에요. | long | |
CamelFileLastModified (consumer) Constant: FILE_LAST_MODIFIED |
파일의 마지막 수정 타임스탬프를 담은 Long 값이에요. | long | |
CamelFileLocalWorkPath (producer) Constant: FILE_LOCAL_WORK_PATH |
로컬 작업 경로예요. | File | |
CamelFileNameOnly (consumer) Constant: FILE_NAME_ONLY |
파일 이름만(선행 경로 없는 이름) 담아요. | String | |
CamelFileName (common) Constant: FILE_NAME |
(producer) 쓸 파일의 이름을 지정해요(엔드포인트 디렉터리 기준 상대 경로). 이 이름은 String, 파일 또는 simple Language 표현식이 담긴 String, 또는 Expression 객체일 수 있어요. null이면 Camel이 메시지 고유 ID 기반으로 파일 이름을 자동 생성해요. (consumer) 엔드포인트에 설정된 시작 디렉터리를 기준으로 한 상대 파일 경로로 나타낸 소비 파일의 이름이에요. | String | |
CamelFileNameConsumed (consumer) Constant: FILE_NAME_CONSUMED |
소비된 파일의 이름이에요. | String | |
CamelFileAbsolute (consumer) Constant: FILE_ABSOLUTE |
소비한 파일이 절대 경로인지 나타내는 boolean 옵션이에요. 상대 경로라면 보통 false여야 해요. 절대 경로는 보통 쓰지 않지만, 파일을 절대 경로로 옮길 수 있게 move 옵션에 추가했어요. 다른 곳에서도 쓸 수 있어요. | Boolean | |
CamelFileAbsolutePath (consumer) Constant: FILE_ABSOLUTE_PATH |
파일의 절대 경로예요. 상대 파일이라면 이 경로에는 대신 상대 경로가 들어가요. | String | |
CamelFileExtendedAttributes (consumer) Constant: FILE_EXTENDED_ATTRIBUTES |
파일의 확장 속성(extended attributes)이에요. | Map | |
CamelFileContentType (consumer) Constant: FILE_CONTENT_TYPE |
파일의 content type이에요. | String | |
CamelFilePath (consumer) Constant: FILE_PATH |
파일 경로예요. 상대 파일이면 시작 디렉터리, 절대 파일이면 절대 경로예요. | String | |
CamelFileRelativePath (consumer) Constant: FILE_RELATIVE_PATH |
상대 경로예요. | String | |
CamelFileParent (common) Constant: FILE_PARENT |
부모 경로예요. | String | |
CamelFileNameProduced (producer) Constant: FILE_NAME_PRODUCED |
쓰여진 출력 파일의 실제 절대 파일 경로(path name)예요. 이 헤더는 Camel이 설정하며, 쓰인 파일 이름을 최종 사용자에게 알려주는 게 목적이에요. | String | |
CamelOverruleFileName (producer) Constant: OVERRULE_FILE_NAME |
CamelFileName 헤더를 덮어쓰고 그 값을 대신 쓰는 데 쓰여요(producer가 파일을 쓴 뒤 이 헤더를 제거하므로 한 번만 적용돼요). 값은 String만 가능해요. fileName 옵션이 설정돼 있어도 이 값은 여전히 평가된다는 점에 유의하세요. | Object | |
CamelFileInitialOffset (consumer) Constant: INITIAL_OFFSET |
초기 오프셋을 담은 long 값이에요. | long | |
CamelFileChecksum (producer) Constant: FILE_CHECKSUM |
checksumAlgorithm을 쓸 때 쓰인 파일의 체크섬이에요. | String |
팁 파일 producer의 기본 동작
기본적으로 같은 이름의 기존 파일이 있으면 그 파일을 덮어써요.
사용법
Move, Pre Move, Delete 동작
기본으로 Camel은 소비한 파일을, 파일을 소비한 디렉터리 기준의 .camel 하위 폴더로 옮겨요.
처리 후에 파일을 삭제하고 싶다면 라우트는 이렇게 돼요:
from("file://inbox?delete=true").to("bean:handleOrder");
<route>
<from uri="file://inbox?delete=true"/>
<to uri="bean:handleOrder"/>
</route>
- route:
from:
uri: file://inbox
parameters:
delete: true
steps:
- to:
uri: bean:handleOrder
참고 아래에 디렉터리에서 읽고 기본 move 동작을 보여주는 예시가 있어요.
Move, Delete와 라우팅 과정
move나 delete 동작은 라우팅이 끝난 뒤에 실행돼요. 그래서 Exchange를 처리하는 동안에는 파일이 아직 inbox 폴더에 있어요.
예시로 살펴볼게요:
from("file://inbox?move=.done").to("bean:handleOrder");
<route>
<from uri="file://inbox?move=.done"/>
<to uri="bean:handleOrder"/>
</route>
- route:
from:
uri: file://inbox
parameters:
move: .done
steps:
- to:
uri: bean:handleOrder
파일을 inbox 폴더에 놓으면 파일 consumer가 이를 알아채고 새 FileExchange를 만들어 handleOrder bean으로 라우팅해요. bean은 그 File 객체를 처리합니다. 이 시점에도 파일은 여전히 inbox 폴더에 있어요. bean이 끝나고 라우트가 완료되면 파일 consumer가 move 동작을 수행해서 파일을 .done 하위 폴더로 옮겨요.
move와 preMove 옵션은 디렉터리 이름으로 간주돼요. 다만 File Language나 Simple 같은 표현식을 쓰면 표현식을 평가한 결과가 쓸 파일 이름이 돼요. 예컨대 이렇게 설정하면:
move=../backup/copy-of-${file:name}
그러면 File Language를 쓰는 거예요. 쓸 파일 이름을 반환하는 데 쓰는데, 상대 경로일 수도 있고 절대 경로일 수도 있어요. 상대 경로면 파일을 소비한 폴더 안에서 하위 폴더로 디렉터리를 만들어요.
Move와 Pre Move 동작
파일을 처리하기 전에 옮기는 preMove 동작도 있어요. 이러면 파일이 이 하위 폴더로 옮겨가면서, 처리 전에 어떤 파일들이 스캔됐는지 표시할 수 있어요.
from("file://inbox?preMove=inprogress").to("bean:handleOrder");
<route>
<from uri="file://inbox?preMove=inprogress"/>
<to uri="bean:handleOrder"/>
</route>
- route:
from:
uri: file://inbox
parameters:
preMove: inprogress
steps:
- to:
uri: bean:handleOrder
preMove와 일반 move를 조합할 수도 있어요:
from("file://inbox?preMove=inprogress&move=.done").to("bean:handleOrder");
<route>
<from uri="file://inbox?preMove=inprogress&move=.done"/>
<to uri="bean:handleOrder"/>
</route>
- route:
from:
uri: file://inbox
parameters:
preMove: inprogress
move: .done
steps:
- to:
uri: bean:handleOrder
이렇게 하면 처리 중에는 파일이 inprogress 폴더에 있고, 처리가 끝나면 .done 폴더로 옮겨져요.
Move와 PreMove 옵션 세밀 제어
move와 preMove 옵션은 Expression 기반이라서 File Language의 모든 기능으로 디렉터리와 이름 패턴을 고급 설정할 수 있어요. 실제로 Camel은 여러분이 입력한 디렉터리 이름을 내부적으로 File Language 표현식으로 변환해요. 그래서 move=.done을 쓰면 Camel이 이를 ``${file:parent}/.done/${file:onlyname}`으로 변환해요.
이 변환은 Camel이 옵션 값에 여러분이 직접 ${ }를 넣지 않았다고 판단할 때만 일어나요. ${ }를 입력하면 Camel이 변환하지 않으므로 모든 기능을 그대로 쓸 수 있어요.
그래서 오늘 날짜를 패턴으로 삼아 파일을 백업 폴더로 옮기고 싶다면 이렇게 하면 돼요:
move=backup/${date:now:yyyyMMdd}/${file:name}
moveFailed 알아보기
moveFailed 옵션은 성공적으로 처리하지 못한 파일을 여러분이 고른 오류 폴더 같은 다른 위치로 옮기게 해줘요. 예컨대 타임스탬프가 붙은 오류 폴더로 파일을 옮기려면 moveFailed=/error/${file:name.noext}-${date:now:yyyyMMddHHmmssSSS}.${file:ext}를 쓸 수 있어요.
팁 더 많은 예시는 File Language를 보세요.
Exchange 프로퍼티(file consumer 전용)
파일 consumer는 BatchConsumer를 구현해서 폴링한 파일을 배치로 묶는 걸 지원해요. 배칭이란 Camel이 Exchange에 다음 프로퍼티들을 추가로 담아준다는 뜻이에요:
| Property | Description |
|---|---|
CamelBatchSize |
이번 배치에서 폴링한 파일의 총 개수예요. |
CamelBatchIndex |
배치의 현재 인덱스예요. 0부터 시작해요. |
CamelBatchComplete |
배치에서 마지막 Exchange임을 나타내는 boolean 값이에요. 마지막 항목에서만 true예요. |
이걸 활용하면 예컨대 이번 배치에 파일이 몇 개 있는지 알 수 있고, 그 개수만큼 파일을 Aggregator2로 모으게 할 수도 있어요.
charset 사용하기
charset 옵션으로 consumer와 producer 엔드포인트 양쪽에서 파일의 인코딩을 설정할 수 있어요. 예컨대 utf-8 파일을 읽어서 iso-8859-1로 변환하고 싶다면 이렇게 해요:
from("file:inbox?charset=utf-8")
.to("file:outbox?charset=iso-8859-1")
<route>
<from uri="file:inbox?charset=utf-8"/>
<to uri="file:outbox?charset=iso-8859-1"/>
</route>
- route:
from:
uri: file:inbox
parameters:
charset: utf-8
steps:
- to:
uri: file:outbox
parameters:
charset: iso-8859-1
라우트에서 convertBodyTo를 쓸 수도 있어요. 아래 예시에서는 입력 파일이 여전히 utf-8 형식이지만, 파일 내용을 iso-8859-1 형식의 바이트 배열로 변환하고 싶어요. 그리고 bean이 그 데이터를 처리하게 한 뒤, 현재 문자셋으로 outbox 폴더에 내용을 쓰게 해요.
from("file:inbox?charset=utf-8")
.convertBodyTo(byte[].class, "iso-8859-1")
.to("bean:myBean")
.to("file:outbox");
<route>
<from uri="file:inbox?charset=utf-8"/>
<convertBodyTo type="byte[]" charset="iso-8859-1"/>
<to uri="bean:myBean"/>
<to uri="file:outbox"/>
</route>
- route:
from:
uri: file:inbox
parameters:
charset: utf-8
steps:
- convertBodyTo:
type: "byte[]"
charset: iso-8859-1
- to:
uri: bean:myBean
- to:
uri: file:outbox
consumer 엔드포인트에서 charset을 생략하면 Camel은 파일의 문자셋을 모르고 기본으로 UTF-8을 써요. 하지만 JVM 시스템 프로퍼티 org.apache.camel.default.charset 키로 기본 인코딩을 다른 값으로 바꿔서 쓸 수 있어요.
아래 예시에서 파일이 UTF-8 인코딩이 아니라면 문제가 될 수 있어요. UTF-8이 파일을 읽을 때의 기본 인코딩이거든요. 이 예시에서 파일을 쓸 때는 내용이 이미 바이트 배열로 변환돼 있어서 그대로(추가 인코딩 없이) 쓰여요.
from("file:inbox")
.convertBodyTo(byte[].class, "iso-8859-1")
.to("bean:myBean")
.to("file:outbox");
<route>
<from uri="file:inbox"/>
<convertBodyTo type="byte[]" charset="iso-8859-1"/>
<to uri="bean:myBean"/>
<to uri="file:outbox"/>
</route>
- route:
from:
uri: file:inbox
steps:
- convertBodyTo:
type: "byte[]"
charset: iso-8859-1
- to:
uri: bean:myBean
- to:
uri: file:outbox
파일을 쓸 때 인코딩을 동적으로 바꾸고 제어할 수도 있어요. Exchange.CHARSET_NAME 키로 exchange에 프로퍼티를 설정하면 돼요. 예컨대 아래 라우트에서는 메시지 헤더 값을 가져와 프로퍼티를 설정해요.
from("file:inbox")
.convertBodyTo(byte[].class, "iso-8859-1")
.to("bean:myBean")
.setProperty(Exchange.CHARSET_NAME, header("someCharsetHeader"))
.to("file:outbox");
<route>
<from uri="file:inbox"/>
<convertBodyTo type="byte[]" charset="iso-8859-1"/>
<to uri="bean:myBean"/>
<setProperty name="CamelCharsetName">
<header>someCharsetHeader</header>
</setProperty>
<to uri="file:outbox"/>
</route>
- route:
from:
uri: file:inbox
steps:
- convertBodyTo:
type: "byte[]"
charset: iso-8859-1
- to:
uri: bean:myBean
- setProperty:
name: CamelCharsetName
header: someCharsetHeader
- to:
uri: file:outbox
가능하면 단순하게 유지하는 걸 권해요. 같은 인코딩의 파일을 집어 올리고 특정 인코딩으로 파일을 쓰고 싶다면 엔드포인트의 charset 옵션을 쓰는 편이 좋아요.
엔드포인트에 charset 옵션을 명시적으로 설정했다면 Exchange.CHARSET_NAME 프로퍼티와 무관하게 그 설정이 쓰인다는 점에 유의하세요.
문제가 생기면 org.apache.camel.component.file에 DEBUG 로깅을 켜면 돼요. Camel이 특정 문자셋으로 파일을 읽거나 쓸 때 로그를 남겨요. 예컨대 아래 라우트는 다음처럼 기록해요:
from("file:inbox?charset=utf-8")
.to("file:outbox?charset=iso-8859-1")
<route>
<from uri="file:inbox?charset=utf-8"/>
<to uri="file:outbox?charset=iso-8859-1"/>
</route>
- route:
from:
uri: file:inbox
parameters:
charset: utf-8
steps:
- to:
uri: file:outbox
parameters:
charset: iso-8859-1
그리고 로그는 이렇게 나와요:
DEBUG GenericFileConverter - Read file /Users/davsclaus/workspace/camel/camel-core/target/charset/input/input.txt with charset utf-8 DEBUG FileOperations - Using Reader to write file: target/charset/output.txt with charset: iso-8859-1
폴더와 파일 이름의 흔한 함정
Camel이 파일을 생산(파일 쓰기)할 때는 파일 이름을 원하는 대로 정하는 데 영향을 주는 함정이 몇 가지 있어요. 기본으로 Camel은 메시지 ID를 파일 이름으로 쓰는데, 메시지 ID는 보통 생성된 고유 ID라서 ID-MACHINENAME-2443-1211718892437-1-0 같은 파일 이름이 나올 수 있어요. 그런 파일 이름이 원치 않는 거라면 CamelFileName 메시지 헤더로 파일 이름을 지정해야 해요. 상수 Exchange.FILE_NAME을 쓸 수도 있어요.
아래 예시 코드는 메시지 ID를 파일 이름으로 써서 파일을 만들어요:
from("direct:report").to("file:target/reports");
<route>
<from uri="direct:report"/>
<to uri="file:target/reports"/>
</route>
- route:
from:
uri: direct:report
steps:
- to:
uri: file:target/reports
report.txt를 파일 이름으로 쓰려면(CamelFileName 헤더 사용) 이렇게 해야 해요:
from("direct:report").setHeader("CamelFileName", constant("report.txt")).to("file:target/reports");
<route>
<from uri="direct:report"/>
<setHeader name="CamelFileName">
<constant>report.txt</constant>
</setHeader>
<to uri="file:target/reports"/>
</route>
- route:
from:
uri: direct:report
steps:
- setHeader:
name: CamelFileName
constant: report.txt
- to:
uri: file:target/reports
그리고 fileName URI 옵션으로 엔드포인트에 파일 이름을 설정하는 문법도 있어요.
from("direct:report").to("file:target/reports/?fileName=report.txt");
<route>
<from uri="direct:report"/>
<to uri="file:target/reports/?fileName=report.txt"/>
</route>
- route:
from:
uri: direct:report
steps:
- to:
uri: file:target/reports/
parameters:
fileName: report.txt
파일 이름 표현식
파일 이름은 expression 옵션으로 설정하거나, CamelFileName 헤더에 문자열 기반 File Language 표현식으로 설정할 수 있어요. 문법과 예시는 File Language를 보세요.
다른 애플리케이션이 파일을 직접 놓는 폴더에서 파일 소비하기
다른 애플리케이션도 파일을 쓰는 폴더에서 파일을 소비한다면 주의하세요. 여러 readLock 옵션을 살펴보고 사용 사례에 맞는 걸 고르세요. 다만 가장 좋은 방법은 다른 폴더에 쓰고, 쓰기가 끝나면 drop 폴더로 파일을 옮기는 거예요. 하지만 drop 폴더에 파일을 직접 쓴다면 changed 옵션이 파일이 쓰이거나 복사되고 있는지 더 잘 탐지할 수 있어요. 일정 기간 동안 파일 크기/수정 여부가 변하는지 보는 파일 변경 알고리즘을 쓰기 때문이에요. 다른 readLock 옵션들은 Java File API에 의존하는데, 안타깝게도 이 API는 이런 탐지를 늘 잘하는 건 아니에요. doneFileName 옵션도 살펴보세요. 마커 파일(done 파일)을 써서 파일이 끝났고 소비할 준비가 됐음을 알리는 방식이에요.
심볼릭 링크와 시작 디렉터리
jailStartingDirectory가 켜져 있으면(기본값) 시작 디렉터리 안에 파일이 머무는지 확인할 때 심볼릭 링크를 실제 대상으로 풀어서 확인해요. consumer는 시작 디렉터리 밖으로 풀리는 링크인 파일을(recursive=true일 때는 디렉터리를) 건너뛰고(처음 발견 시 WARN으로 기록), producer는 그런 링크를 통해서 쓰는 데 실패해요. 시작 디렉터리 안으로 풀리는 링크와 링크 자체가 시작 디렉터리인 경우는 평소처럼 동작해요. 애플리케이션이 다른 디렉터리로 이어지는 링크를 따라가는 데 의존한다면 jailStartingDirectory=false로 설정하세요. 확인과 그 뒤의 읽기/쓰기는 원자적이지 않으니 디렉터리 권한도 여전히 중요해요.
Done 파일
done 파일 사용하기
아래 done 파일 쓰기 섹션도 참고하세요.
_done 파일_이 존재할 때만 파일을 소비하고 싶다면 엔드포인트에 doneFileName 옵션을 쓰면 돼요.
from("file:bar?doneFileName=done");
<from uri="file:bar?doneFileName=done"/>
- from:
uri: file:bar
parameters:
doneFileName: done
steps:
- log:
message: "${header.CamelFileName}"
대상 파일과 같은 디렉터리에 _done 파일_이 존재할 때만 bar 폴더에서 파일을 소비해요. Camel은 파일 소비가 끝나면 _done 파일_을 자동으로 삭제해요.
참고
noop=true가 설정돼 있으면 Camel은 _done 파일_을 자동으로 삭제하지 않아요.
하지만 더 흔한 방식은 대상 파일마다 하나의 _done 파일_을 두는 거예요. 즉 1:1 대응이에요. 이러려면 doneFileName 옵션에서 동적 플레이스홀더를 써야 해요. 현재 Camel은 file:name과 file:name.noext 두 동적 토큰을 지원하고, ${ }로 감싸야 해요. consumer는 done 파일 이름의 정적 부분을 접두어나 접미어 둘 중 하나로만 지원해요(둘 다는 안 돼요).
from("file:bar?doneFileName=${file:name}.done");
이 예시에서는 이름이 파일 이름.done인 _done 파일_이 존재할 때만 파일을 폴링해요. 예컨대:
-
hello.txt: 소비할 파일 -
hello.txt.done: 짝이 되는done파일
from("file:bar?doneFileName=ready-${file:name}");
_done 파일_에 접두어를 쓸 수도 있어요. 예컨대:
-
hello.txt: 소비할 파일 -
ready-hello.txt: 짝이 되는done파일
done 파일 쓰기
파일을 쓴 뒤, 파일이 끝났고 쓰기가 완료됐음을 다른 쪽에 알리는 표시로 _done 파일_을 추가로 쓰고 싶을 수 있어요. 그러려면 파일 producer 엔드포인트에 doneFileName 옵션을 쓰면 돼요.
.to("file:bar?doneFileName=done");
<to uri="file:bar?doneFileName=done"/>
- route:
from:
uri: direct:start
steps:
- to:
uri: file:bar
parameters:
doneFileName: done
대상 파일과 같은 디렉터리에 done이라는 이름의 파일이 만들어져요.
하지만 더 흔한 방식은 대상 파일마다 하나의 _done 파일_을 두는 거예요. 즉 1:1 대응이에요. 이러려면 doneFileName 옵션에서 동적 플레이스홀더를 써야 해요. 현재 Camel은 file:name과 file:name.noext 두 동적 토큰을 지원해요. ${ }로 감싸야 합니다:
.to("file:bar?doneFileName=done-${file:name}");
예컨대 대상 파일이 foo.txt였다면 같은 디렉터리에 done-foo.txt라는 이름의 파일이 만들어져요.
.to("file:bar?doneFileName=${file:name}.done");
예컨대 대상 파일이 foo.txt였다면 같은 디렉터리에 foo.txt.done이라는 이름의 파일이 만들어져요.
.to("file:bar?doneFileName=${file:name.noext}.done");
예컨대 대상 파일이 foo.txt였다면 같은 디렉터리에 foo.done이라는 이름의 파일이 만들어져요.
flatten 사용하기
파일을 outputdir 디렉터리에 소스 디렉터리 구조와 무관하게 같은 디렉터리에 담고 싶다면(예: 경로를 평탄화) 파일 producer 쪽에 flatten=true 옵션을 추가해요:
from("file://inputdir/?recursive=true&delete=true").to("file://outputdir?flatten=true")
<route>
<from uri="file://inputdir/?recursive=true&delete=true"/>
<to uri="file://outputdir?flatten=true"/>
</route>
- route:
from:
uri: file://inputdir/
parameters:
recursive: true
delete: true
steps:
- to:
uri: file://outputdir
parameters:
flatten: true
결과 출력 구조는 이렇게 돼요:
outputdir/foo.txt outputdir/bar.txt
파일 쓰기
Camel은 파일을 쓸 수도 있어요. 즉 파일을 생산할 수도 있어요. 아래 예시에서는 SEDA 큐로 받은 리포트들을 처리한 뒤 디렉터리에 써요.
Exchange.FILE_NAME으로 하위 디렉터리에 쓰기
라우트 하나로 파일을 원하는 수만큼 하위 디렉터리에 쓸 수 있어요. 라우트가 이렇게 설정돼 있다면:
from("seda:reports")
.to("bean:myBean")
.to("file:/rootDirectory");
<route>
<from uri="seda:reports"/>
<to uri="bean:myBean"/>
<to uri="file:/rootDirectory"/>
</route>
- route:
from:
uri: seda:reports
steps:
- to:
uri: bean:myBean
- to:
uri: file:/rootDirectory
myBean이 Exchange.FILE_NAME 헤더를 이런 값으로 설정하게 할 수 있어요:
Exchange.FILE_NAME = hello.txt => /rootDirectory/hello.txt Exchange.FILE_NAME = foo/bye.txt => /rootDirectory/foo/bye.txt
라우트 하나로 여러 목적지에 파일을 쓸 수 있어요.
최종 목적지 기준 임시 디렉터리를 거쳐 파일 쓰기
파일을 일시적으로 목적지 디렉터리 기준의 어떤 디렉터리에 써야 할 때가 있어요. 이런 상황은 보통 필터링 기능이 제한적인 외부 프로세스가 여러분이 쓰는 디렉터리에서 읽을 때 생겨요. 아래 예시에서 파일은 /var/myapp/filesInProgress 디렉터리에 쓰이고, 데이터 전송이 끝나면 원자적으로 /var/myapp/finalDirectory 디렉터리로 옮겨져요.
from("direct:start").
to("file:///var/myapp/finalDirectory?tempPrefix=/../filesInProgress/");
<route>
<from uri="direct:start"/>
<to uri="file:///var/myapp/finalDirectory?tempPrefix=/../filesInProgress/"/>
</route>
- route:
from:
uri: direct:start
steps:
- to:
uri: file:///var/myapp/finalDirectory
parameters:
tempPrefix: "/../filesInProgress/"
같은 파일을 두 번 읽지 않기(idempotent consumer)
Camel은 컴포넌트 안에서 Idempotent Consumer를 직접 지원해서 이미 처리한 파일은 건너뛰어요. idempotent=true 옵션으로 이 기능을 켤 수 있어요.
from("file://inbox?idempotent=true").to("...");
<from uri="file://inbox?idempotent=true"/>
- from:
uri: file://inbox
parameters:
idempotent: true
steps:
- log:
message: "${header.CamelFileName}"
Camel은 중복 파일을 탐지하려고 절대 파일 이름을 idempotent 키로 써요. idempotentKey 옵션에 표현식을 넣어 이 키를 바꿀 수 있어요. 예컨대 이름과 파일 크기를 둘 다 키로 쓰려면 이렇게 해요:
from("file://inbox?idempotent=true&idempotentKey=${file:name}-${file:size}")
.to("bean:processInbox");
<route>
<from uri="file://inbox?idempotent=true&idempotentKey=${file:name}-${file:size}"/>
<to uri="bean:processInbox"/>
</route>
- route:
from:
uri: file://inbox
parameters:
idempotent: true
idempotentKey: "${file:name}-${file:size}"
steps:
- to:
uri: bean:processInbox
기본으로 Camel은 소비한 파일을 추적하려고 메모리 내 저장소를 써요. 최대 1000개 항목을 담는 LRU 캐시예요. idempotentRepository 옵션으로 이 저장소의 자체 구현을 연결할 수 있어요. 값에 # 기호를 써서 지정한 id를 가진 Registry의 bean을 참조한다는 걸 나타내면 돼요.
from("file://inbox?idempotent=true&idempotentRepository=#myStore")
.to("bean:processInbox");
<!-- define our store as a plain spring bean -->
<bean id="myStore" class="com.mycompany.MyIdempotentStore"/>
<route>
<from uri="file://inbox?idempotent=true&idempotentRepository=#myStore"/>
<to uri="bean:processInbox"/>
</route>
- from:
uri: file://inbox
parameters:
idempotent: true
idempotentRepository: "#myStore"
steps:
- to:
uri: bean:processInbox
Camel은 이전에 소비한 적이 있어서 파일을 건너뛸 때 DEBUG 레벨로 기록해요:
DEBUG FileConsumer is idempotent and the file has been consumed before. Will skip this file: target\idempotent\report.txt
Idempotent 리포지터리
파일 기반 idempotent 리포지터리 사용하기
이 섹션에서는 기본으로 쓰이는 메모리 기반 리포지터리 대신 파일 기반 idempotent 리포지터리 org.apache.camel.processor.idempotent.FileIdempotentRepository를 써요. 이 리포지터리는 파일 리포지터리를 읽지 않으려고 1차 캐시(first level cache)를 써요. 1차 캐시의 내용을 저장할 때만 파일 리포지터리를 씁니다. 덕분에 리포지터리가 서버 재시작을 견딜 수 있어요. 시작할 때 파일 내용을 1차 캐시로 불러와요. 파일 구조는 기본적인 수준으로, 키를 파일 안에서 별도 줄에 저장해요. 기본으로 파일 저장소에는 1mb 크기 한도가 있어요. 파일이 더 커지면 Camel이 파일 저장소를 잘라내고(truncate), 1차 캐시 내용을 새 빈 파일로 내보내서(flush) 내용을 다시 만들어요.
Spring XML로 리포지터리를 설정해서 파일 idempotent 리포지터리를 만들고, 파일 consumer가 우리 리포지터리를 쓰게 idempotentRepository에 # 기호로 Registry 조회를 나타내서 정의해요:
JPA 기반 idempotent 리포지터리 사용하기
이 섹션에서는 기본으로 쓰이는 메모리 기반 리포지터리 대신 JPA 기반 idempotent 리포지터리를 써요.
먼저 META-INF/persistence.xml에 persistence-unit이 필요해요. 모델로는 org.apache.camel.processor.idempotent.jpa.MessageProcessed 클래스를 써야 해요.
<persistence-unit name="idempotentDb" transaction-type="RESOURCE_LOCAL">
<class>org.apache.camel.processor.idempotent.jpa.MessageProcessed</class>
<properties>
<property name="openjpa.ConnectionURL" value="jdbc:h2:./target/idempotentTest"/>
<property name="openjpa.ConnectionDriverName" value="org.h2.Driver"/>
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema"/>
<property name="openjpa.Log" value="DefaultLevel=WARN, Tool=INFO"/>
<property name="openjpa.Multithreaded" value="true"/>
</properties>
</persistence-unit>
그다음 Spring XML 파일에서 JPA idempotent 리포지터리도 만들 수 있어요:
XML 전용: JPA 기반 idempotent 리포지터리의 Spring bean 정의
<!-- we define our jpa based idempotent repository we want to use in the file consumer -->
<bean id="jpaStore" class="org.apache.camel.processor.idempotent.jpa.JpaMessageIdRepository">
<!-- Here we refer to the entityManagerFactory -->
<constructor-arg index="0" ref="entityManagerFactory"/>
<!-- This 2nd parameter is the name (= a category name).
You can have different repositories with different names -->
<constructor-arg index="1" value="FileConsumer"/>
</bean>
그리고 파일 consumer 엔드포인트에서 idempotentRepository 옵션에 # 문법으로 jpaStore bean을 참조하기만 하면 돼요:
from("file://inbox?idempotent=true&idempotentRepository=#jpaStore")
.to("bean:processInbox");
<route>
<from uri="file://inbox?idempotent=true&idempotentRepository=#jpaStore"/>
<to uri="bean:processInbox"/>
</route>
- route:
from:
uri: file://inbox
parameters:
idempotent: true
idempotentRepository: "#jpaStore"
steps:
- to:
uri: bean:processInbox
필터링 전략
Camel은 플러그 가능한 필터링 전략을 지원해요. 아래에서 설명해요.
GenericFilter로 필터링하기
filter 옵션으로 org.apache.camel.component.file.GenericFileFilter 인터페이스를 구현해서 Java 코드로 커스텀 필터를 만들 수 있어요.
GenericFilter 구현하기
이 인터페이스는 boolean을 반환하는 accept 메서드를 갖고 있어요. 반환 값의 의미는 이래요:
-
true면 파일을 포함해요 -
false면 파일을 건너뛰어요.
GenericFile에는 파일이 디렉터리인지 알려주는 isDirectory 메서드도 있어요. 이걸로 원치 않는 디렉터리를 걸러서, 원치 않는 디렉터리로 내려가는 탐색을 피할 수 있어요.
GenericFilter 사용하기
그다음 엔드포인트에 이런 필터를 설정해서 특정 파일이 처리되지 않게 할 수 있어요.
예시에서는 파일 이름이 skip으로 시작하면 건너뛰는 필터를 직접 만들었어요:
그리고 Spring XML 파일에서 정의한 필터를(# 표기법으로) filter 속성으로 참조해서 라우트를 설정할 수 있어요:
from("file://inbox?filter=#myFilter")
.to("bean:processInbox");
<!-- define our filter as a plain spring bean -->
<bean id="myFilter" class="com.mycompany.MyFileFilter"/>
<route>
<from uri="file://inbox?filter=#myFilter"/>
<to uri="bean:processInbox"/>
</route>
- from:
uri: file://inbox
parameters:
filter: "#myFilter"
steps:
- to:
uri: bean:processInbox
ANT path 매처로 필터링하기
ANT path 매처는 AntPathMatcher를 기반으로 해요.
파일 경로는 다음 규칙으로 매칭해요:
-
?는 한 문자와 매칭해요 -
*는 0개 이상의 문자와 매칭해요 -
**는 경로에서 0개 이상의 디렉터리와 매칭해요
antInclude와 antExclude 옵션을 쓰면 필터를 정의하지 않고도 ANT 스타일 include/exclude를 간편하게 지정할 수 있어요. 자세한 내용은 위의 URI 옵션을 보세요.
아래 예시에서 사용 방법을 보여줘요:
from("file://inbox?antInclude=**/*.txt").to("...");
<from uri="file://inbox?antInclude=**/*.txt"/>
- from:
uri: file://inbox
parameters:
antInclude: "**/*.txt"
steps:
- log:
message: "${header.CamelFileName}"
정렬 전략
Camel은 플러그 가능한 정렬 전략을 지원해요. 아래에서 설명해요.
Comparator로 정렬하기
이 전략은 Java의 내장 java.util.Comparator를 쓰는 거예요. 이런 컴퍼레이터로 엔드포인트를 설정하면 Camel이 파일을 처리 전에 정렬해요.
예시에서는 파일 이름으로 정렬만 하는 컴퍼레이터를 직접 만들었어요:
그리고 Spring XML 파일에서 정의한 정렬기(mySorter)를 sorter 옵션으로 참조해서 라우트를 설정할 수 있어요:
from("file://inbox?sorter=#mySorter")
.to("bean:processInbox");
<!-- define our sorter as a plain spring bean -->
<bean id="mySorter" class="com.mycompany.MyFileSorter"/>
<route>
<from uri="file://inbox?sorter=#mySorter"/>
<to uri="bean:processInbox"/>
</route>
- from:
uri: file://inbox
parameters:
sorter: "#mySorter"
steps:
- to:
uri: bean:processInbox
팁 URI 옵션은 # 문법으로 bean을 참조할 수 있어요
위 Spring DSL 라우트에서 id 앞에
#를 붙여 Registry의 bean을 참조할 수 있다는 점에 주목하세요.sorter=#mySorter라고 쓰면 Camel이 Registry에서mySorter라는 ID의 bean을 찾아요.
sortBy로 정렬하기
Camel은 플러그 가능한 정렬 전략을 지원해요. 이 전략은 File Language로 정렬을 설정해요. sortBy 옵션은 이렇게 설정해요:
sortBy=group 1;group 2;group 3;...
각 그룹은 세미콜론으로 구분해요. 간단한 상황에서는 그룹 하나만 쓰면 돼요. 간단한 예시는 이래요:
sortBy=file:name
파일 이름으로 정렬해요. 그룹 앞에 reverse:를 붙이면 순서를 뒤집을 수 있어서, 이제 Z에서 A로 정렬해요:
sortBy=reverse:file:name
File Language의 모든 기능을 쓸 수 있으니 다른 파라미터도 쓸 수 있어요. 예컨대 파일 크기로 정렬하려면 이렇게 해요:
sortBy=file:length
문자열 비교에서 ignoreCase:로 대소문자를 무시하도록 설정할 수 있어요. 파일 이름 정렬을 쓰되 대소문자를 무시하고 싶다면 이렇게 해요:
sortBy=ignoreCase:file:name
대소문자 무시와 뒤집기를 조합할 수도 있어요. 다만 reverse를 먼저 지정해야 해요:
sortBy=reverse:ignoreCase:file:name
아래 예시에서는 마지막으로 수정된 파일 기준으로 정렬하고 싶으니 이렇게 해요:
sortBy=file:modified
그다음 이름을 두 번째 옵션으로 그룹핑하고 싶으니, 같은 수정 시각의 파일들은 이름으로 정렬돼요:
sortBy=file:modified;file:name
여기에 문제가 하나 있어요. 눈치채셨나요? 그렇죠, 파일의 수정 타임스탬프는 밀리초 단위라 너무 촘촘해요. 그런데 날짜로만 정렬하고 이름으로 하위 그룹을 나누고 싶다면 어떻게 할까요? File Language의 진짜 힘을 쓸 수 있으니 패턴을 지원하는 date 명령을 쓰면 돼요. 이렇게 풀 수 있어요:
sortBy=date:file:yyyyMMdd;file:name
그렇죠, 꽤 강력하죠. 아 그러고 보니 그룹마다 reverse를 쓸 수도 있어서, 파일 이름을 뒤집을 수도 있어요:
sortBy=date:file:yyyyMMdd;reverse:file:name
GenericFileProcessStrategy 사용하기
processStrategy 옵션으로 커스텀 GenericFileProcessStrategy를 쓸 수 있어서, 자신만의 begin, commit, rollback 로직을 구현할 수 있어요. 예컨대 어떤 시스템이 여러분이 소비할 폴더에 파일을 쓴다고 해봐요. 하지만 다른 ready 파일도 쓰이기 전에는 파일을 소비하면 안 된다고 해보죠.
자체 GenericFileProcessStrategy를 구현하면 이렇게 할 수 있어요:
-
begin()메서드에서 특별한 ready 파일이 존재하는지 검사할 수 있어요.begin메서드는 파일을 소비할 수 있는지 boolean으로 알려줘요. -
abort()메서드에서는begin작업이false를 반환한 경우에 특별한 로직을 실행할 수 있어요. 예컨대 리소스 정리 같은 거죠. -
commit()메서드에서는 실제 파일을 옮기고 ready 파일을 삭제할 수 있어요.
bridgeErrorHandler 사용하기
파일 consumer에서 발생한 예외를 Camel Error Handler로 처리하고 싶다면 아래처럼 bridgeErrorHandler 옵션을 켜면 돼요:
Java 전용: onException 에러 핸들러 설정은 Java DSL 전용
// to handle any IOException being thrown
onException(IOException.class)
.handled(true)
.log("IOException occurred due: ${exception.message}")
.transform().simple("Error ${exception.message}")
.to("mock:error");
// this is the file route that pickup files. Notice how we bridge the consumer to use the Camel routing error handler
// the exclusiveReadLockStrategy is only configured because this is from a unit test, so we use that to simulate exceptions
from("file:target/nospace?bridgeErrorHandler=true")
.convertBodyTo(String.class)
.to("mock:result");
이 옵션만 켜면 되고, 그 뒤는 라우트의 에러 핸들러가 알아서 처리해요.
중요 bridgeErrorHandler를 쓰면
interceptors,OnCompletions는 적용되지 않아요. Exchange는 Camel Error Handler가 직접 처리하고, interceptor나 onCompletion 같은 선행 동작이 개입할 수 없어요.
디버그 로깅
이 컴포넌트에는 문제가 있을 때 도움이 되는 TRACE 로그 레벨이 있어요.
예시
디렉터리에서 읽고 기본 move 동작 쓰기
기본으로 Camel은 처리한 파일을 파일을 소비한 디렉터리 안의 .camel 하위 디렉터리로 옮겨요.
from("file://inputdir/?recursive=true&delete=true").to("file://outputdir");
<route>
<from uri="file://inputdir/?recursive=true&delete=true"/>
<to uri="file://outputdir"/>
</route>
- route:
from:
uri: file://inputdir/
parameters:
recursive: true
delete: true
steps:
- to:
uri: file://outputdir
레이아웃은 이렇게 바뀌어요:
before
inputdir/foo.txt inputdir/sub/bar.txt
after
inputdir/.camel/foo.txt inputdir/sub/.camel/bar.txt outputdir/foo.txt outputdir/sub/bar.txt
디렉터리에서 읽어 다른 디렉터리에 쓰기
from("file://inputdir/?delete=true").to("file://outputdir");
<route>
<from uri="file://inputdir/?delete=true"/>
<to uri="file://outputdir"/>
</route>
- route:
from:
uri: file://inputdir/
parameters:
delete: true
steps:
- to:
uri: file://outputdir
동적 이름으로 디렉터리에서 읽어 다른 디렉터리에 쓰기
from("file://inputdir/?delete=true").to("file://outputdir?fileName=copy-of-${file:name}");
<route>
<from uri="file://inputdir/?delete=true"/>
<to uri="file://outputdir?fileName=copy-of-${file:name}"/>
</route>
- route:
from:
uri: file://inputdir/
parameters:
delete: true
steps:
- to:
uri: file://outputdir
parameters:
fileName: "copy-of-${file:name}"
디렉터리를 지켜보다가 그곳에 놓인 파일마다 메시지를 만들어요. 내용은 outputdir로 복사하고 inputdir의 파일은 삭제해요.
디렉터리를 재귀적으로 읽어 다른 곳에 쓰기
from("file://inputdir/?recursive=true&delete=true").to("file://outputdir");
<route>
<from uri="file://inputdir/?recursive=true&delete=true"/>
<to uri="file://outputdir"/>
</route>
- route:
from:
uri: file://inputdir/
parameters:
recursive: true
delete: true
steps:
- to:
uri: file://outputdir
디렉터리를 지켜보다가 그곳에 놓인 파일마다 메시지를 만들어요. 내용은 outputdir로 복사하고 inputdir의 파일은 삭제해요. 하위 디렉터리까지 재귀적으로 스캔해요. inputdir의 디렉터리 구조(하위 디렉터리 포함)를 그대로 outputdir에 재현해요.
inputdir/foo.txt inputdir/sub/bar.txt
출력 구조는 이렇게 돼요:
outputdir/foo.txt outputdir/sub/bar.txt
디렉터리에서 읽어 Java에서 메시지 처리하기
Java 전용: 인라인 Processor 구현
from("file://inputdir/").process(exchange -> {
Object body = exchange.getIn().getBody();
// do some business logic with the input body
});
본문은 inputdir 디렉터리에 방금 놓인 파일을 가리키는 File 객체예요.
파일 이름에 표현식 사용하기
이 예시에서는 소비한 파일을 오늘 날짜를 하위 폴더 이름으로 삼아 백업 폴더로 옮기고 싶어요:
from("file://inbox?move=backup/${date:now:yyyyMMdd}/${file:name}").to("...");
<route>
<from uri="file://inbox?move=backup/${date:now:yyyyMMdd}/${file:name}"/>
<to uri="..."/>
</route>
- route:
from:
uri: file://inbox
parameters:
move: "backup/${date:now:yyyyMMdd}/${file:name}"
steps:
- to:
uri: "..."
더 많은 예시는 File Language를 보세요.
더 알아보기 (Learn more)
파일 이름 패턴과 정렬 표현식을 더 깊게 다루고 싶다면 File Language 문서를 참고하세요. 다른 애플리케이션과 같은 폴더를 두고 파일을 소비할 때는 본문의 readLock 옵션과 doneFileName 옵션 설명을 다시 확인하는 걸 권해요.