[원문출처] https://camel.apache.org/components/2.x/aws-s3-component.html

 

해당 문서의 경우 Apache 재단의 Camel Document 문서 일부를 번역 한 것입니다.  일부 내용의 경우 필요에 따라 삭제된 부분이 있슴
번역본인 해당 문서의 경우 역자에게 있음을 알리며 상업적 이용을 불허합니다. 

www.sogomsoft.co.kr (주) 소곰소프트  

 

(주) 소곰소프트

GS인증 획득 (주)소곰소프트의 Sogom ISB 1.2.0(ESB:Enterpries Service Bus)제품이 한국정보통신기술협회(TTA)의 소프트웨어 품질인증인 Good Software 1등급 인증을 획득하였습니다. GS 인증은 국가가 국산 소프

www.sogomsoft.co.kr

Amazon S3 스토리지 서비스 컴포넌트

Camel 2.8 이후 지원, Both producer 와 consumer 양쪽 모두 지원.

S3 컴포넌트는 Amazon’s S3 서비스에 또는 서비스로 부터 객체를 저장하거나 조회 하는 것을 지원한다.

전제 조건으로 유효한 Amazon Web Services 개발자 계정을 가지고 있어야 하고,  S3에 가입되어 있어야 한다.  Amazon S3에서 더 많은 정보를 볼 수 있다.

URI 형식

aws-s3://[bucketName|arn][?options]

이 버킷은 만약 존재 하지 않는다면 생성되게 될 것이다. ?options=value&option2=value&… 과 같은 형식으로 URI에 쿼리 옵션을 추가 할 수있다. 

예를 들면,  helloBucket 버킷으로 부터 hello.txt 파일을 읽기 위해, 다음처럼 사용한다. :

from("aws-s3://helloBucket?accessKey=yourAccessKey&secretKey=yourSecretKey&prefix=hello.txt")
  .to("file:/var/downloaded");

 

환경 구성 옵션

환경 구성 옵션은 다음과 같이 2레벨로 구분된 레벨로 환경 설정 될 수 있다. 

  • 컴포넌트 레벨
  • 엔드포인트 레벨

환경 구성 컴포넌트 옵션

컴포넌트 레벨은 엔드포인트에 의해 상속받게 되는 일반적이고 보통의 환경 구성을 설정하기 위한 가장 높은 레벨이다. 컴포넌트의 보안 설정, 인증을 위한 자격증명, 네트워크 연결을 위한 URL 등과 같은 것을 설정한다. 

환경 구성 엔드포인트 옵션

가장 많이 자체적으로 환경 구성하는 곳이 엔드포인트이고, 엔드포인트가 종종 많은 옵션을 가지고, 엔드포인트에 해야 할 필요가 있는 환경 설정하는 것을 허용한다.  이 옵션은 엔드포인트가 컨슈머로서, 프로듀서로서 또는 양쪽 모두에서 사용되는 지에 따라 분류 된다.

 

컴포넌트 옵션

AWS S3 스토리지 서비스 컴포는트는 아래 목록에 나열된 것처럼 39가지옵션을 제공한다. 

이름 상세설명 기본값 유형
configuration 
(common)
컴포넌트의 환경 구성을 한다.   S3Configuration
region 
(common)
S3 클라이언트가 작업할 필요가 있는 리즌. 이 파라미터를 사용할 때, 환경구성에서 리즌의 대문자 이름을 예상하게 될 것이다. (예를 들면, AP_EAST_1) Regions.EU_WEST_1.name() 메서드로 이름을 사용해야 할 것이다.    String
resolvePropertyPlaceholders
(advanced)
시작할 때, 자체적으로 프로퍼티 플레이스홀더를 해결해야 하는지 여부를 설정한다. 단지 문자열 유형으로 된 프로퍼티들만 프로퍼티 플레이스 홀더를 사용 할 수 있다. true boolean
accessKey 
(security)
아마존 AWS 엑세스 키   String
secretKey 
(security)
아마존 AWS 시크릿 키   String

 

엔드포인트 옵션 

 AWS S3 저장소 서비스 엔드포인트는 다음 URI 구문을 사용하여 환경 구성될 수 있다. :

aws-s3://bucketNameOrArn

다음 패스 파라미터와 와 쿼리 파라미터를 사용하여 :

패스 파라미터 (1개 파라미터):

이름 상세설명 기본값 타입
bucketNameOrArn 필수값 버킷명 또는ARN(Amazon Resource Number, 아마존 리소스 번호)   String

 

쿼리 파라미터 (58개 파라미터):

이름 상세설명 기본값 타입
amazonS3Client 
(common)
레지스터리에서 com.amazonaws.services.s3.AmazonS3 에 참조 된다..   AmazonS3
pathStyleAccess 
(common)
S3 클라이언트가 패스 스타일을 사용하는지 아닌지를 설정한다.  false boolean
policy 
(common)
com.amazonaws.services.s3.AmazonS3#setBucketPolicy() 메서드에서 설정할 큐를 위한 정책을 설정한다.   String
proxyHost 
(common)
S3 클라이언트를 인스턴스화 할때 프록시 호스트를 정의한다.   String
proxyPort 
(common)
S3 클라이언트 정의 내부에 사용될 프록시 포트를 지정한다.   Integer
region 
(common)
S3 클라이언트가 작업할 필요가 있는 리즌. 이 파라미터를 사용할 때, 환경구성에서 리즌의 대문자 이름을 예상하게 될 것이다. (예를 들면, AP_EAST_1) Regions.EU_WEST_1.name() 메서드로 이름을 사용해야 할 것이다.    String
useIAMCredentials 
(common)
S3 클라이언트가 EC2 인스턴스에 자격증명을 로드 하는 것을 기대하는지 또는 통과되기 위한 정적 자격 증명을 기대 해야 하는지를 설정한다. false boolean
encryptionMaterials 
(common)
클라이언트가 사용할 대칭/비대칭의 경우에 사용하게 될 암호화 자료   EncryptionMaterials
useEncryption 
(common)
암호화를 사용할 것인지 아닌지를 정의 한다. false boolean
bridgeErrorHandler 
(consumer)
Camel 라우팅 에러 핸들러에 컨슈머를 연결하는 것을 허용한다. 이는 어떤 예외가 발생했을 때, 컨슈머가 들어오는 메시지 또는 선호하는 것을 가져오는 것을 시도 하는것을 의미 한다. 라우팅 에러 핸들러에 의해 메시지가 처리 하고 관리 되게 될 것이다. 기본값은 컨슈머가 예외를 다루기 위해 org.apache.camel.spi.ExceptionHandler를 사용하게 될 것이고, WARN 또는 ERROR 레벨로 로깅 되거나 무시 될 것이다.  false boolean
deleteAfterRead 
(consumer)
이 옵션은 조회된 이후에 S3로 부터 객체를 지운다. 이 삭제는 단지 Exchange가 커밋되었을 때 수행되게 된다. 만약 롤백이 발생하면 이 객체는 지워지지 않는다. 이 옵션이 false이면, 메시지를 폴링시에 계속 계속 조회 되게 될 것이다. 그러므로 라우터에서 중복을 필터링하기 위해 멱등적 컨슈며 EIP를 사용할 필요가 있다. S3Constants#BUCKET_NAME 과 S3Constants#KEY 헤더 또는 S3Constants#KEY 헤더를 사용하여 필터링 할 수 있다 . true boolean
fileName 
(consumer)
주어진 파일 명으로 버킷에서 객체를 얻기위해 사용한다.   String
includeBody 
(consumer)
이 옵션이 true이면, exchange 바디에 파일의 내용을 스트림으로 설정되게 되고 false이면, 헤더에 S3 객체 메타 데이타로 설정되게 되고 바디 값은 null이 된다. 이 옵션은 autocloseBody 옵션과 밀접한 관련 되어 있다. includeBody가 true로 설정된 경우에, autocloseBody가 false이면, S3Object 스트림을 닫는 것은 호출자에 달려 있다. autocloseBody가 true 설정되어 있으면, S3Object 스트림이 자동으로 닫히게 될 것이다. true boolean
maxConnections 
(consumer)
S3 클라이언트 환경 구성에서 최대 커넥션 파라미터를 설정한다. 60 int
maxMessagesPerPoll
(consumer)
각각을 폴링 시 폴링하기 위한 제한으로 메시지의 최대 수를 얻는다. 기본 값은 10이고. 제한없도록 설정하기 위해서는 0 또는 음수를 설정한다. 10 int
prefix 
(consumer)
이 옵션은 관심 있는 객체를 소비 하기 위해 com.amazonaws.services.s3.model.ListObjectsRequest에서 사용될 접두사이다.   String
sendEmptyMessageWhenIdle 
(consumer)
만약 폴링 컨슈머가 어떤 파일을 폴링 하지 않는다면, 대신 비어있는 메시지(no body)를 보내기 위해서 이 옵션을 활성화 할 수 있다. false boolean
autocloseBody 
(consumer)
만약 이 옵션이 true 이고 includeBody가 true이면, S3Object.close() 메서드가 exchange가 완료시 호출 되게 될 것이다. 이 옵션은 includeBody 옵션에 밀접하게 관련되어 있다. includeBody가 true로 설정된 경우에, autocloseBody가 false이면, S3Object 스트림을 닫는 것은 호출자에 달려 있다. autocloseBody가 true 설정되어 있으면, S3Object 스트림이 자동으로 닫히게 될 것이다.
true boolean
exceptionHandler 
(consumer)
컨슈며가 사용자 정의 ExceptionHandler를 사용하게 한다.  만약 bridgeErrorHandler 옵션이 활성화 되어 있다면 이 옵션이 사용되지 않는다는 점에 유의하라. 기본적으로 컨슈머는 예외를 처리 하게 되고, WARN 나 ERROR 레벨로 로그릴 기록하게 되거나 무시되게 될 것이다.    ExceptionHandler
exchangePattern 
(consumer)
컨슘가 Exchange를 생성할 때, Exchange 패턴을 설정한다. 3개의 열거형으로 구성되어 있고 InOnly, InOut, InOptionalOut 중에 하나가 될 수 있다.   ExchangePattern
pollStrategy 
(consumer)
Camel에서 Exchange가 생성되고 라우팅 되기 전에 폴링 동작을 하는 동안 에러를 핸들링을 컨트롤 하기 위해 사용자 정의 구현체를 제공하기 위해 하용하고 있는 플러그인 가능한 org.apache.camel.PollingConsumerPollingStrategy 을 설정한다.    PollingConsumerPollStrategy
deleteAfterWrite 
(producer)
이 옵션은 S3 파일이 업로드 되었을 때 파일 객체를 삭제한다. false boolean
multiPartUpload 
(producer)
만약 true이면, multi part 형식으로 파일을 업로드 하게 될 것이다. 이 멀티파트의 사이즈는 partSize 옵션에 의해 결정되게 된다. false boolean
operation 
(producer)
단지 업로드만 원하지 않는 경우에 사용하기 위한 작업이다. copyObject, deleteBucket, listBuckets, downloadLink 의 4개의 열거형이 있고 그중에 하나의 값을 선택 할 수 있다.   S3Operations
partSize 
(producer)
multi part 업로드에서 사용할 partSize를 설정한다 기본값은 25M이다. 26214400 long
serverSideEncryption 
(producer)
AWS에서 관리되는 키로 객체를 암호화 하기 위해서 서버 측 암호화 알고리즘을 설정한다. 예를 들면 AES256.   String
storageClass 
(producer)
com.amazonaws.services.s3.model.PutObjectRequest 요청에서 설정할 저장소 클래스를 지정한다.   String
awsKMSKeyId 
(producer)
KMS가 활성화 된 경우에 사용할 KMS 키의 ID를 정의 한다.    String
useAwsKMS 
(producer)
KMS 사용해야 하는지 아닌지를 정의한다. false boolean
accelerateModeEnabled 
(advanced)
가속 모드(Accelerate Mode)를 true인지 false인지를 정의한다. false boolean
chunkedEncodingDisabled 
(advanced)
비활성화 된 청크 인코딩이 true인지 false인지를 정의한다. 
HTTP 1.1 version 에서 사용가능한 스트리밍 데이터 전송 방식, 
chunked 인코딩 방식에서 데이터는 각각의 덩어리들 나눠 독립적으로 송신 및 수신 ,
각 chunk의 앞에는 해당 chunk의 크기 설정 형식은 byte 형식으로 송수신
길이가 0인 chunk가 수신되면 전송이 종료
false boolean
dualstackEnabled 
( advanced)
듀얼스택이 활성화가 true인지 false인지를 정의한다. false boolean
forceGlobalBucketAccessEnabled 
( advanced)
강제 글로벌 버킷 엑세스(Force Global Bucket Access)이 활성화가 true인지 false인지를 정의한다. false boolean
payloadSigningEnabled 
( advanced)
페이로드 서명(Payload Signing)이 활성화가 true인지 false인지를 정의한다. false boolean
backoffErrorThreshold 
(scheduler)
backoffMultipler 시작 되기 전에 발생되어야 하는 (어떤 에러로 실패된) 에러 폴링의 하위순번의 숫자.   int
backoffIdleThreshold 
(scheduler)
backoffMultipler 시작 되기 전에 발생되어야 하는 유휴 홀링의 하위 순번의 숫자   int
backoffMultiplier 
(scheduler)
To let the scheduled polling consumer backoff if there has been a number of subsequent idles/errors in a row.
개별 행의 유휴/에러 하위순번의 숫자를 가지고 있다면 예약된 폴링 컨슈며가 물러나게 한다.
multiplier는 다음 실제 시도가 다시 발생하기 전에 건너뛰어야 하는 폴링의 수이다. 이 옵션을 사용할 때는 backoffIdleThreshold 나/또는 backoffErrorThreshold가 반드시 환경 구성 되어 있어야 한다.
  int
delay 
(scheduler)
다음 폴링 전에 지연 시간(밀리초). 500 long
greedy 
(scheduler)
greedy 옵션이 활성화 되어 있다면, 이전 실행이 하나 또는 그 이상의 메시지를 폴링한 경우, ScheduledPollConsumer가 즉시 다시 실행되게 된다.  false boolean
initialDelay 
(scheduler)
첫번째 폴링 시작 전에 지연시간(밀리초). 1000 long
runLoggingLevel 
(scheduler)
컨슈며가 폴링 될 때, 시작/완료 로그 라인을 로그 기록한다. 이 옵션은 로그를 위한 로그 레벨을 환경 설정하는 것을 허용하고 로그 레벨은 6개의 열거형으로 되어 있고 TRACE, DEBUG, INFO, WARN, ERROR, OFF 값중에 하나를 사용한다.  TRACE LoggingLevel
scheduledExecutorService 
(scheduler)
컨슈머를 위해 사용할 사용자정의 또는 공유된 쓰레드 풀을 환경 구성하는 것을 허용한다. 기본적으로 각각 컨슈며는 단일 쓰레드로된 자신의 쓰레드 풀을 가지고 있다.    ScheduledExecutorService
scheduler 
(scheduler)
camel-spring 또는 camel-quartz 컴포넌트로부터 크론 스케줄을 사용하기 위해서 설정. 스케줄러에서 내장된 스프링 또는 쿼츠의 값을 사용한다.  none Object
schedulerProperties 
(scheduler)
사용자 정의 스케줄러 또는 쿼츠나 스프링 기반 스케줄러 중에 하나를 사용할때 추가적인 프로퍼티를 환경 구성하기 위해 사용한다.    Map
startScheduler 
(scheduler)
스케줄러가 자동 시작 되어야 하는지를 설정한다. true boolean
timeUnit 
(scheduler)
initialDelay 과 delay 옵션읠 위한 타임 유닛 값. 7개의 열거형을 가지고 NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS 중에 하나의 값이 될 수 있다. MILLISECONDS TimeUnit
useFixedDelay 
(scheduler)
고정된 지연시간 또는 고정된 비율이 사용되었는지를 제어한다. 자세한 것은 JDK의 ScheduledExecutorService 를 보라 true boolean
accessKey (security) 아마존 AWS 엑세스 키   String
secretKey (security) 아마존 AWS 시크릿 키   String
필수 S3 컴포넌트 옵션
레지스터리에 Amazon’s S3에 엑세스 하기 위해  amazonS3Client 또는 액세스 키 와 시크릿 키를 제공 해야 한다. 

 

배치 컨슈머

이 컴포넌트는 배치 컨슈머를 구현한다.

인스턴스가 배치에 존재 하는 메시지가 얼마나 많은지 알 수 있고 Aggregator가 메시지의 수를 집계 하게 해주는 것을 허용한다. 

S3 컴포넌트 사용예

S3 프로듀서에 의해 사용될 메시지 헤더
 
헤더 유형 상세 설명
CamelAwsS3BucketName String 현재 작업에서 저장되거나 사용되게 될 객체의 버킷 이름.
CamelAwsS3BucketDestinationName String 현재 작업에서 사용되게 될 버킷 목적지 명.
CamelAwsS3ContentLength Long 이 객체의 컨텐츠 길이.
CamelAwsS3ContentType String 이 객체의 컨텐츠 유형.
CamelAwsS3ContentControl String 이 객체의 컨텐츠 컨트롤
CamelAwsS3ContentDisposition String 이 객체의 컨텐츠 처분
CamelAwsS3ContentEncoding String 이 객체의 컨텐츠 인코딩
CamelAwsS3ContentMD5 String 이 객체의 md5 체크섬
CamelAwsS3DestinationKey String 현재 작업에서 사용되게 될 목적지 키.
CamelAwsS3Key String 현재 작업에서 저장되거나 사용되게 될 아래 키.
CamelAwsS3LastModified java.util.Date 이 객체의 마지막 변경된 타임스탬프.
CamelAwsS3Operation String 수행할 작업. 허용되는 값은 copyObject, deleteObject, listBuckets, deleteBucket, downloadLink, listObjects 이다.
CamelAwsS3StorageClass String 객체의 저장 클래스
CamelAwsS3CannedAcl String 객체에 적용하게 될 취소된 acl. 허용되는 값은 com.amazonaws.services.s3.model.CannedAccessControlList를 보라.
CamelAwsS3Acl com.amazonaws.services.s3.model.AccessControlList 맞게 생성된 Amazon S3 Access Control List 객체. 더 자세한 것은 com.amazonaws.services.s3.model.AccessControlList를 보라.
CamelAwsS3Headers Map<String,String> 사용자 정의 objectMetadata 헤더들을 얻거나 설정하는 것을 지원한다..
CamelAwsS3ServerSideEncryption String AWS에 관리되는 키를 사용하여 객체를 암호화 할 때, 서버측 암호화 알고리즘을 설정한다. 예를 들면, AES256를 사용.
CamelAwsS3VersionId String 현재 작업으로 부터 저장되거나 반환될 객체의 버전 ID

S3 프로듀서에 의해 설정될 메시지 헤더

헤더 유형 상세 설명
CamelAwsS3ETag String 새로 업로드 될 객체를 위한 ETag 값.
CamelAwsS3VersionId String 새로 업로드 될 객체의 부가적인 버전 ID.
CamelAwsS3DownloadLinkExpiration String URL 다운로드 링크의 만료 시간(밀리초). 다운로드 링크는 CamelAwsS3DownloadLink 응답 헤더에 저장되게 될 것이다.

S3 컨슈머에 의해 설정될 메시지 헤더

헤더 유형 상세 설명
CamelAwsS3Key String 객체가 저장 될 때 키
CamelAwsS3BucketName String 객체가 포함된 버킷의 이름.
CamelAwsS3ETag String RFC 1864에 따라 객체와 관련된 16진수로 인코딩된 128-bit MD5 다이제스트. 이 데이타는 호출자가 수신 받은 데이타와 아마존 S3에 의해 보내 질 데이타와 같은지 검증하기 위해 무결성 체크로 사용된다. 
CamelAwsS3LastModified Date 객체와 관련해서 아마존 S3 마지막 기록된 변경 사항에 날짜와 시간을 나타내는 마지막 변경 시점 헤더의 값
CamelAwsS3VersionId String 만약 가능하다면 연관된 아마존 S3 객체 버전 ID. 버전 ID들은 객체 버전 관리가 활성화 된 Amazon S3 버킷에 객체가 업로드 될 때,  객체에만 할당 된다.
CamelAwsS3ContentType String 연관된 객체에 저장될 컨텐츠의 유형을 가리키는 Content-Type HTTP 헤더. 이 헤더의 값은 표준 MIME 유형이다.
CamelAwsS3ContentMD5 String RFC 1864에 따라연관된 객체의 base64 인코딩된 128-bit MD5 다이제스트. (헤더를 포함하지 않는 컨텐츠). 이 데이타는 Amazon S3에 의해 수신 된 데이타가 호출자에게 보내진 데이타와 같은지를 검증하기 위해 무결성 체크 메시지로 사용된다. 
CamelAwsS3ContentLength Long byte로 객체와 연관된 사이즈를 나타내는 Content-Length HTTP 헤더.
CamelAwsS3ContentEncoding String 객체에 적용된 컨텐츠 인코딩이 무엇인지, Content-Type 필드에 의해 참조될 미디어 타입을 포함하기 위해 적용되어야 하는 디코딩 매카니즘이 무엇인지를 지정하는 부가적인 Content-Encoding HTTP 헤더.
CamelAwsS3ContentDisposition String 저장될 객체를 위한 요청되어진 파일 명 같은 프리젠테이션 정보를 지정하는 부가적인 Content-Disposition HTTP 헤더.
일반적인 HTTP 응답에서 Content-Disposition 헤더는 컨텐츠가 브라우저에 inline 되어야 하는 웹페이지 자체이거나 웹페이지의 일부인지, 아니면 attachment로써 다운로드 되거나 로컬에 저장될 용도록 쓰이는 것인지를 알려주는 헤더입니다
CamelAwsS3ContentControl String 사용자가 HTTP 요청/응답 체인을 따라 캐싱 동작을 지정하는 것을 허용하는 부가적인 Cache-Control HTTP 헤더. 
CamelAwsS3ServerSideEncryption String AWS에 의해 관리되는 키를 사용하여 객체를 암호화 할때 서버측 암호화 알고리즘.

S3 프로듀셔 작업

Camel-AWS s3 컴포넌트는 프로듀서 측에서 다음 작업을 제공한다. :

  • copyObject
  • deleteObject
  • listBuckets
  • deleteBucket
  • downloadLink
  • listObjects
  • getObject (S3Object 인스턴스를 반환하게 될 것이다.)
  • getObjectRange ( S3Object 인스턴스를 반환하게 될 것이다.)

고급 아마존 S3 환경 구성

만약 Camel 어플리케이션이 방화벽 뒤에서 동작중이거나 또는 아마존 S3 인스턴스 환경 구성을 좀더 컨트롤 하는 것을 원하면 자신의 환경 구성 인스턴스를 생성 할 수 있다. :

AWSCredentials awsCredentials = new BasicAWSCredentials("myAccessKey", "mySecretKey");

ClientConfiguration clientConfiguration = new ClientConfiguration();
clientConfiguration.setProxyHost("http://myProxyHost");
clientConfiguration.setProxyPort(8080);

AmazonS3 client = new AmazonS3Client(awsCredentials, clientConfiguration);

registry.bind("client", client);

Camel aws-s3 컴포넌트 환경구성 설정을 참조한다.:

from("aws-s3://MyBucket?amazonS3Client=#client&delay=5000&maxMessagesPerPoll=5")
.to("mock:result");

S3 컴포넌트로 KMS 사용하기

To use AWS KMS to encrypt/decrypt data by using AWS infrastructure you can use the options introduced in 2.21.x like in the following example

from("file:tmp/test?fileName=test.txt")
     .setHeader(S3Constants.KEY, constant("testFile"))
     .to("aws-s3://mybucket?amazonS3Client=#client&useAwsKMS=true&awsKMSKeyId=3f0637ad-296a-3dfe-a796-e60654fb128c");

In this way you’ll ask to S3, to use the KMS key 3f0637ad-296a-3dfe-a796-e60654fb128c, to encrypt the file test.txt. When you’ll ask to download this file, the decryption will be done directly before the download.

S3 컴포넌트로 IAM자격증명사용 옵션 ("useIAMCredentials" ) 사용하기

아마존 IAM 자격증명을 사용하기 위해, 먼저 Camel 어플리케이션을 구동하고 있는 EC2가 효과적으로 동작하기 위해 첨부된 적절한 정책을 포함된 것과 관련된 IAM 롤을 가지는지를 검증 해야 한다.  단지 이 기능은 원격 인스턴스에 "true" 를 설정해야 한다는 것을 유의해라. 심지어 더 명확하기 위해, IAM 가  AWS 특정 컴포넌트이기 때문에 로컬환경에서는 정적 자격증명을 여전히 사용해야 하고, 그러나 AWS 환경은 더 휩게 관리 되어야 한다. 이 IAM 자격 증명이 구현되고 이해 되었을 때,  AWS 환경 설정을 위해, 쿼리 파리미터 "useIAMCredentials"에 "true"로 설정 할 수 있다.  로컬 환경과 원격 환경에 기반하여 on/off로 효과적으로 전환 하기 위해, 시스템 환경 변수로 쿼리 파라미터를 활성화 하는 것을 고려 할 수 있다. 예를 들면, "isRemote" 가 "true"로 시스템 환경 변수에서 호출 될 때, 당신의 코드에 쿼리 파라미터에 "true" 를 설정해야 한다. ( 간단한 예제처럼 동작해야 하고 그렇게 하기 위한 많은 다른 방법이 있다). 비록 정적 자격 증명이 완전하게 필요가 제거 되지 않는다 할지라도, 마마존 AWS 환경 설정에서 IAM credentials을 사용하는 것은 원격 환경에서 갱신될 필요를 없애고, 주요 보안이 향상을 추가 한다.  (IAM 자격증명은 매일 6시간 마다 자동적으로 갱신되고 그 보안 정책이 업데이트 될때 업데이트 된다). 이것은 가격 증명을 관리 하기 위해 AWS에서 요구된 방법이고 그러므로 가능한 자주 사용해야 한다. 

S3 PRODUCER OPERATION EXAMPLES

  • CopyObject: 이 동작은 하나의 버킷으로 부터 다른 하나의 버킷으로 객체를 복사 한다. 
  from("direct:start").process(new Processor() {

      @Override
      public void process(Exchange exchange) throws Exception {
          exchange.getIn().setHeader(S3Constants.BUCKET_DESTINATION_NAME, "camelDestinationBucket");
          exchange.getIn().setHeader(S3Constants.KEY, "camelKey");
          exchange.getIn().setHeader(S3Constants.DESTINATION_KEY, "camelDestinationKey");
      }
  })
  .to("aws-s3://mycamelbucket?amazonS3Client=#amazonS3Client&operation=copyObject")
  .to("mock:result");

이 옵션은 객체를 camelDestinationKey 헤더에서 표현된 이름으로 mycamelbucket 버킷으로 부터  to the camelDestinationBucket 버킷로 복사한다.

  • DeleteObject: 이 작업은 버킷으로 부터 객체를 삭제 한다. 
  from("direct:start").process(new Processor() {

      @Override
      public void process(Exchange exchange) throws Exception {
          exchange.getIn().setHeader(S3Constants.KEY, "camelKey");
      }
  })
  .to("aws-s3://mycamelbucket?amazonS3Client=#amazonS3Client&operation=deleteObject")
  .to("mock:result");

이 작업은 mycamelbucket 버킷으로 부터 camelKey 객체를 삭제 하게 된다.

  • ListBuckets: 이 작업은 이 리즌 내의 계정에 버킷의 목록을 나열한다.
  from("direct:start")
  .to("aws-s3://mycamelbucket?amazonS3Client=#amazonS3Client&operation=listBuckets")
  .to("mock:result");

이 작업은 이 계정의 버킷들의 목록을 나열하게 될 것이다. 

  • DeleteBucket: 이 작업은 URI 파라미터와 헤더로 지정된 버킷을 삭제 한다. 
  from("direct:start")
  .to("aws-s3://mycamelbucket?amazonS3Client=#amazonS3Client&operation=deleteBucket")
  .to("mock:result");

이 작업은 mycamelbucket 버킷을 삭제 한다.

  • DownloadLink: 이 작업은 키 헤더에 지정된 파일을 위한 다운로드 링크를 생성한다. 
  from("direct:start").process(new Processor() {

      @Override
      public void process(Exchange exchange) throws Exception {
          exchange.getIn().setHeader(S3Constants.KEY, "camelKey");
      }
  })
  .to("aws-s3://mycamelbucket?amazonS3Client=#amazonS3Client&operation=downloadLink")
  .to("mock:result");

이 작업은 mycamelbucket 버킷에 camelKey의 파일을 위한 다운로드 링크를 생성한다.

  • ListObjects: 이 작업은 지정된 버킷에 객체를 목록으로 나열한다. 
  from("direct:start")
  .to("aws-s3://mycamelbucket?amazonS3Client=#amazonS3Client&operation=listObjects")
  .to("mock:result");

이 작업은 mycamelbucket 버킷에 객체 목록을 나열하게 될 것다.

  • GetObject: 이 작업은 지정된 버킷에 단일 객체를 얻게 된다. 
  from("direct:start").process(new Processor() {

      @Override
      public void process(Exchange exchange) throws Exception {
          exchange.getIn().setHeader(S3Constants.KEY, "camelKey");
      }
  })
  .to("aws-s3://mycamelbucket?amazonS3Client=#amazonS3Client&operation=getObject")
  .to("mock:result");

이 작업은 mycamelbucket 버킷에서 camelKey 객체에 관련된 S3Object를 반환하게 될 것이다..

  • GetObjectRange: 이 작업은 지정된 버킷에서 단일 범위 객체를 얻는다. 
  from("direct:start").process(new Processor() {

      @Override
      public void process(Exchange exchange) throws Exception {
          exchange.getIn().setHeader(S3Constants.KEY, "camelKey");
          exchange.getIn().setHeader(S3Constants.RANGE_START, "0");
          exchange.getIn().setHeader(S3Constants.RANGE_END, "9");
      }
  })
  .to("aws-s3://mycamelbucket?amazonS3Client=#amazonS3Client&operation=getObjectRange")
  .to("mock:result");

이 작업은 0에서 9까지 바이트들을 포함하는 mycamelbucket 버킷에 camelKey 객체에 관련된 S3Object 인스턴스를 반환한다.

버킷 자동 생성

autoCreateBucket 옵션으로, 사용자는S3 버킷이 존재 하지 않는 경우에 자동 생성을 피할 수 있다. 이 옵션의 기본값은 true 이다. 만약 false로 설정되면, AWS 내에 존재 하지 않은 모든 버킷에 대한 어떤 동작도 성공할 수 없고 에러를 반환하게 될 것이다. 

레지스터리 에서 Amazon S3 클라이언트 자동 탐색 

이 컴포넌트는 레지스터리에서 Amazon S3 빈의 존재를 탐색하는 것이 가능하다.  만약 Amazon S3 클라이언트 유형의 유일한 인스턴스라면 그 인스턴스를 클라이언트로 사용하게 될 것이고, 예제에서 처럼 uri 파라미터로 S3 클라이언트를 정의 할 필요는 없다. 이는 엔드포인트의 스마트하게 환경구성하기 위해 더 유용할 수 있다. 

하나의 버킷에서 다른 버킷으로 객체 이동

몇몇 사용자는 이 컴포넌트의  copyObject 기능을 사용하지 않고 어떤 버킷으로 부터 객체를 소비하고 다른 버킷에 컨텐츠를 옮기는 것을 좋아한다. 만약 당신이 이 경우라면, 컨슈머의 들어오는 Exchange로 부터  bucketName 헤더를 제거 하는 것을 잊지 말아라. 그렇지 않으면 그 파일이 같은 원본 버킷을 항상 재 작성 하게 될 것이다. 

디펜던시

메이븐 사용자는 pom.xml 파일에 다음 디펜던시를 추가 해야 할 것이다.

pom.xml

<dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-aws-s3</artifactId>
    <version>${camel-version}</version>
</dependency>

where ${camel-version} must be replaced by the actual version of Camel.

스프링 부트 자동 환경 구성 

스프링 부트로 aws-s3를 사용할 때 자동 환경 구성을 가지기 위해 메이븐 디펜던시를 다음과 같이 설정 해야 한다.:

<dependency>
  <groupId>org.apache.camel.springboot</groupId>
  <artifactId>camel-aws-s3-starter</artifactId>
  <version>x.x.x</version>
  <!-- use the same version as your Camel core version -->
</dependency>

이 컴포넌트는 아래 나열된 40개 옵션을 지원한다. 

이름 상서 설명 기본값 유형
camel.component.aws-s3.accelerate-mode-enabled Define if Accelerate Mode enabled is true or false. false Boolean
camel.component.aws-s3.access-key Amazon AWS Access Key.   String
camel.component.aws-s3.amazon-s3-client Reference to a com.amazonaws.services.s3.AmazonS3 in the registry. The option is a com.amazonaws.services.s3.AmazonS3 type.   AmazonS3
camel.component.aws-s3.auto-create-bucket Setting the autocreation of the bucket. true Boolean
camel.component.aws-s3.auto-discover-client Setting the autoDiscoverClient mechanism, if true, the component will look for a client instance in the registry automatically otherwise it will skip that checking. true Boolean
camel.component.aws-s3.autoclose-body If this option is true and includeBody is true, then the S3Object.close() method will be called on exchange completion. This option is strongly related to includeBody option. In case of setting includeBody to true and autocloseBody to false, it will be up to the caller to close the S3Object stream. Setting autocloseBody to true, will close the S3Object stream automatically. true Boolean
camel.component.aws-s3.autowired-enabled Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc. true Boolean
camel.component.aws-s3.aws-k-m-s-key-id Define the id of KMS key to use in case KMS is enabled.   String
camel.component.aws-s3.bridge-error-handler Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions occurred while the consumer is trying to pickup incoming messages, or the likes, will now be processed as a message and handled by the routing Error Handler. By default the consumer will use the org.apache.camel.spi.ExceptionHandler to deal with exceptions, that will be logged at WARN or ERROR level and ignored. false Boolean
camel.component.aws-s3.chunked-encoding-disabled Define if disabled Chunked Encoding is true or false. false Boolean
camel.component.aws-s3.configuration The component configuration. The option is a org.apache.camel.component.aws.s3.S3Configuration type.   S3Configuration
camel.component.aws-s3.delete-after-read Delete objects from S3 after they have been retrieved. The delete is only performed if the Exchange is committed. If a rollback occurs, the object is not deleted. If this option is false, then the same objects will be retrieve over and over again on the polls. Therefore you need to use the Idempotent Consumer EIP in the route to filter out duplicates. You can filter using the S3Constants#BUCKET_NAME and S3Constants#KEY headers, or only the S3Constants#KEY header. true Boolean
camel.component.aws-s3.delete-after-write Delete file object after the S3 file has been uploaded. false Boolean
camel.component.aws-s3.delimiter The delimiter which is used in the com.amazonaws.services.s3.model.ListObjectsRequest to only consume objects we are interested in.   String
camel.component.aws-s3.dualstack-enabled Define if Dualstack enabled is true or false. false Boolean
camel.component.aws-s3.enabled Whether to enable auto configuration of the aws-s3 component. This is enabled by default.   Boolean
camel.component.aws-s3.encryption-materials The encryption materials to use in case of Symmetric/Asymmetric client usage. The option is a com.amazonaws.services.s3.model.EncryptionMaterials type.   EncryptionMaterials
camel.component.aws-s3.endpoint-configuration Amazon AWS Endpoint Configuration. The option is a com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration type.   AwsClientBuilder$EndpointConfiguration
camel.component.aws-s3.file-name To get the object from the bucket with the given file name.   String
camel.component.aws-s3.force-global-bucket-access-enabled Define if Force Global Bucket Access enabled is true or false. false Boolean
camel.component.aws-s3.include-body If it is true, the exchange body will be set to a stream to the contents of the file. If false, the headers will be set with the S3 object metadata, but the body will be null. This option is strongly related to autocloseBody option. In case of setting includeBody to true and autocloseBody to false, it will be up to the caller to close the S3Object stream. Setting autocloseBody to true, will close the S3Object stream automatically. true Boolean
camel.component.aws-s3.key-name Setting the key name for an element in the bucket through endpoint parameter.   String
camel.component.aws-s3.lazy-start-producer Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel’s routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing. false Boolean
camel.component.aws-s3.multi-part-upload If it is true, camel will upload the file with multi part format, the part size is decided by the option of partSize. false Boolean
camel.component.aws-s3.operation The operation to do in case the user don’t want to do only an upload.   S3Operations
camel.component.aws-s3.part-size Setup the partSize which is used in multi part upload, the default size is 25M. 26214400 Long
camel.component.aws-s3.path-style-access Whether or not the S3 client should use path style access. false Boolean
camel.component.aws-s3.payload-signing-enabled Define if Payload Signing enabled is true or false. false Boolean
camel.component.aws-s3.policy The policy for this queue to set in the com.amazonaws.services.s3.AmazonS3#setBucketPolicy() method.   String
camel.component.aws-s3.prefix The prefix which is used in the com.amazonaws.services.s3.model.ListObjectsRequest to only consume objects we are interested in.   String
camel.component.aws-s3.proxy-host To define a proxy host when instantiating the S3 client.   String
camel.component.aws-s3.proxy-port Specify a proxy port to be used inside the client definition.   Integer
camel.component.aws-s3.proxy-protocol To define a proxy protocol when instantiating the S3 client.   Protocol
camel.component.aws-s3.region The region in which S3 client needs to work. When using this parameter, the configuration will expect the capitalized name of the region (for example AP_EAST_1) You’ll need to use the name Regions.EU_WEST_1.name().   String
camel.component.aws-s3.secret-key Amazon AWS Secret Key.   String
camel.component.aws-s3.server-side-encryption Sets the server-side encryption algorithm when encrypting the object using AWS-managed keys. For example use AES256.   String
camel.component.aws-s3.storage-class The storage class to set in the com.amazonaws.services.s3.model.PutObjectRequest request.   String
camel.component.aws-s3.use-aws-k-m-s Define if KMS must be used or not. false Boolean
camel.component.aws-s3.use-encryption Define if encryption must be used or not. false Boolean
camel.component.aws-s3.use-i-a-m-credentials Set whether the S3 client should expect to load credentials on an EC2 instance or to expect static credentials to be passed in. false Boolean
번역 문서 저작권 표시 :
저작자표시-비영리-동일조건 변경 허락 (CC BY-NC-SA)
원문 저작권 표시 :
(Developing a customer text editor for the Eclipse IDE, Lars Vogel (c) 2009 - 2020 vogella GmbHVersion 1.1,09.09.2020
, CC BY-NC-SA )
소스 코드의 경우 Eclipse Public License 2.0 를 따른다.

[원문 출처] www.vogella.com/tutorials/EclipseEditors/article.html


해당 문서의 경우 vogella사의 Eclipse IDE Extensions 
Document 문서 일부를 번역 한 것입니다. 
번역본인 해당 문서의 경우 역자에게 있음을 알리며 상업적 이용을 불허합니다. 
번역 작업 시 이클립스 내 고유이름을 가진 값의 경우, 가령 Tab의 명칭 등의 경우 화면에서 표시되는 이름이므로 별도로 번역없이 제공하거나 괄호를 사용하여 동시에 명기한다.
저작자표시-비영리-동일조건 변경 허락 (BY-NC-SA)

www.sogomsoft.co.kr (주) 소곰소프트 

1. 이클립스 에디터

텍스트 에디터는 일반적으로 텍스트 데이타를 변경하는 것을 허용하고, 저장 액션이 발생했을때는 언제나 기본 모델의 변경을 적용한다. 

어떤 파일 확장자 또는 컨텍스트 유형을 위한 기능성 에디터를 제공하기 위해 , 다음과 같이 할 수 있다:

  • 일반적인 에디터를 확장한다. 

  • 자체 에디터를 구현한다. 

일반적인 에디터를 확장하는 것은 구현을 가속화시키고 단순화 할수 있기 때문에 새 파일을 위한 선호하는 선택이다.

일반적인 에디터에서 새 컨텍스트 유형을 지원을 추가하기 위해 다음을 할 필요가 있다. 

  • 컨텍스트 유형을 추가한다. 

  • "org.eclipse.ui.genericeditor.presentationReconcilers" 확장점을 통해 에디터에 프리젠테이션조정자(PresentationReconsiler)를 등록한다.

1.1. JFace 텍스트 프레임워크

JFace 텍스트에서, 텍스트 문서는 IDocument로 모델링되어 있다.  컨트롤러로 ITextViewer를 사용하는 IDocument 문서를 보거나 편집하기 위해, 문서를 프리젠테이션 하기 위한 StyledText 위젯을 사용한다.

IDocument 인터페이스는 텍스트를 저장하고 다음을 위한 지원을 제공한다.:

  • 라인 정보

  • 텍스트 조작

  • 문서 변경 리스너

  • 맞춤형 위치 관리자

  • 검색

  • 맞춤형 파티션 관리

  • 문서 파티션 변경 리스너

문서들은 문서 파티셔너를 통해 다른 파티션으로 분해 될 수 있다. 즉 그 파티션들은 그 파티션들의 유형에 따라 조작할 수 있고 다른 포그라운트(전경) 색상을 가질 수 있다.

1.2. 프리젠테이션 조정자 소개

사용자가 문서를 변경 할 때마다,  파티션 조정자는 시각적 프리젠테이션이 유효하지 않는 영역과 그 영역을 고칠수 있는 방법을 결정한다.

소스코드의 하이라이팅(강조)은 프리젠테이션 조정자를 사용함으로써 보관될 수 있다.  그런 프리젠테이션 조정자는 "org.eclipse.ui.genericeditor.presentationReconcilers" 확장을 통해서 정의 될 수 있다. 프리젠테이션 조정자는 IPresentationReconciler 인터페이스를 구현할 컨텍스트유형과 클래스의 명세서를 필요로 한다. IPresentationReconciler를 사용할때, 어떤 IRules는 지정된 컨텍스트 유형을 지원할 수 있다. IRule은 문서의 파티셔닝 또는 텍스트의 스타일링의 목적을 위해 텍스트를 스캔하는 것을 사용하여 룰을 정의한다. 

파티션은 문서상에 의미론적인 뷰이다.( 역자주, 파티션은 개념상으로는 뷰이다):

  • 각각의 파티션은 컨텍스트 유형을 가진다. 

  • 문서의 각각의 문자는 파티션에 속해 있다. 

  • 문서는 멀티 파티셔닝을 지원한다. 

  • 파티셔닝은 항상 최신 정보이다.

1.3. 에티터로 동작하기 위한 API

현재 활성화 페이지를 통해서 에디터를 열 수 있다. 그렇게 하기 위해 "org.eclipse.ui.editors" 확장점에서 정의된 에디터를 위한 EditorInput 객체와 ID를 필요하다. 

page.openEditor(new YourEditorInput(), ID_OF_THE_EDITOR);

그외 페이지에서 얻기 위해 다음처럼 사용 할 수  있다.:

// 에디터가 뷰 안에 있다면 
getViewSite().getPage(); 

// 에디터가 커멘드 내에 있다면
HandlerUtil.getActiveWorkbenchWindow(event).getActivePage(); 

// 에디터가 다른 어딘가에 있다면 
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();

Java 에디터에서 엘리먼트에 컨트롤 키를 누르고 클릭하면 그곳을 탐색 할 수 있다.

이 기능은 "org.eclipse.ui.workbench.texteditor.hyperlinkDetectors" 확장점(extension point)을 위한 확장을 통해서 제공된다. 지정된 이름은 General > Editors > Text Editors > Hyperlinkingpreferences에서 보인다.  만약 "org.eclipse.ui.genericeditor.GenericEditor" 대상 id를 사용하는 일반적인 에디터를 사용하기 위해 "org.eclipse.ui.DefaultTextEditor"를 사용하는 모든 텍스트 에디터에서 사용하기를 원하면 대상ID(targetId)가 지원하기를 원하는 에디터 유형에 대상 아이디를 지정한다.

IHyperlinkDetector는 IHyperlink 객체의 배열을 반환하는 것을 하기로 되어 있다. IHyperlink 구현체는 하이퍼링크 액션을 수행한다.

2.2. 색상과 폰트 preferences 추가 하기 

이클립스는 General > Appearance > Colors and Fonts에서 사용자에 의해서 색상과 폰트를 사용자 정의 하기 위한 페이지를 제공한다. 

이 페이지를 위한 엔트리를 정의하기 위해, "org.eclipse.ui.themes" 확장점(extension point)을 위한 확장점을 정의할 필요가 있다.

예를 들면, plugin.xml 파일 또는 플러그인에서 다음 엔트리로 카테고리, 폰트와 컬러를 제공할 수 있다.

<extension point="org.eclipse.ui.themes"> 
	
    <themeElementCategory 
    	id="com.vogella.eclipse.preferences.mythemeElementCategory" 
        label="vogella category"> 
    	<description> An example theme category </description> 
    </themeElementCategory> 
    
    <colorDefinition 
    	categoryId="com.vogella.eclipse.preferences.mythemeElementCategory" 
        id="com.vogella.eclipse.preferences.myFirstColorDefinition" 
        label="vogella color" 
        value="COLOR_DARK_BLUE"> 
    	<description> Your description for the color </description> 
    </colorDefinition> 
    
    <fontDefinition 
    	categoryId="com.vogella.eclipse.preferences.mythemeElementCategory" 
        id="com.vogella.eclipse.preferences.myFirstFontDefinition" 
        label="vogella Font" 
        value="Lucida Sans-italic-18"> 
        <description> Your description for the font </description> 
    </fontDefinition> 
    
</extension>

색상을 위한 값은 SWT 클래스에 정의된 COLOR_* 상수가 될수 있다. 또한 255,0,0같은 RGB 값으로 지정할 수 있다. 폰트를 위해 값이 다음 패턴 `fontname-style-height` 값으로 정의되어 있다.

이제 preferences은 사용자나 CSS 엔진을 통해 변경 될 수있다. 현재 값을 얻기 위해, IThemeManager를 사용할 수 있다.

// Eclipse 4 API 
@Inject 
IThemeManager themeManager; 

// Eclipse 3 API
IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager(); 
ITheme currentTheme = themeManager.getCurrentTheme(); 

ColorRegistry colorRegistry = currentTheme.getColorRegistry(); 
Color color = colorRegistry.get("com.vogella.eclipse.preferences.myFirstColorDefinition"); 

FontRegistry fontRegistry = currentTheme.getFontRegistry(); 
Font font = fontRegistry.get("com.vogella.eclipse.preferences.myFirstFontDefinition");

2.3. 사용자 정의 스펠링 엔진

"org.eclipse.ui.workbench.texteditor" 플러그인은 "org.eclipse.ui.workbench.texteditor.spellingEngine" 확장점을 통해 사용자 정의 스펠링 엔진을 등록하기 위한 옵션을 제공한다. 

3. 연습 : 사용자 정의 파일 유형을 위한 일반적인 에디터 사용하기

이 연습에서, 일반적인 텍스트 에디터와 작업확장자로 파일을 연결한다. 파일 내에서 예제처럼 속송 파일을 편집하는 것을 지원하기를 원한다. 

test:Hello 
Helper:stuff

3.1. 새 플러그인 생성

"com.vogella.ide.editor.tasks"로 불리는 간단한 새 플러그인 프로젝트를 생성한다.

3.2. Manifest 위존관계 추가하기

MANIFEST.MF 파일을 위한 에디터를 연다. Add 버튼을 사용하여 의존관계(Dependencies) 탭을 통해서 다음 의존관례를 추가한다.

  • org.eclipse.text

  • org.eclipse.ui

  • org.eclipse.ui.editors

  • org.eclipse.ui.genericeditor

  • org.eclipse.ui.workbench.texteditor

  • org.eclipse.jface.text

  • org.eclipse.core.runtime

  • org.eclipse.core.resources

3.3. 텍스티로 manifest 파일 리뷰하기

MANIFEST.MF 탭을 선택하면, 일반적인 텍스트(plain text)로 이 파일을 볼 수 있다. 다음 해결방법과 같이야 힌다. (매번 릴리즈로 변경되기 때문에 버전 번호는 제거 되었다.).

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Tasks
Bundle-SymbolicName: com.vogella.ide.editor.tasks
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: VOGELLA
Automatic-Module-Name: com.vogella.ide.editor.tasks
Bundle-RequiredExecutionEnvironment: JavaSE-11
Require-Bundle: org.eclipse.text,
 org.eclipse.ui,
 org.eclipse.ui.editors,
 org.eclipse.ui.genericeditor,
 org.eclipse.ui.workbench.texteditor,
 org.eclipse.jface.text,
 org.eclipse.core.runtime,
 org.eclipse.core.resources

3.4. 컨텍스트 유형 정의하기

MANIFEST.MF 에디터를 사용하여, 확장(Extensions) 탭을 열고 Add…​ 버튼을 누른다..

"org.eclipse.core.contenttype.contentTypes" 확장점(extension point)을 선택한다.

다이얼로그를 닫고 확장을 추가 하기 위해 Finish을 누른다.

새 엔트리에 오른쪽 마우스 클릭하고 New > content-type을 선택한다.

다음 스크린샷과 유사하게 .tasks 확장자를 사용하는 파일을 위한 컨텍스트 유형을 지정한다. 

plugin.xml 파일에 엔트리를 생성한다..

plugin.xml 파일은 다음 목록과 유사하게 보여야 한다. manifest 편집기의 plugin.xml 탭을 클릭함으로써 그 내용을 볼 수 있다.

<?xml version="1.0" encoding="UTF-8"?> 
<?eclipse version="3.4"?> 
<plugin> 
	<extension point="org.eclipse.core.contenttype.contentTypes"> 
    	<content-type file-extensions="tasks" id="com.vogella.ide.contenttype.tasks" name="Tasks" priority="high"> 
        </content-type> 
    </extension> 
</plugin>

3.5. 에디터로 컨텐츠 유형 연결하기

컨텐츠 유형은 특정 에디터로 연결 될 수 있다. 이를 위해 "org.eclipse.ui.editors" 확장점이 사용 될 수 있다. 

확장(Extensions) 탭에 Add 버튼을 통해서 "org.eclipse.ui.editors extension"을 추가한다.

컨텍스트 유형을 정의하기 위해 "org.eclipse.ui.editors"애 오른쪽 마우스 클릭하고, New > editorContentTypeBinding를 선택한다.

그 결과 plugin.xml은 이제 다음과 같이 보여야 한다. 

<?xml version="1.0" encoding="UTF-8"?> 
<?eclipse version="3.4"?> 
<plugin> 

	<extension point="org.eclipse.core.contenttype.contentTypes"> 
		<content-type file-extensions="tasks" id="com.vogella.ide.contenttype.tasks" name="Tasks" priority="high"> 
		</content-type> 
	</extension> 

	<extension point="org.eclipse.ui.editors"> 
		<editorContentTypeBinding contentTypeId="com.vogella.ide.contenttype.tasks" editorId="org.eclipse.ui.genericeditor.GenericEditor"> 
		</editorContentTypeBinding> 
	</extension> 
    
</plugin>

3.6. feature를 통해서 product에 플러그인 추가 하기

IDE feature에 새 플러그인을 추가하라. features를 사용하지 않는 경우, 이 단계를 넘어가라

3.7. 개발 테스트 하기

 features 와 product를 사용한다면 product를 통해 새 이클립스 런타임을 시작하라.

product를 통한 시작은 런처 환경 구성을 업데이트 하게 될 것이다. 변경되지 않은 런터 환경 구성을 통해 직접 런타임 이클립스를 시작한다면, 새 플러그인은 포함되지 않을 것이다. 

features 와 product를 사용하지 않는다면, 새 플러그인이 시작시에 포함 될 수 있도록 직접 런터 환경 구성을 업데이트하라. 

이클립스 런타임에서 Window > Preferences > General > Content Types에서 컨텐츠 유형이 보이는지 확인하라

새 프로젝트를 생성하라 (General 또는 Java 프로젝트).

새 프로젝트에서 .tasks 확장자로 새 파일을 생성하라. 만약 파일을 연다면, 일반적인 텍스트 데이터에서 열려야 한다.

아이콘은 일번적인 에디터의 아이콘이 되여야 한다. 

4. 연습: 구문 하이라이팅 구현하기

이 연습에서 작업 파일 에디터를 위한 구문 하이라이팅을 구현한다. com.vogella.ide.editor.tasks플러그인 상에서 작업을 계속한다.

4.1. 구문 하이라이팅 구현하기 

IRule을 정의 하기 위한 다음 클래스를 구현한다.

package com.vogella.ide.editor.tasks; 

import org.eclipse.jface.text.rules.ICharacterScanner; 
import org.eclipse.jface.text.rules.IRule; 
import org.eclipse.jface.text.rules.IToken; 
import org.eclipse.jface.text.rules.Token; 

public class PropertyNameRule implements IRule { 

	private final Token token; 
    
    public PropertyNameRule(Token token) { 
    	this.token = token; 
    } 
    
    @Override 
    public IToken evaluate(ICharacterScanner scanner) { 
   		int c = scanner.read(); 
    	int count = 1; 
    	while (c != ICharacterScanner.EOF) { 
    		if (c == ':') { 
    		return token; 
    		} 
    		if ('\n' == c || '\r' == c) { 
    			break; 
    		} 
    		count++; 
    		c = scanner.read(); 
   		} 
    
    	// 일치 하지 않으면 원본 포지선에 스캐너 다시 넣는다.  
    	for (int i = 0; i < count; i++) { 
    		scanner.unread(); 
    	} 
    
    	return Token.UNDEFINED; 
    } 
    
}

에디터를 위한 조정자로 사용되게 될 다음 클래스를 구현한다. 

package com.vogella.ide.editor.tasks; 

import org.eclipse.jface.text.IDocument; 
import org.eclipse.jface.text.TextAttribute; 
import org.eclipse.jface.text.presentation.PresentationReconciler; 
import org.eclipse.jface.text.rules.DefaultDamagerRepairer; 
import org.eclipse.jface.text.rules.IRule; 
import org.eclipse.jface.text.rules.RuleBasedScanner; 
import org.eclipse.jface.text.rules.Token; 
import org.eclipse.swt.SWT; 
import org.eclipse.swt.widgets.Display; 

public class PropertiesReconciler extends PresentationReconciler { 

	private final TextAttribute tagAttribute = new TextAttribute( Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GREEN)); 
	
    public PropertiesReconciler() { 
		RuleBasedScanner scanner = new RuleBasedScanner(); 
		IRule rule = new PropertyNameRule(new Token(tagAttribute)); 
		scanner.setRules(new IRule[] { rule }); 
		
        DefaultDamagerRepairer dr = new DefaultDamagerRepairer(scanner); 
		this.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE); 
		this.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE); 
	} 
    
}

"com.vogella.ide.editor.tasks" 플러그인의 plugin.xml 파일에 "org.eclipse.ui.genericeditor.presentationReconcilers" 확장점에 확장을 추가한다..

<extension point="org.eclipse.ui.genericeditor.presentationReconcilers"> 
	<presentationReconciler class="com.vogella.ide.editor.tasks.PropertiesReconciler" contentType="com.vogella.ide.contenttype.tasks"> 
    </presentationReconciler> 
</extension>

4.2. 구현체 테스트하기

런타임 이클립스를 재시작한다. 

.tasks 파일을 연다. 다음 예처럼 파일에 몇몇 프로퍼티 값을 넣는다. :

ID: 1 
Summary: Eclipse IDE Training 
Description: 
Done: 
Duedate: 
Dependent:

결과는 다음 처럼 보여야 한다.:

5. 연습: 색상을 사용자정의하기 위한 사용자를 허용한다. 

이전 연습에서, 하드코드 컬러를 사용한 .task. 파일 에디터를 위한 구문 하이라이팅을 구현했다. 사용자가 생상을 커스터마이징하는 것을 방지할 수 있기 때무네 이것은 최적은 아니다.

이 연습에서 이 색상을 사용자정의 하는 것을 사용자를 허용하기 위해 "com.vogella.ide.editor.tasks" 플러그인을 확장한다. 

5.1. 색상 정의하기

"org.eclipse.ui.themes" 확장점(extension point)을 위한 확장을 추가 하기 위해 manifest 에디터의 확장(Extensions) 탭을 사용한다. 생성된 엔트리에 오른쪽 마우스 클릭하고, themeElementCategory를 선택하고 사용하다. :

  • id: com.vogella.ide.editor.tasks.settings

  • label: Tasks settings

생성된 엔트리에 오른쪽 마우스 클릭하고 colorDefinition를 선택한다.

사용:

  • id: com.vogella.ide.editor.tasks.key

  • label: Task key color

  • value: 255,0,0

  • categoryId: com.vogella.ide.editor.tasks.settings

plugin.xml 컨텐츠가 다음과 같이 보여야 한다. :

<?xml version="1.0" encoding="UTF-8"?> 
<?eclipse version="3.4"?> 
<plugin> 

	<extension point="org.eclipse.core.contenttype.contentTypes"> 
		<content-type 
        	file-extensions="tasks" 
        	id="com.vogella.ide.contenttype.tasks" 
            name="Tasks" 
            priority="high"> 
		</content-type> 
	</extension> 

	<extension point="org.eclipse.ui.editors"> 
		<editorContentTypeBinding 
        	contentTypeId="com.vogella.ide.contenttype.tasks" 
        	editorId="org.eclipse.ui.genericeditor.GenericEditor"> 
		</editorContentTypeBinding> 
	</extension> 

	<extension point="org.eclipse.ui.genericeditor.presentationReconcilers"> 
		<presentationReconciler 
        	class="com.vogella.ide.editor.tasks.PropertiesReconciler" 
    		contentType="com.vogella.ide.contenttype.tasks"> 
		</presentationReconciler> 
	</extension> 

	<extension point="org.eclipse.ui.themes"> 
		<colorDefinition 
        	categoryId="com.vogella.ide.editor.tasks.settings" 
        	id="com.vogella.ide.editor.tasks.key" 
            label="Task key color" 
            value="255,0,0"> 
		</colorDefinition> 
		<themeElementCategory 
        	id="com.vogella.ide.editor.tasks.settings" 
        	label="Tasks settings"> 
		</themeElementCategory> 
	</extension> 
    
</plugin>

5.2. 검증하기

만약 런타임 이클립스를 실행하면, Window > Preferences > General > Appearance > Colors and Fonts 설정에서 카테고리와 컬러를 보는것이 가능해야 한다.

5.3. 구문 하이라이팅을 위한 색상 사용하기

색상 레지스터리에 접근하고 에디터에서 그것을 사용한다. 다음 코드 짧은 발췌는 도움이 될 것이다.

IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager(); 
ITheme currentTheme = themeManager.getCurrentTheme(); 
ColorRegistry colorRegistry = currentTheme.getColorRegistry(); 
Color color = colorRegistry.get("com.vogella.ide.editor.tasks.key");
package com.vogella.ide.editor.tasks;

import org.eclipse.jface.resource.ColorRegistry;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.TextAttribute;
import org.eclipse.jface.text.presentation.PresentationReconciler;
import org.eclipse.jface.text.rules.DefaultDamagerRepairer;
import org.eclipse.jface.text.rules.IRule;
import org.eclipse.jface.text.rules.RuleBasedScanner;
import org.eclipse.jface.text.rules.Token;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.themes.ITheme;
import org.eclipse.ui.themes.IThemeManager;

public class PropertiesReconciler extends PresentationReconciler {

    public PropertiesReconciler() {

        IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager();
        ITheme currentTheme = themeManager.getCurrentTheme();
        ColorRegistry colorRegistry = currentTheme.getColorRegistry();
        Color color = colorRegistry.get("com.vogella.ide.editor.tasks.key");

        TextAttribute tagAttribute = new TextAttribute(color);

        RuleBasedScanner scanner = new RuleBasedScanner();
        IRule rule = new PropertyNameRule(new Token(tagAttribute));
        scanner.setRules(new IRule[] { rule });
        DefaultDamagerRepairer dr = new DefaultDamagerRepairer(scanner);
        this.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE);
        this.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE);
    }
}

5.4. 변경 테스트하기

Window > Preferences > General > Appearance > Colors and Fonts를 연다.  에디터의 색상을 검색하고 변경한다. 

에디터를 닫고 새로 연다. 새 색상이 사용되었는지를 검증한다. 

5.5. 색상을 업데이트 하기 위해 preferences 리스너 사용하기 

preferences은 사용자 세팅이 지속됩니다. preferences 리스터를 통해 노드에서 변경을 등록 하는 것이 가능하다. 

프로퍼티 조정자(` PropertiesReconciler`) 재정의에서 설치 메서드는 색상을 지속하기 위해 사용되는 "org.eclipse.ui.workbench" 노드에서 변경을 리스팅할 수 있다..

public void install(ITextViewer viewer) { 
	super.install(viewer); 
	IEclipsePreferences node = InstanceScope.INSTANCE.getNode("org.eclipse.ui.workbench"); 
	node.addPreferenceChangeListener(event -> { 
		// TODO 새 색상으로 변경된 룰로 룰을 업데이트한다. 
    	viewer.invalidateTextPresentation(); 
    }); 
}

TODO 를 해결하고 색상이 업데이트 되었는지 체크하라 

preference를 위한 preferences 노드를 찾기 위해 선호도 스파이(preference spy)를 사용 할수 있다. 뷰를 열고 preferences를 위한 trace를 껐다 켰다한다. 색상 변경 이후 그 데이타를 본다.

 

package com.vogella.ide.editor.tasks;

import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jface.resource.ColorRegistry;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.TextAttribute;
import org.eclipse.jface.text.presentation.PresentationReconciler;
import org.eclipse.jface.text.rules.DefaultDamagerRepairer;
import org.eclipse.jface.text.rules.IRule;
import org.eclipse.jface.text.rules.RuleBasedScanner;
import org.eclipse.jface.text.rules.Token;
import org.eclipse.swt.graphics.Color;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.themes.ITheme;
import org.eclipse.ui.themes.IThemeManager;

public class PropertiesReconciler extends PresentationReconciler {

    private ColorRegistry colorRegistry;
    private RuleBasedScanner scanner;
    private IRule rule;

    @Override
    public void install(ITextViewer viewer) {
        super.install(viewer);

        IEclipsePreferences node = InstanceScope.INSTANCE.getNode("org.eclipse.ui.workbench");

        node.addPreferenceChangeListener(event -> {
            updateRule();
            viewer.invalidateTextPresentation();
        });
    }

    private void updateRule() {
        Color color = colorRegistry.get("com.vogella.ide.editor.tasks.key");
        TextAttribute tagAttribute = new TextAttribute(color);
        rule = new PropertyNameRule(new Token(tagAttribute));
        scanner.setRules(new IRule[] { rule });

    }

    public PropertiesReconciler() {

        IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager();
        ITheme currentTheme = themeManager.getCurrentTheme();
        colorRegistry = currentTheme.getColorRegistry();

        scanner = new RuleBasedScanner();
        updateRule();

        DefaultDamagerRepairer dr = new DefaultDamagerRepairer(scanner);
        this.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE);
        this.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE);
    }
}

6. 연습: TODO 프로퍼티를 위한 컨텐츠 지원을 구현

이 연습에서 .tasks 파일을 위한 컨텐츠 지원(코드 완성)을 구현한다. 앞서 논의 했듯이, 그 파일들은 컬럼으로 구분된 키와 값의 쌍을 포함 하여야 한다. 속성(Properties)은 커서가 줄의 시작에 위치 하는 경우에 제안되게 될 것이다. 

6.1. 컨텐츠 지원 확장 추가하기

"com.vogella.ide.editor.tasks" 플러그인 manifest 파일에 "org.eclipse.ui.genericeditor.contentAssistProcessors" 확장점(extension point)을 위한 확장을 추가한다. 

<extension point="org.eclipse.ui.genericeditor.contentAssistProcessors"> 
	<contentAssistProcessor class="com.vogella.ide.editor.tasks.TodoPropertiesContentAssistProcessor" contentType="com.vogella.ide.contenttype.tasks"> 
	</contentAssistProcessor> 
</extension>

TodoPropertiesContentAssistProcessor 구현체는 다음 코드와 유사하게 보여야 한다. 

package com.vogella.ide.editor.tasks; import java.util.Arrays; 

import java.util.List; 
import org.eclipse.jface.text.BadLocationException; 
import org.eclipse.jface.text.IDocument; 
import org.eclipse.jface.text.ITextViewer; 
import org.eclipse.jface.text.contentassist.CompletionProposal; 
import org.eclipse.jface.text.contentassist.ICompletionProposal; 
import org.eclipse.jface.text.contentassist.IContentAssistProcessor; 
import org.eclipse.jface.text.contentassist.IContextInformation; 
import org.eclipse.jface.text.contentassist.IContextInformationValidator; 

public class TodoPropertiesContentAssistProcessor implements IContentAssistProcessor { 

	// public as used later by other code 
	public static final List<String> PROPOSALS = Arrays.asList( "ID:", "Summary:", "Description:", "Done:", "Duedate:", "Dependent:"); 

	@Override 
	public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) { 
		
    	IDocument document = viewer.getDocument(); 
		try { 
			int lineOfOffset = document.getLineOfOffset(offset); 
			int lineOffset = document.getLineOffset(lineOfOffset); 

			// 줄의 시작이 아닌 위치의 경우에 어떠한 컨텐츠 지원도 보여주지 않는다. 
			if (offset != lineOffset) { 
				return new ICompletionProposal[0]; 
			} 
		} catch (BadLocationException e) { 
			// 여기서는 무시하고 계속...
		} 

		return PROPOSALS.stream().filter(proposal -> !viewer.getDocument().get().contains(proposal)) 
								.map(proposal -> new CompletionProposal(proposal, offset, 0, proposal.length())) 
								.toArray(ICompletionProposal[]::new); 
	} 

	@Override 
	public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { 
		return null; 
	} 

	@Override 
	public char[] getCompletionProposalAutoActivationCharacters() { 
		return null; 
	} 

	@Override 
	public char[] getContextInformationAutoActivationCharacters() { 
		return null; 
	} 

	@Override 
    public String getErrorMessage() { 
		return null; 
	} 

	@Override 
	public IContextInformationValidator getContextInformationValidator() { 
		return null; 
	} 
}

6.2. 구현체 테스트하기 

IDE를 시작하고 .tasks 파일을 연다. 그리고 컨테츠 지원 활성화 시키기 위해 CTRL+Space 를 누른다. 그 결과는 다음과 같다:

6.3. 추가적인 연습 - 지연된 컨텐츠 지원 프로세서 구현하기

일반적인 에디터는 기본적으로 비동기적 코드 완성을 사용한다. 즉, 심지어 제안 컴퓨터 중에 하나가 느리더라도 사용자 이터페이스를 막지 않는다. 

코드 완성 프로세스에 지연을 추가 함으로써 이것을 테스트 한다. 심지어 컨텐츠 지원이 촉발되더라도 에디터가 사용 가능하게 남아 있는 것을 확인한다.

이후에 다시 지연을 제거하라 

7. 향상된 코드 완성

7.1. 추가 연습 - 접두사 완성 추가하기

IDocument 클래스는 컨텐츠들을 파싱하고 변경하기 위한 많은 유틸리티들을 제공한다.  TodoPropertiesContentAssistProcessor#computeCompletionProposals 메서드를 확당한다. 그래서 접두사에 일치 시킬수 있다. 

예를 들면, "D"를 입력하면,  D와 일치 하는 모든 제안을 보여야 한다.

 

public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {

    IDocument document = viewer.getDocument();

    try {
        int lineOfOffset = document.getLineOfOffset(offset);
        int lineOffset = document.getLineOffset(lineOfOffset);

        int lineTextLenght = offset - lineOffset;
        String lineStartToOffsetValue = document.get(lineOffset, lineTextLenght).toLowerCase();

        return PROPOSALS.stream()
                .filter(proposal -> !viewer.getDocument().get().contains(proposal)
                        && proposal.toLowerCase().startsWith(lineStartToOffsetValue))
                .map(proposal -> new CompletionProposal(proposal, lineOffset, lineTextLenght, proposal.length()))
                .toArray(ICompletionProposal[]::new);
    } catch (BadLocationException e) {
        e.printStackTrace();
    }
    return new ICompletionProposal[0];
}

7.2. 추가 연습 - 컨텐츠 지원의 자동 활성화 가능하게 하기

현재 컨텐츠 지원은 단지 사용자가 CTRL+Space를 누를때 제안들을 보여 준다.  사용자를 위해서는 분명하지 않을지 모른다. 

IContentAssistProcessor는 getCompletionProposalAutoActivationCharacters 메서드를 사용하여 이 행위(CTRL+Space를 누르는 행위)를 변경하는 것을 허용한다. .

사용자를 위해 CTRL+Space 를 누른느 것은 분명하지 않을지 모른다. 모든 문자 컨텐츠 지원을 활성화하는 것을 구현한다.

@Override
public char[] getCompletionProposalAutoActivationCharacters() {
    String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    return str.toCharArray();
}

8. 연습 : 다른 정보를 사용하여 컨텐츠 지원을 구현하기 

같은 컨텐츠 유형을 위해 복수로 컨텐츠 지원 프로세스를 등록하는 것이 가능하다. 

이 연습에서 에디터에 다른 컨텐츠 지원을 추가한다. 이 프로세서는 _ property를 위한 값을 설정하는 것을 허용하게 될 것이다. 이것은 다른 작업에 종속적인 작업을 모델링 하는 것을 허용한다. 

8.1. 새 컨텐츠 지원 프로세서 구현과 등록

IContentAssistProcessor 인터페이스를 구현하는 DependentTodoContentAssistProcessor 클래스를 생성한다. 

"org.eclipse.ui.genericeditor.contentAssistProcessors" 확장점(extension point)에 확장으로 plugin.xml 파일을 통해 이 클래스를 등록한다.

<extension
      ​point="org.eclipse.ui.genericeditor.contentAssistProcessors">
   ​<contentAssistProcessor
         ​class="com.vogella.ide.editor.tasks.TodoPropertiesContentAssistProcessor"
         ​contentType="com.vogella.ide.contenttype.tasks">
   ​</contentAssistProcessor>
​</extension>
​<extension
      ​point="org.eclipse.ui.genericeditor.contentAssistProcessors">
   ​<contentAssistProcessor
         ​class="com.vogella.ide.editor.tasks.DependentTodoContentProcessor"
         ​contentType="com.vogella.ide.contenttype.tasks">
   ​</contentAssistProcessor>
​</extension>

활성화 데이터에 접근하기 위해, 다음 유틸 클래스를 생성한다.

package com.vogella.ide.editor.tasks; 

import org.eclipse.ui.IEditorPart; 
import org.eclipse.ui.IWorkbench; 
import org.eclipse.ui.IWorkbenchPage; 
import org.eclipse.ui.IWorkbenchWindow; 
import org.eclipse.ui.PlatformUI; 

public class Util { 

	private Util() { 
		// 단지 핼퍼 클래스
	} 

	public static IEditorPart getActiveEditor() { 
		IWorkbench workbench = PlatformUI.getWorkbench(); 
		IWorkbenchWindow activeWorkbenchWindow = workbench.getActiveWorkbenchWindow(); 
		if (null == activeWorkbenchWindow) { 
			activeWorkbenchWindow = workbench.getWorkbenchWindows()[0]; 
		} 

		IWorkbenchPage activePage = activeWorkbenchWindow.getActivePage(); 
		if (activePage == null) { 
			return null; 
		} 
        
		return activePage.getActiveEditor(); 
	} 
    
}

DependentTodoContentAssistProcessor 구현체는 다음과 같이 보여야 한다. :

package com.vogella.ide.editor.tasks; 

import static com.vogella.ide.editor.tasks.Util.getActiveEditor; 
import java.util.Arrays; import org.eclipse.core.resources.IContainer; 
import org.eclipse.core.resources.IResource; 
import org.eclipse.core.runtime.CoreException; 
import org.eclipse.jface.text.BadLocationException; 
import org.eclipse.jface.text.IDocument; 
import org.eclipse.jface.text.ITextViewer; 
import org.eclipse.jface.text.contentassist.CompletionProposal; 
import org.eclipse.jface.text.contentassist.ICompletionProposal; 
import org.eclipse.jface.text.contentassist.IContentAssistProcessor; 
import org.eclipse.jface.text.contentassist.IContextInformation; 
import org.eclipse.jface.text.contentassist.IContextInformationValidator; 
import org.eclipse.ui.IEditorInput; 
import org.eclipse.ui.IEditorPart; 

public class DependentTodoContentAssistProcessor implements IContentAssistProcessor { 

	@Override 
	public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) { 

		IDocument document = viewer.getDocument(); 
		IEditorPart activeEditor = getActiveEditor(); 
        
		if (activeEditor != null) { 
			IEditorInput editorInput = activeEditor.getEditorInput(); 
			IResource adapter = editorInput.getAdapter(IResource.class); 
			IContainer parent = adapter.getParent(); 
            
			try { 
				int lineOfOffset = document.getLineOfOffset(offset); 
				int lineOffset = document.getLineOffset(lineOfOffset); 
				String lineProperty = document.get(lineOffset, offset - lineOffset); 

				// 컨텐츠 지원은 단지 라인에 종속적으로 사용되어야 한다. 
				if (lineProperty.startWith("Dependent:")) { 
					IResource[] members = parent.members(); 
					// Only take resources, which have the "tasks" file extension and skip the current resource itself 
					return Arrays.asList(members).stream().filter( res -> !adapter.getName().equals(res.getName()) && "tasks".equals(res.getFileExtension())) 
                    									.map(res -> new CompletionProposal(res.getName(), offset, 0, res.getName().length())) 
                                                        .toArray(ICompletionProposal[]::new); 
				} 
			} catch (CoreException | BadLocationException e) { 
				// 여기 무시 하고 계속... 
			} 
		} 
        
		return new ICompletionProposal[0]; 
        
	} 

	@Override 
	public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { 
	return null; 
	} 

	@Override 
	public char[] getCompletionProposalAutoActivationCharacters() { 
		return null; 
	} 

	@Override 
	public char[] getContextInformationAutoActivationCharacters() { 
		return null; 
	} 

	@Override 
	public String getErrorMessage() { 
		return null; 
	} 

	@Override 
	public IContextInformationValidator getContextInformationValidator() { 
		return null; 
	} 
}

8.2. 검증하기

IDE를 시작하고 적어도 2 .tasks 파일들이 가능한지를 확인한다 그 파일 중 하나를 오픈한다. 그리고 컨텐츠 지원을 활성화 하기 위한 프로퍼티에 종속된 이후 바로 CTRL+Space 키를 누른다.  결과는 다음과 같아야 한다:

 

9. 연습: 문서 변경에 반응하기

IDocumentSetupParticipant는 기능을 촉발하는 문서의 설정과 변경하는 동안 알림을 받는 것을 허용한다. 컨텐츠 유형을 위한 "org.eclipse.core.filebuffers.documentSetup" 확장점(extension point)을 통한 구현체를 등록 할 수 있다. 

예제에서, .tasks 파일에 빠진 키를 위해 문제점(Problems) 뷰에 마커를 추가 하기 위해 사용한다.

9.1. IDocumentSetupParticipant 구현과 등록

TodoMarkerDocumentSetup은 현재 변경된 IResource를 위한 마커를 적용하기 위한 IDocumentListener을 등록한다.

package com.vogella.ide.editor.tasks; 

import static com.vogella.ide.editor.tasks.Util.getActiveEditor; 
import java.util.Arrays; import java.util.List; 
import java.util.Optional; 
import org.eclipse.core.filebuffers.IDocumentSetupParticipant; 
import org.eclipse.core.resources.IMarker; 
import org.eclipse.core.resources.IResource; 
import org.eclipse.core.runtime.CoreException; 
import org.eclipse.core.runtime.ICoreRunnable; 
import org.eclipse.core.runtime.jobs.Job; 
import org.eclipse.jface.text.DocumentEvent; 
import org.eclipse.jface.text.IDocument; 
import org.eclipse.jface.text.IDocumentListener; 
import org.eclipse.ui.IEditorInput; 
import org.eclipse.ui.IEditorPart; 
import org.eclipse.ui.PlatformUI; 

public class TodoMarkerDocumentSetup implements IDocumentSetupParticipant { 

	private static final String TODO_PROPERTY = "todoProperty"; 

	@Override 
	public void setup(IDocument document) { 
    
		document.addDocumentListener(new IDocumentListener() { 
			private Job markerJob; 
        
			@Override 
			public void documentChanged(DocumentEvent event) { 
				IEditorPart activeEditor = getActiveEditor(); 
				if (activeEditor != null) { 
					IEditorInput editorInput = activeEditor.getEditorInput(); 
					IResource adapter = editorInput.getAdapter(IResource.class); 
					if (markerJob != null) { 
						markerJob.cancel(); 
					} 
					markerJob = Job.create("Adding Marker", (ICoreRunnable) monitor -> createMarker(event, adapter));  // --> 1
					markerJob.setUser(false); markerJob.setPriority(Job.DECORATE); 

					// 계속 타이핑을 핸들링하기 위해 사용자 액션에 반응하기 전에 지연 설정 
					markerJob.schedule(500);  // --> 2
				} 
			} 

			@Override 
			public void documentAboutToBeChanged(DocumentEvent event) { 
				// 필요하지 않음
			} 
		}); 
	}

	private void createMarker(DocumentEvent event, IResource adapter) throws CoreException { 
		String docText = event.getDocument().get(); 
		for (String todoProperty : TodoPropertiesContentAssistProcessor.PROPOSALS) { 
			List<IMarker> markers = Arrays .asList(adapter.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE)); 
			Optional<IMarker> findAny = markers.stream().filter(m -> todoProperty.equals(m.getAttribute(TODO_PROPERTY, ""))).findAny(); 
			if (docText.contains(todoProperty) && findAny.isPresent()) { 
				findAny.get().delete(); 
			} else if (!docText.contains(todoProperty) && !findAny.isPresent()) { 
				IMarker marker = adapter.createMarker(IMarker.PROBLEM); 
				marker.setAttribute(IMarker.MESSAGE, todoProperty + " property is not set"); 
				marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO); 
				marker.setAttribute(IMarker.LOCATION, "Missing line"); 
				marker.setAttribute(TODO_PROPERTY, todoProperty); 
			} 
		} 
	} 
}
1 이 구현체는 Job 구현체를 사용한다. 그래서 UI 쓰레드는 처리 하는 동안 블럭되지 않는다. 
2 job이 문서 변경이 시작 되었기 때문에, 지연으로 시작된다. 그래서 다른 문서의 변경으로 취소 될 수 있고 불필요한 마커 생성이 생략 될 수 있다. 

plugin.xml에 다음 확장을 추가한다..

<extension point="org.eclipse.core.filebuffers.documentSetup"> 
	<participant class="com.vogella.ide.editor.tasks.TodoMarkerDocumentSetup" contentTypeId="com.vogella.ide.contenttype.tasks"> 
    </participant> 
</extension>

9.2. IDocumentSetupParticipant 등록과 구현

그 결과는 다음과 같이 보여야 한다.:

현재 이미지 링크가 깨져 있슴

10. 연습: 작업을 위한 하이라이팅 구현

이클립스는 다른 파일들에 참조 사이에 탐색을 지원한다. 소스 에디터는 전형적으로 열려있는 파일에 참조에 CTRL + left 마우스 클릭을 지원한다.

*.tasks 파일들은 종속(Dependent)을 통해서 가리킬수 있다: 같은 프로젝트 내에 다른 *.task 파일에 프로퍼티 이 연습에서 에디터는 사용자가 종속 작업 파일 사이 탐색하기 위한것을 허용해야한다.

 

10.1. manifest 위존관계 추가하기

"com.vogella.ide.editor.tasks"에 플러그인 의존관계로 "org.eclipse.ui.ide" 추가한다.

10.2. 종속 task 파일을 위한 하이퍼링크 구현하기

다음 IHyperlink 구현체 생성하기.

import org.eclipse.core.resources.IFile; 
import org.eclipse.jface.text.IRegion; 
import org.eclipse.jface.text.hyperlink.IHyperlink; 
import org.eclipse.ui.IWorkbenchPage; 
import org.eclipse.ui.PartInitException; 
import org.eclipse.ui.PlatformUI; 
import org.eclipse.ui.ide.IDE; 

public class ResourceHyperlink implements IHyperlink { 

	private IRegion region; 
	private String hyperlinkText; 
	private IFile resource; 

	public ResourceHyperlink(IRegion region, String hyperlinkText, IFile resource) { 
		this.region = region; 
		this.hyperlinkText = hyperlinkText; 
		this.resource = resource;
	}

	@Override 
	public IRegion getHyperlinkRegion() { 
		return region; 
	} 

	@Override 
	public String getTypeLabel() { 
		return null; 
	} 

	@Override 
	public String getHyperlinkText() { 
		return hyperlinkText; 
	} 

	@Override 
	public void open() { 
	IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); 
		try { 
			IDE.openEditor(activePage, resource); 
		} catch (PartInitException e) { 
			e.printStackTrace(); 
		} 
	}
    
}

다음 IHyperlinkDetector 구현체를 생성한다.

import java.util.Arrays; 
import org.eclipse.core.resources.IContainer; 
import org.eclipse.core.resources.IFile; 
import org.eclipse.core.resources.IResource; 
import org.eclipse.core.runtime.CoreException; 
import org.eclipse.jface.text.BadLocationException; 
import org.eclipse.jface.text.IDocument; 
import org.eclipse.jface.text.IRegion; 
import org.eclipse.jface.text.ITextViewer; 
import org.eclipse.jface.text.Region; 
import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; 
import org.eclipse.jface.text.hyperlink.IHyperlink; 
import org.eclipse.ui.IEditorInput; 
import org.eclipse.ui.IEditorPart; 
import org.eclipse.ui.IWorkbench; 
import org.eclipse.ui.IWorkbenchPage; 
import org.eclipse.ui.IWorkbenchWindow; 
import org.eclipse.ui.PlatformUI; 

public class DependentTodoHyperlinkDetector extends AbstractHyperlinkDetector { 

	private static final String DEPENDENT_PROPERTY = "Dependent:"; 

	@Override 
	public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) { 
		IDocument document = textViewer.getDocument(); 
		IWorkbench workbench = PlatformUI.getWorkbench(); 
		IWorkbenchWindow activeWorkbenchWindow = workbench.getActiveWorkbenchWindow(); 
		if (null == activeWorkbenchWindow) { 
			activeWorkbenchWindow = workbench.getWorkbenchWindows()[0]; 
		} 

		IWorkbenchPage activePage = activeWorkbenchWindow.getActivePage(); 
		IEditorPart activeEditor = activePage.getActiveEditor();

		if (activeEditor != null) { 
			IEditorInput editorInput = activeEditor.getEditorInput(); 
			IResource adapter = editorInput.getAdapter(IResource.class); 
			IContainer parent = adapter.getParent(); 

			try { 
				int offset = region.getOffset(); 
				IRegion lineInformationOfOffset = document.getLineInformationOfOffset(offset); 
				String lineContent = document.get(lineInformationOfOffset.getOffset(), lineInformationOfOffset.getLength()); 

				// 컨텐츠 지원은 단지 종속 라인에서 사용되어야 한다.  
				if (lineContent.startsWith(DEPENDENT_PROPERTY)) { 
					String dependentResourceName = lineContent.substring(DEPENDENT_PROPERTY.length()).trim(); 
					Region targetRegion = new Region(lineInformationOfOffset.getOffset() + DEPENDENT_PROPERTY.length(), lineInformationOfOffset.getLength() - DEPENDENT_PROPERTY.length()); 
					IResource[] members = parent.members(); 

					// 단지 "todo" 파일 확장자를 가지는 리소스만 취한다 
					// 그리고 현재 자신의 리소스를 넘어간다. 
					return Arrays.asList(members).stream() 
								.filter(res -> res instanceof IFile && dependentResourceName.equals(res.getName())) 
								.map(res -> new ResourceHyperlink(targetRegion, res.getName(), (IFile) res)) 
								.toArray(IHyperlink[]::new); 
				} 
			} catch (CoreException | BadLocationException e) { 
				e.printStackTrace(); 
			} 
		} 

		// 새 IHyperlink[0]를 반환하지 않는다 
    	// 왜냐하면 배열 단지 null일 수 있고 또는 비어 있지 않을 수 있기 때문이다
		return null; 
	} 
}

DependentTodoHyperlinkDetector 클래스는 다음처럼 plugin.xml에 등록될 수 있다.:

<extension point="org.eclipse.ui.workbench.texteditor.hyperlinkDetectors"> 
	<hyperlinkDetector activate="true" 
    		class="com.vogella.ide.editor.tasks.DependentTodoHyperlinkDetector" 
        	id="com.vogella.ide.editor.tasks.hyperlinkDetector" 
       		name="Hyperlink to other tasks files" 
        	targetId="org.eclipse.ui.genericeditor.GenericEditor"> 
    </hyperlinkDetector> 
</extension>

10.3. 검증하기

검증하기 위해 적어도 2개 *.tasks 파일들을 생성하고 그 중에 하나를 가르키기 위한 종속적 속성을 사용한다. 연결된 파일을 탐색하기 위해 CTRL + 왼쪽 마우스 클릭을 사용할수 있는것을 검증한다. 

Dependent:Training2.tasks

 

이 연습에서 일반적 에디터에서 vogella 키워드를 위한 하이퍼링크 탐지기를 생성한다.

11.1. 프로젝트를 생성하고 의존관계를 추가한다. 

"com.vogella.ide.editor.companylink" 이름으로 된 새 플러그인 프로젝트를 생성한다..

의존관계로 다음 플러그인들을 추가한다.:

  • org.eclipse.ui

  • org.eclipse.jface

  • org.eclipse.ui.workbench.texteditor

유니크 ID와 기술한 이름으로 "org.eclipse.ui.workbench.texteditor.hyperlinkDetectors" 확장 추가하기.

하이퍼링크를 결정하고 그 하이퍼링크와 상효작용하기 위한 다음 두 클래스를 생성한다. 

package com.vogella.ide.editor.companylink; 

import org.eclipse.jface.text.IRegion; 
import org.eclipse.jface.text.hyperlink.IHyperlink; 
import org.eclipse.swt.program.Program; 

public class VogellaHyperlink implements IHyperlink { 

	private final IRegion fUrlRegion;
    
	public VogellaHyperlink(IRegion urlRegion) { 
		fUrlRegion = urlRegion; 
	} 

	@Override 
	public IRegion getHyperlinkRegion() { 
		return fUrlRegion; 
	} 

	@Override 
	public String getTypeLabel() { 
		return null; 
	} 

	@Override 
	public String getHyperlinkText() { 
		return "Open vogella website"; 
	} 

	@Override 
	public void open() { 
		Program.launch("https://www.vogella.com/"); 
	} 
}
package com.vogella.ide.editor.companylink; 

import org.eclipse.jface.text.BadLocationException; 
import org.eclipse.jface.text.IDocument; 
import org.eclipse.jface.text.IRegion; 
import org.eclipse.jface.text.ITextViewer; 
import org.eclipse.jface.text.Region; 
import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; 
import org.eclipse.jface.text.hyperlink.IHyperlink; 

public class VogellaHyperlinkDetector extends AbstractHyperlinkDetector { 

	public VogellaHyperlinkDetector() { 
	} 

	@Override 
	public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) { 

		IDocument document = textViewer.getDocument(); 
		int offset = region.getOffset(); 

		// 관련 문자 추출한다
		IRegion lineRegion; 
		String candidate; 
		try { 
			lineRegion = document.getLineInformationOfOffset(offset); 
			candidate = document.get(lineRegion.getOffset(), lineRegion.getLength()); 
		} catch (BadLocationException ex) { 
			return null; 
		} 

		// 키워드를 찾는다.
		int index = candidate.indexOf("vogella"); 
		if (index != -1) { 

			// 키워드를 포함하는 영역을 탐색한다. 
			IRegion targetRegion = new Region(lineRegion.getOffset() + index, "vogella".length()); 
			if ((targetRegion.getOffset() <= offset) && ((targetRegion.getOffset() + targetRegion.getLength()) > offset)) {
				// 링크를 생성한다. 
				return new IHyperlink[] { new VogellaHyperlink(targetRegion) };
            }
		} 
        
		return null; 
        
	} 
    
}

11.3. 검증하기

플러그인을 시작하고 텍스트 에디터에 예를 들면, Java 에디터에서 "vogella"를 추가 한다. Ctrl을 누르고 "vogella"를 클릭한다.외부 브라우저에서 https://www.vogella.com/ 웹사이트가 열려야 한다.

12. 연습: task 파일에 코드 마이닝(추출) 정보 추가 하기.

이클립스는 텍스트 에디터에서 그 정보를 향샹 시키기 위해 추가적인 정보를 보여 주는 것을 지원한다. 그 정보는 저장되지 않는다. 그러나 사용자가 더 나은 내용을 이해 하는데 도움을 준다. 또한 코드 마이닝은 행위를 등록하고 텍스트 에디터를 사용한 디렉토리의 행위를 수행할 수 있다. 

이 연습에서 추가적인 정보를 보여주고 사용자가 코드 마이닝을 통해 어떤 행위를 수행하는 것을 허용하기 위해 task 에디터를 위한 코드 마이닝을 향상 시키게 될 것이다. 

12.1. 코드 마이닝 구현과 등록 

라인 헤더 어노테이션을 생성한 다음 클래스를 구현한다. 

package com.vogella.ide.editor.tasks; 

import java.util.concurrent.CompletableFuture; 
import org.eclipse.core.runtime.IProgressMonitor; 
import org.eclipse.jface.text.BadLocationException; 
import org.eclipse.jface.text.IDocument; 
import org.eclipse.jface.text.ITextViewer; 
import org.eclipse.jface.text.codemining.ICodeMiningProvider; 
import org.eclipse.jface.text.codemining.LineHeaderCodeMining; 

public class TaskCodeMining extends LineHeaderCodeMining { 

	public TaskCodeMining(int beforeLineNumber, IDocument document, ICodeMiningProvider provider) throws BadLocationException { 
		super(beforeLineNumber, document, provider); 
	} 

	@Override 
	protected CompletableFuture<Void> doResolve(ITextViewer viewer, IProgressMonitor monitor) { 
		return CompletableFuture.runAsync(() -> { super.setLabel("This is additional information about the tasks"); }); 
	} 
    
}

다음 ICodeMiningProvider의 구현체를 생성한다..

package com.vogella.ide.editor.tasks; 

import java.util.ArrayList; 
import java.util.List; 
import java.util.concurrent.CompletableFuture; 
import org.eclipse.core.runtime.IProgressMonitor; 
import org.eclipse.jface.text.BadLocationException; 
import org.eclipse.jface.text.IDocument; 
import org.eclipse.jface.text.ITextViewer; 
import org.eclipse.jface.text.codemining.ICodeMining; 
import org.eclipse.jface.text.codemining.ICodeMiningProvider; 

public class TaskCodeMiningProvider implements ICodeMiningProvider { 

	public TaskCodeMiningProvider() { 
	} 

	@Override 
	public CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor) { 

		return CompletableFuture.supplyAsync(() -> { 
			List<ICodeMining> minings = new ArrayList<>(); 
			IDocument document = viewer.getDocument(); 
			try { 
				minings.add(new TaskCodeMining(0, document, this)); 
			} catch (BadLocationException e) { 
				e.printStackTrace(); 
			} 
            return minings; 
		}); 
	} 

	@Override 
	public void dispose() { 
	} 

}

"org.eclipse.ui.genericeditor.reconcilers" 확장을 통해 컨텐츠 유형을 위한 기본 CodeMiningReconciler를 등록한다..

<extension point="org.eclipse.ui.genericeditor.reconcilers"> 
	<reconciler class="org.eclipse.jface.text.codemining.CodeMiningReconciler" 
			contentType="com.vogella.ide.contenttype.tasks"> 
	</reconciler> 
</extension>

이제 plugin.xml에 다음 확장을 추가한다..

<extension point="org.eclipse.ui.workbench.texteditor.codeMiningProviders"> 
	<codeMiningProvider class="com.vogella.ide.editor.tasks.TaskCodeMiningProvider" 
			id="com.vogella.ide.editor.tasks.codeMiningProvider" 
			label="Show additional task info"> 
		<enabledWhen> 
			<with variable="editorInput"> 
				<adapt type="org.eclipse.core.resources.IFile"> 
					<test property="org.eclipse.core.resources.contentTypeId" 
							value="com.vogella.ide.contenttype.tasks" /> 
				</adapt> 
			</with>
		</enabledWhen> 
	</codeMiningProvider> 
</extension>

12.2. Validate

에디터가 헤더에 추가 정보를 보여주는지 확인하라.

13. 추가 연습 - 코드 마이닝에 액션 추가하기

사용자가 몇몇 액션을 촉발하는 것을 허용하기 위해  TaskCodeMining에 getAction을 재정의 한다. 

package com.vogella.ide.editor.tasks; 

import java.util.concurrent.CompletableFuture; 
import java.util.function.Consumer; 
import org.eclipse.core.runtime.IProgressMonitor; 
import org.eclipse.jface.dialogs.MessageDialog; 
import org.eclipse.jface.text.BadLocationException; 
import org.eclipse.jface.text.IDocument; 
import org.eclipse.jface.text.ITextViewer; 
import org.eclipse.jface.text.codemining.ICodeMiningProvider; 
import org.eclipse.jface.text.codemining.LineHeaderCodeMining; 
import org.eclipse.swt.events.MouseEvent; 

public class TaskCodeMining extends LineHeaderCodeMining { 

	private ITextViewer viewer; 
    
	public TaskCodeMining(int beforeLineNumber, IDocument document, ICodeMiningProvider provider, boolean isValid) throws BadLocationException { 
		super(beforeLineNumber, document, provider); 
	} 

	@Override 
	protected CompletableFuture<Void> doResolve(ITextViewer viewer, IProgressMonitor monitor) { 
		this.viewer = viewer; 
		return CompletableFuture.runAsync(() -> { super.setLabel("This is additional information about the tasks"); }); 
	} 

	@Override 
	public Consumer<MouseEvent> getAction() { 
    	return r->showMessageDialog(); 
    } 

	private void showMessageDialog() { 
		MessageDialog.openInformation(viewer.getTextWidget().getShell(), "Clicked", "You clicked on the code mining annotation"); 
	} 
}

14. Optional exercise - Extend your code mining

Clone the Git repository located at https://github.com/angelozerr/EclipseConFrance2018. Import the included project and test them.

Use the information to extend your code mining, e.g., add code minings to each line in your editor.

15. 추가 연습 - JDT 코드 마이닝 리뷰하기

https://github.com/angelozerr/jdt-codemining 위치한 Git 저장소를 복제한다.

코드를 복제하고 프로젝트를 import 한다. 런타임 이클립스에 해당소스를 포함시킨다. 코드를 리뷰한다.

16. 연습: 사용자 정의 에디터 구현하기

새로운것을 저으이 하는 데신에 일반 에디터를재 사용하는 것을 권장하기 때문에, 이것은 여전히 지원된다. 이 장은 어떻게 할 수 있는지 설명을 제공한다. 

16.1. IEditorPart 와 EditorPart

이클립스 IDE를 위한 새 에디터를 정의 하기 위해, 전형적으로, * IEditorInput 클래스를 생성한다. * "org.eclipse.ui.editors" 확장점(extension point)을 위한 확장을 정의한다. * IEditorPart를 확장한 클래스를 구현한다.

IEditorInput는 에디터를 위한 모델로 서비스한다. 이클립스는 IEditorInput 객체 버퍼를 제공하게 될 것이고 그러므로   그 객체는 상대적으로 작아야 한다.

그것은 모델의 경령화 표현을 제공되게 되도록 되어 있다. 예를들면 이클립스 IDE 완전한 파일로 핸들링 하지 않고 파일을 식별하는 IEditorInput 객체를 사용한다. 

IEditorInput의 equals()은 에디터의 본질을 정의한다. 즉, 만약 에디터가 이미 열려 있는지 아닌지 결정하는데 사용된다.

에디터는  init() 메서드에서 IEditorSite 와IEditorInput 를 받는다. setInput() 메서드를 통한 input 과 setSite() 메서드를 통한 side를 설정해야 한다. .

createPartControl() (사용자 인터페이스를 생성하는 ) 이전에 init()이 호출된다. 그러므로 UI 생성하는 동안 input을 사용할 수 있다. 

자체 퍼스펙티브를 정의 한다면, 퍼스펙티브 구현체에 다음 코드를 통해서 에디터 영역을 가능하게 할 수 있다. 

import org.eclipse.ui.IPageLayout; 

public class Perspective implements IPerspectiveFactory { 

	public void createInitialLayout(IPageLayout layout) { 
		//layout.setEditorAreaVisible(false); 
        layout.setFixed(true); 
	} 
    
}

16.2. 에디터 제목과 툴팁을 설정하기 

기본적으로, 에디터는 IEditorInput으로 부터 툴팁과 제목을 사용하게될 것이다.  그러므로 에디터에 타이틀과 툴팁을 변경하는 것을 원할지 모른다. 에디터의 제목을 설정하기 위해 setPartName()와 툴닙을 설정하기 위해 getTitleToolTip()을 사용한다. 툴팁에 대한 자세한 것은 버그 https://bugs.eclipse.org/bugs/show_bug.cgi?id=107772 를 보라.

16.3. 에디터 내용 저장하기 

isDirty()메서드는 에디터가 변경된 데이타를 포함하는지를 결정하다. 이 더티 상태에 벼경에 대한 워크벤치 정보를 위해, 이벤트를 시작한다.

firePropertyChange(IEditorPart.PROP.DIRTY);

Adding Colors and Font preferences blog post

https://www.eclipse.org/eclipse/platform-text/eclipseCon/talk.pdf

https://flylib.com/books/en/1.70.1/creating_a_text_editor_with_jface_text.html

https://www.eclipse.org/articles/Article-Folding-in-Eclipse-Text-Editors/folding.html

https://wiki.eclipse.org/FAQ_How_do_I_use_the_text_document_model%3F

18. vogella 트레이닝과 컨설팅 지원

온라인 트레이닝

fitness_center

사이트 트레이닝

group

컨설팅

Copyright © 2012-2019 vogella GmbH. Free use of the software examples is granted under the terms of the Eclipse Public License 2.0. This tutorial is published under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Germany license.

Licence

Source code

Sponsor our Open Source development activities and our free content to help us make development easier for everyone

번역 문서 저작권 표시 :
저작자표시-비영리-동일조건 변경 허락 (CC BY-NC-SA)
원문 저작권 표시 :
(ExtensionsAdding menus, toolbar and popup menus to the Eclipse IDE, by Lars Vogel, (c) 2009 - 2020 vogella GmbH Version 3.2, 29.08.2016
, CC BY-NC-SA )
소스 코드의 경우 Eclipse Public License 2.0 를 따른다.

[원문 출처] www.vogella.com/tutorials/EclipseCommands/article.html


해당 문서의 경우 vogella사의 Eclipse IDE Extensions 
Document 문서 일부를 번역 한 것입니다. 
번역본인 해당 문서의 경우 역자에게 있음을 알리며 상업적 이용을 불허합니다. 
번역 작업 시 이클립스 내 고유이름을 가진 값의 경우, 가령 Tab의 명칭 등의 경우 화면에서 표시되는 이름이므로 별도로 번역없이 제공하거나 괄호를 사용하여 동시에 명기한다.
저작자표시-비영리-동일조건 변경 허락 (BY-NC-SA)

www.sogomsoft.co.kr (주) 소곰소프트 

1. 커멘드 소개

이클립스 프레임워크는 사용자 인터페이스에 액션을 기여하기 위해 커멘트 프레임워크를 사용한다. 

이클립스에서 커멘드는 컴포넌트의 선언적 기술이며 구현 상세로 부터 독립적이다. 

커멘드는 카테고리화 될 수 있고 사용자 인터페이스에 할당 될 수 있고 키 바인딩은 커멘드를 위해 정의 될 수 있다. 핸들러를 통해 커멘드의 동작이 정의 된다. 

1.1. 이클립스 3.x API로 커멘트 정의하기

이클립스3.x API는 커멘드를 정의하기 위해 확장점(extension points)을 사용한다. 3 확장점이 여기서 역할을 수행한다. 다음 확장점(extension points)은 이클립스 플러그인을 위해 메뉴 또는 툴바 컨트리뷰션을 정의 하는데 관련 있다. 

표 1. 확장점 설명

확장점(Extension point)

상세 설명

org.eclipse.ui.command

커멘드를 선언적으로 기술한다.

org.eclipse.ui.handlers

동작 정의 한다. 즉, 호출 되어야 하는 Java 클래스

org.eclipse.ui.menu

사용자 인터페이스에서 커멘트가 포함 되어야 하는 위치와 방법 즉, 메뉴, 팝업 메뉴, 툴바 등등.

2.2. 메뉴 확장점의 위치 정의 하기

커멘드의 위치를 정의 할 수있다. 예를들면, 저장(Save) 메뉴 엔트리 다음에 전체 저장(Save All) 메뉴 엔트리가 위치해야 한다. "?before=ID" 또는 "?after=ID" 같은 패턴을 사용함으로써 이 로케이션URL(locationURI)에 커멘드의 상대적 위치를 정의 할 수 있다. .

ID는 menu ID, 또는 item ID 같은 존재 하는 구별자 이름이 될 수 있다. 커멘드는 상응하는 ID로 에리먼트의 앞과 뒤에 놓일 수 있다.

예를 들면, oneEntry 메뉴 엔트리 앞에 fileMenu 메뉴에서 커멘드를 놓기 위해, menu:fileMenu?after=oneEntry 로케이션URI(locationURI)를 사용한다.

3. 핸들러(Handler)

3.1. 커멘드 핸들러

org.eclipse.ui.handlers 확장점(extension point)은 커멘드가 실행될때 한번 호출 되는 어떤 클래스에 커멘드를 연결한다.

커멘드의 동작은 핸들러를 통해서 정의된다. 핸들러(handler)는 커멘드가 호출 될때 한번 실행되는 클래스이다. org.eclipse.core.commands 패키지로 부터 IHandler 인터페이스를 구현 해야 한다. .

IHandler 인터페이스 기본 구현체로 제공되는 org.eclipse.core.commands.AbstractHandler 클래스를 확장 할 수 있다.

핸들러가 실행되면 execute() 메서드가 호출된다. 

execute() 메서드에서 HandlerUtil 클래스를 통해서 시스템 값에 접근 할 수있다. 

3.2. 핸들러의 효력

핸들러는 핸들러가 커멘트에 유효한지에 따라 정의된 조건하에서 조건(activeWhen)으로 정의 될 수 있다. 커멘트들 위해 여러 핸들러들이 정의 될 수 있다. 단지 하나의 핸들러가 어떤 조건의 커멘드를 위해 유효 할 수 있고, 만약 여러개 핸들러가 유효하다면, 이클립스 런타임은 그 커멘드를 비활성화 한다. (역자주, 여러개의 핸들러와 매핑될 수 있지만 하나의 핸들러만 유효해야 하고 여러 핸들러가 유효한 상태이면 그 커멘드가 비활성화 된다.)

4. 튜토리얼의 전제조건

이 튜토리얼은 이클립스 플랫폼 개발의 기본적 이해를 가지고 있다는 것을 가정한다. 만약 기본 정보를 필요로 하면 Eclipse RCP Tutorial 또는 Eclipse Plug-in Tutorial 을 보라.

5. 튜토리얼: 커멘드(Commands )와 메뉴(menus) 

5.1. 연습: 커멘드 정의하기

이 연습에서 어플르케이션을 빠져나오기 위해 허용하는 커멘드를 생성한다. 새 RCP project "Hello RCP" 템플릿을 사용하여 "de.vogella.rcp.commands.first" 생성하라. plugin.xml을 열고 확장(Extensions) 탭을 선택하라. Add…​ 버튼을 눌러라.

확장 "org.eclipse.ui.commands"를 검색하고, 해당 확장을 선택하고 finish버튼을 누른다.

확장(extension)을 오른쪽 마우스 클릭하고 New → command를 선택함으로써 새 커멘드를 생성한다.

단지 "Generic" 엔트리만 보인다면,  아마 "Eclipse for RCP/Plug-in Developers"를 다운로드 하지 않은 것이 분명하다. Eclipse Installation를 보라.

"de.vogella.rcp.commands.first.commands.Exit"를 id*인 ID 항목에 "Exit"를 name*인 이름 항목에 설정한다. defaultHandler인 기본핸들러 항목에 "de.vogella.rcp.commands.first.commands.ExitHandler" 클래스를 넣는다..

"org.eclipse.core.commands.AbstractHandler"를 확장해야 하는 클래스를 생성해야 하기 위해 "defaultHandler" 하이퍼 링크를 누른다.

package de.vogella.rcp.commands.first.commands; 

import org.eclipse.core.commands.AbstractHandler; 
import org.eclipse.core.commands.ExecutionEvent; 
import org.eclipse.core.commands.ExecutionException; 
import org.eclipse.ui.handlers.HandlerUtil; 

public class ExitHandler extends AbstractHandler { 
	
    @Override 
    public Object execute(ExecutionEvent event) throws ExecutionException { 
    	HandlerUtil.getActiveWorkbenchWindow(event).close(); 
        return null; 
    } 
}

5.2. 메뉴에서 커멘드 사용하기 

정의된 커멘드는메뉴에서 사용되어야 한다. org.eclipse.ui.menus 확장점(extension point)에 확장(extension)을 추가한다. 확장점(extension point)에 오른쪽 마우스 클릭하고 New > menuContribution.을 선택한다. 

"menu:org.eclipse.ui.main.menu" 로케이션URI(locationURI)로 새 메뉴 컨트리 뷰션을 생성한다. URL이 정확한 스펠링으로 되어 있는지 확인하라 아니면 그 메뉴가 보여지지 않게 될 것이다. 

메뉴컨트리뷰션(menucontribution)에 오른쪽 클릭하고 New > Menu를 선택하라. label*인 라벨 항목에 "File" 과 id 인 ID 항목에 "fileMenu"로 메뉴를 추가 하라.

 메류를 선택하고, 그 메뉴에 오른쪽 마우스 클릭하고, New→ Command를 선택한다. commandID를 지정하라. "Exit"를 라벨 항목에 "Exits the application"를 툴팁 항목에 설정하라..

plugin.xml 파일에 다음과 예제와 같이 보이는 결과가 만들어져야 한다. 

<?xml version="1.0" encoding="UTF-8"?> 
<?eclipse version="3.4"?> 
<plugin> 
   	<extension id="application" point="org.eclipse.core.runtime.applications"> 
       	<application> 
           	<run class="de.vogella.rcp.commands.first.Application"> 
               </run> 
           </application> 
    </extension> 
	<extension point="org.eclipse.ui.perspectives"> 
       	<perspective name="RCP Perspective" 
           			class="de.vogella.rcp.commands.first.Perspective" 
                    id="de.vogella.rcp.commands.first.perspective"> 
        </perspective> 
    </extension> 
    <extension point="org.eclipse.ui.commands"> 
      	<command defaultHandler="de.vogella.rcp.commands.first.commands.ExitHandler" 
           		id="de.vogella.rcp.commands.first.commands.Exit" 
                name="Exit"> 
        </command> 
    </extension> 
    <extension point="org.eclipse.ui.menus"> 
       	<menuContribution locationURI="menu:org.eclipse.ui.main.menu"> 
           	<menu id="fileMenu" label="File"> 
               	<command commandId="de.vogella.rcp.commands.first.commands.Exit" 
                   		label="Exit" style="push" 
                        tooltip="Exit the application"> 
                </command> 
            </menu> 
         </menuContribution> 
    </extension> 
 </plugin>

예제를 실행하면. 파일 메뉴가 보여야 하고 "Exit" 엔트리를 선택하면 어플리케이션이 종료된다.

6. Tutorial: 커멘드(Commands) 와 툴바(toolbars)

6.1. 개요

어플리케이션 툴바와 뷰 툴바에 커멘드를 추가 할 수 있다. 

6.2. 어플리케이션 툴바 (coolbar)

"de.vogella.rcp.intro.commands.toolbar" 이름의 새 프로젝트를 생성한다. 탬플릿 "RCP application with a view" 을 사용한다. "de.vogella.rcp.intro.commands.toolbar.handler.Hello"을 기본 핸들러로  "de.vogella.rcp.intro.commands.toolbar.Hello" 커멘드를 생성한다. 이 핸들러가 JFace 다이얼로그를 연다.

package de.vogella.rcp.intro.commands.toolbar.handler; 

import org.eclipse.core.commands.AbstractHandler; 
import org.eclipse.core.commands.ExecutionEvent; 
import org.eclipse.core.commands.ExecutionException; 
import org.eclipse.jface.dialogs.MessageDialog; 
import org.eclipse.ui.handlers.HandlerUtil; 

public class Hello extends AbstractHandler { 

	@Override 
    public Object execute(ExecutionEvent event) throws ExecutionException { 
    	MessageDialog.openInformation(HandlerUtil.getActiveWorkbenchWindow( event).getShell(), "Info", "Info for you"); 
        return null; 
    } 
}

"org.eclipse.ui.menus extension" 확장에 메뉴컨트리뷰션(menucontribution)을 추가한 후 "toolbar:org.eclipse.ui.main.toolbar"에 로케이션 URI(location URI)을 설정한다.  앞서 생성한 메뉴 컨트리뷰션에 자식 엘리먼트로 툴바를 추가한다.

툴바에 "de.vogella.rcp.intro.commands.toolbar.Hello" 커멘드를 추가한다. 그 커멘트에 라벨과 아이콘을 작성한다.

ApplicationWorkbenchWindowAdvisor 의 configurer.setShowCoolBar(true); 메서드 호출을 통해 어플리케이션 툴바를 활성화 시킨다.

package de.vogella.rcp.intro.commands.toolbar; 

import org.eclipse.swt.graphics.Point; 
import org.eclipse.ui.application.ActionBarAdvisor; 
import org.eclipse.ui.application.IActionBarConfigurer; 
import org.eclipse.ui.application.IWorkbenchWindowConfigurer; 
import org.eclipse.ui.application.WorkbenchWindowAdvisor; 

public class ApplicationWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor { 

	public ApplicationWorkbenchWindowAdvisor( 
    	IWorkbenchWindowConfigurer configurer) { 
        	super(configurer); 
        } 
   	
    public ActionBarAdvisor createActionBarAdvisor( IActionBarConfigurer configurer) { 
    	return new ApplicationActionBarAdvisor(configurer); 
    } 
    
    public void preWindowOpen() { 
    	IWorkbenchWindowConfigurer configurer = getWindowConfigurer(); 
        configurer.setInitialSize(new Point(400, 300)); 
        configurer.setShowStatusLine(false); 
        configurer.setShowCoolBar(true); 
        configurer.setTitle("RCP Application"); 
    } 
}

결과는 다음처럼 보이게 된다. 만약 툴바에 엘리먼트를 선택하면 정보 대화창이 열려야 한다.

6.3. 뷰툴바에 기여

뷰 툴바에 직접적으로 커멘드를 추가할 수 있다. 그 것을 위해 이전 예제를 확장한다.  다음에  IPerspectiveFactory를 상속 받은 퍼스펙티브("Perspective") 클래스를 변경하라 (단독 실행(스탠드-얼론, stand-alone) 뷰는 자신의 툴바를 가지지 않는다.).

package de.vogella.rcp.intro.commands.toolbar; 

import org.eclipse.ui.IPageLayout; 
import org.eclipse.ui.IPerspectiveFactory; 

public class Perspective implements IPerspectiveFactory { 

	public void createInitialLayout(IPageLayout layout) { 
    	String editorArea = layout.getEditorArea(); 
        layout.setEditorAreaVisible(false); 
        layout.setFixed(true); layout.addView(View.ID, IPageLayout.LEFT, 1.0f, editorArea); 
    }
}

"toolbar:de.vogella.rcp.intro.commands.toolbar.view" 값을 locationURI: 항목에 로케이션 URI를 사용하는 "org.eclipse.ui.menus" 확장점에 새 메뉴컨트리뷰션을 생성한다.  여기에 toolbar: 접두사는 툴바에 방금 생성한 메뉴컨트리뷰션 추가하기 위해 시스템을 말하고 두번째 인수는 그 뷰의 ID이다. 

이 메뉴컨트리뷰션을 위해 새 커멘드를 생성하고 "de.vogella.rcp.intro.commands.toolbar.Hello"로 커멘드 ID를 설정한다. 그 커멘드의 라벨에 "Say Hello"를 입력한다.

새 뷰 컨트리뷰션을 보기위해 어플리케이션을 실행하라.

다음은 어플리케이션 쿨바에 드롭다운 목록을 추가한다. 

이 드롭다운 목록 결과물은 약간 낮설다. 나중에 다른 (실제) 커멘드가 할당되게 될 드롭다운 커멘드 핼퍼(도우미)를 생성한다.

따라서 "referenceToMenuId" 값을 ID로 커멘드를 생성하고 기본 핸들러를 관리 한다. 예를 들면,  "de.vogella.rcp.intro.commands.toolbar.handler.Hello"를 재사용할 수 있다.

"org.eclipse.ui.menus" 확장점에 새 메뉴컨트리뷰션을 추가하고, 그 컨트리뷰션에 "toolbar:org.eclipse.ui.main.toolbar"를 로케이션 URI(location URI)를 설정한다. 새 확장에 툴바와 그 툴바에 새 커멘드를 추가한다. 그 커멘드에 ID는 referenceToMenuId를 사용하는 ID에 라벨과 아이컨을 부여하고  "pulldown"로 스타일을 변경한다.

As the id use referenceToMenuId give it a label and an icon and change the style to "pulldown".

새 메뉴컨트리뷰션을 생성하고 "menu:referenceToMenuId"을 로케이션 URI(locationURI)로 설정한다.

referenceToMenuId 는 커멘트데서 앞서 사용했던 ID 이다. 

이 풀다운 메뉴에 존재하는 커멘드 "de.vogella.rcp.intro.commands.toolbar.Hello"를 각각 다른 라벨을 사용하여 2번 추가한다. 

어플리케이션을 실행하면, 이제 어플리케이션 툴바에 드롭다운 메뉴를 가지게 된다. 

뷰에 드롭다운 메뉴를 얻기 위해, 기존 뷰 툴바 컨트리뷰션에  "referenceToMenuId" 커멘드를 추가하라. 

7. 튜토리얼: 커멘드와 컨텍스트 메뉴

이 예제에서 SWT 테이블 위젯에 컨텍스트 메뉴를 추가한다. "RCP application with a view" 샘플을 기반으로 de.vogella.rcp.intro.commands.popup 이름의 새 프로젝트를 생성한다.

"de.vogella.rcp.intro.commands.popup.showSelected" ID 와 "Show" 라는 이름으로 새 커멘드를 생성한다.

이 예제에서 기본 핸들러를 사용하지 않게 될 것이다.  따라서 plugin.xml 파일에 "org.eclipse.ui.handlers" 확장점을 추가 하고 핸들러를 추가한다. 첫번째 파리미터는 커멘드ID(commandId)이고 두번째 파라미터는 핸들러를 위한 클래스이다. 클래스 이름으로 de.vogella.rcp.intro.commands.popup.handler.ShowSelected를 사용한다.

이제 핸들러에 이 소스를 구현한다. 이 소스는 단지 콘솔에 선택된 엘리먼트를 출력한다. 

package de.vogella.rcp.intro.commands.popup.handler; 

import java.util.Iterator; 
import org.eclipse.core.commands.AbstractHandler; 
import org.eclipse.core.commands.ExecutionEvent; 
import org.eclipse.core.commands.ExecutionException; 
import org.eclipse.jface.viewers.ISelection; 
import org.eclipse.jface.viewers.IStructuredSelection; 
import org.eclipse.ui.handlers.HandlerUtil; 

public class ShowSelected extends AbstractHandler { 
	
    @SuppressWarnings("unchecked") 
    @Override 
    public Object execute(ExecutionEvent event) throws ExecutionException { 
    
    	ISelection selection = HandlerUtil.getActiveWorkbenchWindow(event) .getActivePage().getSelection(); 
        
        if (selection != null & selection instanceof IStructuredSelection) { 
        	IStructuredSelection strucSelection = (IStructuredSelection) selection; 
            for (Iterator<Object> iterator = strucSelection.iterator(); iterator.hasNext();) { 
            	Object element = iterator.next(); 
            	System.out.println(element.toString()); 
            } 
        } 
        
        return null; 
    } 
    
}

자동적으로 생성되게 될 뷰의 ID가 "de.vogella.rcp.intro.commands.popup.view" 인 곳에  "popup:de.vogella.rcp.intro.commands.popup.view" 로 로케이션 URI를 설정한 새 메뉴컨트리뷰션을 추가한다.

새 메뉴컨트리뷰션을 오른쪽 마우스 클릭하고 New → Command를 선택한다. Assign your command to the field "commandId" 필드에 커멘드를 지정하고. 라벨 필드에  "One Item selected" 을 입력한다.

이제 뷰에 메뉴관리자(MenuManager)를 추가했다. 뷰 클래스를 선택하고 다음처럼 변경한다. 

package de.vogella.rcp.intro.commands.popup; 

import java.util.ArrayList; 
import java.util.List; 
import org.eclipse.jface.action.MenuManager; 
import org.eclipse.jface.viewers.ArrayContentProvider; 
import org.eclipse.jface.viewers.LabelProvider; 
import org.eclipse.jface.viewers.TableViewer; 
import org.eclipse.swt.SWT; 
import org.eclipse.swt.graphics.Image; 
import org.eclipse.swt.widgets.Composite; 
import org.eclipse.swt.widgets.Menu; 
import org.eclipse.ui.ISharedImages; 
import org.eclipse.ui.PlatformUI; 
import org.eclipse.ui.part.ViewPart; 

public class View extends ViewPart { 

	private TableViewer viewer; 
    
    class ViewLabelProvider extends LabelProvider { 
    
        @Override 
        public Image getImage(Object obj) { 
        	return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_ELEMENT); 
        } 
        
    } 
    
    @Override 
    public void createPartControl(Composite parent) { 
    
    	viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); 
        viewer.setContentProvider(ArrayContentProvider.getInstance()); 
        viewer.setLabelProvider(new ViewLabelProvider()); 
        viewer.setInput(getData()); 
        
        // 메뉴 관리자를 생성하고 컨텍스트 메뉴를 생성한다. 
        MenuManager menuManager = new MenuManager(); 
        Menu menu = menuManager.createContextMenu(viewer.getTable()); 
        
        // SWT 위젯에 메뉴를 설정한다. 
        viewer.getTable().setMenu(menu); 
        
        // 프레임워크에 메뉴를 등록한다.  
        getSite().registerContextMenu(menuManager, viewer); 
        
        // 선택 가능한 뷰어를 만든다.
        getSite().setSelectionProvider(viewer); 
	
    } 
        
    /** 
      * 뷰어의 컨트롤에 포커스 요청을 전달한다.  
      */
    @Override 
    public void setFocus() { 
      	viewer.getControl().setFocus(); 
    } 
        
    private List<String> getData() { 
       	List<String> list = new ArrayList<String>(); 
        list.add("One"); 
        list.add("Two"); 
        list.add("Three"); 
            
        return list; 
    } 
    
}

어플리케이션을 실행하라. 테이블에 오른쪽 마우스 클랙을 수행하면, 메뉴가 볼수 있게 되어야 한다. 마약 그 메뉴를 선택하면,  선택된 아이템의 이름이 콘솔에 기록되어야 한다.

8.1. 이클립스 커멘드 리소스

Eclipse Commands Advanced

Key bindings for Commands

http://wiki.eclipse.org/Platform_Command_Framework Command Framework wiki

http://wiki.eclipse.org/Command_Core_Expressions Commands Core Expressions

번역 문서 저작권 표시 :
저작자표시-비영리-동일조건 변경 허락 (CC BY-NC-SA)
원문 저작권 표시 :
(Eclipse IDE Plug-in Development: Plug-ins, Features, Update Sites and IDE Extensions, by Lars Vogel (c) 2009 - 2020 vogella GmbHVersion 5.5,31.08.2018, CC BY-NC-SA )
소스 코드의 경우 Eclipse Public License 2.0 를 따른다.

[원문 출처] www.vogella.com/tutorials/EclipsePlugin/article.html


해당 문서의 경우 vogella사의 Eclipse IDE Extensions 
Document 문서 일부를 번역 한 것입니다.
번역본인 해당 문서의 경우 역자에게 있음을 알리며 상업적 이용을 불허합니다. 
번역 작업 시 이클립스 내 고유이름을 가진 값의 경우, 가령 Tab의 명칭 등의 경우 화면에서 표시되는 이름이므로 별도로 번역없이 제공하거나 괄호를 사용하여 동시에 명기한다.
저작자표시-비영리-동일조건 변경 허락 (BY-NC-SA)

www.sogomsoft.co.kr (주) 소곰소프트 

 

이클립스 IDE 플러그인 개발: Plug-ins, Features, Update Sites 와 IDE Extensions

Lars Vogel (c) 2009 - 2020 vogella GmbHVersion 5.5,31.08.2018

이 튜토리얼은 사용자 정의 플러그인으로 이클립스 IDE를 확장 하기 위한 방법을 기술한다. 

 

1. 이클립스 IDE 확장하기

이클립스 플랫폼과 이클립스 IDE는 기능을 추가, 제거, 또는 존재하는 기능을 사용자 정의 하는 것을 가능하게 하는 확장 가능한 프레임워크이다. 

이클립스 IDE의 모든 다운로드는 소프트웨어 개발을 지원하는 것에 맞춰진 특별한 이클립스 어플리캐이션을 볼수 있다. 이클립스 어플리케이션은 플러그인으로 불리는 개별 소프트웨어 컴포너트로 구성되어 있다. 예를 들면, 이클립스 IDE는 JDT 플러그인들을 통해 Java 어플리케션을 개발 하기 위한 기능을 제공한다.

이클립스가 확장가능한 프레임워크로 만들어 졌기 때문에, 플러그인을 사용할 수 있고 다른 플러그인을 확장 할 수 있다. 

개발 하는 동안, 플러그인을 테스트 하고 디버깅하기 위해 이클립스 IDE의 새 인스턴스를 시작할 수 있다. 새 인스턴스는 때때로 런타임 이클립스 내부 인스턴스로 불린다.

플러그인 또는 MANIFEST 파일의 컨텍스트 메뉴에서 Run As  Eclipse Application 을 선택하는 것이 런타임 이클립스를 시작하는 가장 시윈 방법이다. 기본적으로, 이 명령어는 워크스페이스와 대상 환경으로 부터 모든 플러그인을 얻고, 그 플러그인들로 부터 런타임 이클립스를 시작한다. 그 워크스페이스에서 하나가 사용된다.

런타임 이클립스를 디버깅 할 수 있다. 인스펙션 하기를 원하는 소스코드에 브레이크포인트를 넣고, 플러그인을 오른쪽 클릭하고 Debug As  Eclipse Application를 선택한다.

실행 흐름이 프레이크 포인트로 표시된 구문에 도달 했을 때, 실행이 멈추고, 관련된 구문을 디버깅하는 것과 현재 데이타를 검사하는 것을 할 수 있다. 이 투토리얼은 이클립스 IDE를 사용하는 것과 기본적인 자바 개발에 익숙한 것을 가정한다.

2. 이클립스 SDK 다운로드

이클립스 플랫폼에 기능들을 추가 할 계획이라면 마지막 릴리즈 버전을 다운로드 해야 한다. 공식 릴리즈가 안정적 API가 있습니다, 따라서 플러그인과 features를 추가하기 위한 가장 좋은 기초가 됩니다.

 이클립스 IDE는 다른 멋을 지니고 제공됩니다. 어떤 이클립스 패키지에서 필요한 툴들을 설치 할수 있다면, 일반적으로 플러그인 개발을 위한 모든 필요 툴을 포함하는 이클립스 표준 배포판을 다운로드 하는 것이 쉽습니다. 더 만은 툴을 포함한다른 패키지들은 플러그인 개발에 필요하지 않습니다.

이클립스 IDE 다운로드 사이트를 열고 이클립스 Committers패키지를 위한 이클립스 IDE를 다운로드 한다.  .

이클립스는 또한 Eclipse installer 인스톨러를 제공한다. 여러가지 멋을 지닌 이클립스를 다운로드 하기를 원하면, 이 인스톨러는 유용하다.  필요로 하는 공간을 줄인 상용 플러그인을 위한 공유된 설치 풀을 사용 한다. 

3. 싱행 환경구성

3.1. 실행 환경 구성이 무엇인가?

런타임 이클립스는 플러그인의 세트의 선택에 기반한다. 그 플러그인들은 런타임 환경 구성을 통해서 선택된다. 이 런타임 환경 구성은 다음처럼 정의 될수 있다:

  • 플러그인 또는 features 등의 세트

  • 실행환경을 생성하고 업데이트 하는데 사용되는 제품(product)

실행 환경구성은 일번적인 런처를 실행하는 것을 사용하게 될 환경구성을 정의한다. 예를 들면, Java 가상머신 (VM), 플러그인 (classpath) 종속성(dependencies), 등등 에 인수들을 정의 한다. .

테스트를 위한 런타임 이클립스 IDE를 시작하기를 원하면, 워크스페이스 위치를 깨끗하게 하면 다음 실행시 워크스페이스를 선택하기위한 메시지가 나타나게 될 것입니다. 

실행 환경 구성을 다시 보거나 편집하기 위해서는 이클립스 메뉴로 부터 Run  Run Configurations…​ 를 선택하라.

여기에서 Main 탭에 Location에서, 런타임 이클립스가 생성되기 위한 환경 구성 파일이 있는 곳을 지정한다.

3.2. 인수 정의하기

실행환경은 인수(Arguments) 탭에 어프리케이션의 추가적 시작 인수를 추가하는 것을 허용한다. 기본적으로, 이클립스는 이미 몇몇 인수들을 포함한다. 예를 들면, 어플리케이션을 실행하는 아키텍처를 지정하기 위해 -os, -ws 그리고 -arch 같은 파라미터이다.

이클립스 어플리케이션에 시스템 프로퍼티를 전달하기 위해, -D 스위치를 사용하여 런처 파라미터를 추가 할 수 있다. 예를 들면, 만약 -Dmy.product.loglevel=INFO 인수를 추가 한다면, System.getProperties("my.product.loglevel")에 값으로 "INFO" 를 설정할 수있다. .

다음 테이블에 유효한 런처 인수들을 목록화 한다. 

테이블 1. 런처 파라미터 

파라미터 상세설명      
consoleLog 어플리케이션이 시작된 RCP 어플리케이션이 보여줄 이클립스 IDE 콘솔에서 보여질수 있는 표준 출력(System.out)이 기록된다.
nl 어플리케이션을 위해 사용될 로케일을 지정한다. 그 로케일은 언어별 설정을 정의한다. 즉, 사용된 언어과 수, 날짜, 통화 형식이다. 예를 들면 영어를 사용하기 위해  -nl en 로 어플리케이션을 시작하다.  변환된 언어를 테스트하는데 유용하다. 
console 어플리케이션의 상태를 체크 할 수 있는 OSGi 콘솔에 접근하는 것을 제공한다. 
noExit
어플리케이션이 충돌하더라도 열린 OSGi 콘솔을 유지한다.  시작하는 동안 어플리케이션이 충돌 하더라도 어플리케이션을 분석하기 위해 허용한다. 
clearPersistedState 이클립스 4 어플리케이션 모델의 충돌된 런타임 변경을 삭제한다..

3.3. 런처 환경 구성과 이클립스 제품

만약 이클립스 RCP 어플리케이션을 개발 하는 중이라면, 제품 환경 구성 파일에 기반해서 런처 환경 구성을 생성한다. 런처 환경 구성이 제품을 통해 어플리케이션을 시작할때 매번 업데이트 된다. 

어플리케이션을 다시 시작하기 위해 직접 생성된 실행 환경 구성을 사용 한다. 제품에서 변경된 경우에 환경 구성 파일이 고려되지 않는다.

존재하는 실행 환경 구성을 사용하는 것은 방해의 소스와 에러 분석을 하는 시간이 일반적이다. 마지막 환경 구성을 사용하는 것을 보장하기 위해, 제품 파일을 통해서 어플리케이션을 시작한다. 

 

3.4. 일반적인 런처 문제를 위한 체크리스트

이클립스 RCP 어플리케이션의 실행 환경 구성에서 에러들은 문제의 원인이 된다. 이 장은 RCP 어플리케이션을 시작과 관련된 일반적인 문제들이 기술된다. 어플리케이션을 시작하는 동안 직면한 문제의 경우에 참조로 사용 될 수 있다. 

가장 일반적인 문제는 제품에서 필요한 플러그인들이 빠져 있는 것이다. 만약 제품 환경 구성에 기반이 되는 feature들를 사용한다면 MANIFEST.MF파일에서 참조될 모든 플러그인을 권장할 필요가 있고 또한 feature들에 포함된다. 이 오류는 콘솔 뷰에 보고되고, 전형적으로 첫번째 오류 메시지중에 하나이고 스크롤을 올려서 볼 필요가 있다. 

다음 목록은 전형적인 이 메시지를 어떻게 할것인지 다음과 같이 나열되어 있다. (텍스트에 맞게 서식 변경).

org.osgi.framework.BundleException: Could not resolve module: com.vogella.tasks.services [9] Unresolved requirement: Require-Bundle: com.vogella.tasks.events; bundle-version="1.0.0"

빠져있는 플러그인을 식별하고 제품에(만약 제품이 플러그인을 기본으로 하면) 또는 feature들(만약 제품이 feature을 기본으로 하면)에 추가 한다. 

이클립스는 런터 환경 구성을 실행사기 전에 자동으로 빠진 의존관계를 체크 할 수 있다. 플러그인(Plug-ins) 탭에서 Validate Plug-ins 버튼을 누르거나 옵션을 시작하기 전에 자동으로 플러그인들을 검증하는 것을 선택하라. 어플리케이션이 실행 될 때 필요한 프러그인들을 모두 가지고 있는지 체크하게 될 것이다. 

제품 환경 구성 파일에 기반하여 실행 환경이 생성되고 업데이트 되기 때문에 실행 환경에서 의존관계 문제를 수정하는 것을 피해야 한다. 그래서 항상 파생된 정보를 변경하는 대신에 제품 파일이 정확하게 환경 구성하는 것을 권장한다. 그 환경 구성은 제품의 내보내기를 사용되고, 그런 이유로 제품 의존관계에서 오류는 시작 될 수 없는 내보내기로 만들어진 어플리케이션에서 생긴다. 

다음 테이블 목록은 잠재적인 문제와 해결 방법을 나열한다. 

테이블 2. 실행 환경 구성 

문제점 문제점 해결방법    
기동 중에 "Could not resolve module" 메시지를 출력하는 경우.

프로젝트 환경구성에 모든 필수 플러그인들이 포홤 되어 있는지를 체크하라. product 에 모든 필수 플러그인 또는 feature에 의존관계를 정의하였는지 확인하라. 문제를 해결하는 방법을 이술하기 위한 이 섹션의 시작하기를 보라. 

번들은 특정 버전의 Java 가상머신을 필요로 할지 모른다. 즉, 번들이 Java 1.6 을 필요로 할지 모르고, 그러므로 Java 1.5 가상머신에서는 로드되지 않을 것이다. 실행환경(Execution Environments) 섹션에 (실행환경에 개요(Overview) 탭에 MANIFEST.MF 파일에서 필요로 하는 자바 버전을 체크하라.

기동 중에 "java.lang.RuntimeException: No application id has been found." 메시지를 출력하는 경우. 기동 중에 "Could not resolve module" 메시지 오류를 본다. 대부분의 경우에 역시 플러그인이 의존관계를 빠뜨림으로써 발생된다.
오류 메시지는 없지만 이상하게 동작하는 경우. -consoleLog 파라미터를 포함한 환경 구성이 실행되는지 체크라라. 이 옵션은 이클립스 IDE의 콘솔 뷰에 이클립스 기반 어플리케이션으로 부터 에러를 보는 것을 허용한다. 
실행 환경 구성이 자주 필요한 플러그인을 빠뜨리는 경우. product 또는 feature가 모든 필요 의존관계를 포함하는지 확인하라. 
실행 환경에서 product 의존관계 탭에서 변경이 반영이 안된다.(즉, 새 플러그인이 추가 되었지만 실행 환경에서는 추가 되지 않는다.) 만약 product 정의 파일로 부터 직접 product를 시작한다면 product는 존재 하는 실행 환경을 업데이트 한다. 직접 실행 환경을 선택하면, 업데이트 되지 않을 것이다.
이클립스 4 어플리케이션에서 어플리케이션 모델 변경이 반영이 안된다.  어플리케이션내의 시작 시 재저장된 델타 파일에서 이클립스 4 persists 사용자를 변경한다.  개발 하는 동안 런타임 모델에 모델 변경이 정확하게 적용되지 않는 상황으로 이어 질 수있다. 즉, 새 메뉴 엔트리를 정의 하고 그 엔트리가 어플리케이션에 표시되지 않는다. 실행환경의 Main 탭에서 Clear 플래그를 설정하거나 product 환경 구성 파일 또는 실행 환경 구성 파일에 clearPersistedState 파라미터를 추가한다.
서비스, 즉, 키 바인딩 또는 선택 서비스가 이클립스 4 어플리케이션에서 동작하지 않는다. 4.3 이전 릴리즈 된 이클립스에서 모든 파트에서 SWT 컨트롤에 포커스를 놓기 위해서 @Focus 메서드를 구현할 필요가 있다. 이 에러는 이클립스 4.3 또는 더 높은 릴리즈 버전에서는 더 이상 발생하지 않는다. 
이클립스 어플리케이션에서 메뉴 엔트리가 비활성화 되어 있다. 모델 에드온(부가기능)으로 등록된 org.eclipse.e4.ui.internal.workbench.addons 패키지에서 HandlerProcessingAddon 클래스를 확인한다. 번들 심볼릭 명이 org.eclipse.e4.ui.workbench 이다.
"org.eclipse.ant.core.antRunner" 어플리케이션을 레지스터리에서 찾을 수 없거나 어플리케이션을 레지스터리에서 찾을수 없을때 product 환경 구성 파일에서 New…​ 버튼을 눌렀는지 시작 할 어플리케이션으로 E4Application 을 선택했는지 확인하라. org.eclipse.core.runtime.products 확장(extension)의 상세에서 확장(Extentions) 탭에 plugin.xml 파일에 현재 설정을 체크 하라.
기동 또는 검증할 때 " Unresolved requirement: Require-Capability: osgi.extender; filter:='(&(osgi.extender=osgi.component)(version>=1.2)(!(version>=2.0)))' " 메시지 org.apache.felix.src을 product에 의존관계에 추가하라. 검증(Validate ) 버튼으로 product 의 일관성을 체크하라
기동할 때 "Event Admin service is not available, unable to publish event org.osgi.service.event.Event" 메시지 org.eclipse.equinox.even를 product에 의존관계에 추가하라. 검증(Validate ) 버튼으로 product 의 일관성을 체크하라.

4. 연습: 첫번째 플러그인을 생성하고 사용하기

이 연습에서, 마법사를 사용하여 IDE에 확장(extension)을 생성한다. 

4.1. 새 플러그인 프로젝트를 생성한다.

새 플러그인을 생성하기 위해 File > New > Other > Plug-in Development > Plug-in Project 를 선택한다.

프로젝트 이름에 com.vogella.ide.first 를 입력한다..

Next 버튼을 누른다. 다음 페이지에서 아래 스크린샷에 입력된 값을 등록한다. Next 버튼을 누른다.

다음 페이지에서 템플릿 체크박스 중에 하나를 사용하여 플러그인을 생성한다. 4.x API 템플릿을 사용하는 컨트리뷰션 뷰(View contribution using 4.x API )를 선택한다. .

그 후에, Next 버튼 또는 Finish 버튼을 누른다.

만약 Next, 버튼을 눌렀다면 다음과 같이 입력하고, 반면 Finish를 눌렀다면 다음 페이지는 보여지지 않는다.

이 마법사는 선택된 템플릿을 사용하여 새 플러그인을 바로 생성한다. 생성된 파일들을 보기 위해서 프로젝트 탐색기(Project Explorer)를 사용한다.

4.2. 새 플러그인으로 런타임 이클립스 시작하기 

이제 생성된 플러그인으로 런타임 이클립스를 시작해 보자.

현재 IDE의 그 새 플러그인과 모든 대상으로 부터 모든 플러그인으로 런타임 이클립스를 시작한다. 그러기 해 , 그 플러그인 프로젝트에서 오른쪽 마우스를 클릭해서 Run As  Eclipse Application.을 선택한다.

새 예제 뷰를 포함하는 런타임 이클립스를 시작한다..

4.3. 결과 검증

런타임 IDE에 메쥬에서 Window > Show View > Others…​ 를 통해서 샘플 뷰(Sample View) 뷰를 열수 있어야 한다.

5. 사용자 정의 IDE를 정의하기 위해 features 와 products 사용하기

이 예제에서 런타임 이클립스에서 선택된 플러그인들을 환경 구성하고 선택하기 위한 features 와 products를 생성하는 방법을 배우게 된다. 

5.1. 생성하기

File > New > Other…​ > Plug-in Development > Feature Project를 사용해서 com.vogella.ide.feature로 불리는 새 feature 프로젝트를 생성한다. 

마법사의 두번째 페이지에서, feature에 추가하기를 원하는 플러그인을 선택한다. 

그 뒤에 Finish 버튼을 누른다.

5.2. feature 내용 검증하기

feature.xml 파일을 열고 이 에디터에서 포함된 플러그인들(Included Plug-ins) 탭을 선택하고 작업 공간에서 원하는 플러그인들이 이 feature에 포함 되어 있는 것을 확인해라.

그 feature에 플러그인들을 포함시키기 위해 포함된 플러그인(Included Plug-ins) 탭에 플러그인들 추가 되었는지를 확인한다. 이 예시에서 의존관계(Dependencies) 탭을 사용하는 것은 잘못이다. 

5.3. 제품 환경 구성 파일을 포함할 프로젝트 생성하기

File > New > Other…​ > General  Project. 를 사용해서 com.vogella.ide.product 이름으로 새 프로젝트를 생성한다.

프로젝트 이름을 생성한다.

Finish.버튼을 누른다.

5.4. 환경 구성 파일 생성하기

위에서 생성한 프로젝트에서 오른쪽 마우스를 클릭하고 File > New > Other…​ > Plug-in Development > Product Configuration를 선택한다.

com.vogella.ide.productproject 폴더 내부에 ide.product 이름의 product 환경 구성 파일을 생성한다. .

Finish 버튼을 누른다..

새 product 파일이 생성되고 에디터에서 열리게 된다.

5.5. product 환경 구성하기

새 파일을 위한 product 에디터가 열려 있는지 확인하라. product 에디터의 개요(Overview) New…​ 버튼을 누른다.

플러그인 정의 하기(Defining Plug-in)에 com.vogella.ide.first 을 입력한다. 어플리케이션(application)에 org.eclipse.ui.ide.workbench를 사용한다. 다음 스크린샷과 유사하게 이름과 ID를 넣는다.

5.6. 버전 번호를 입력한다. 

product에 버전 1.0.0을 입력한다. 

5.7. features를 포함하는 것으로 입력한다.

product는 features에 기반으로 해야 한다. 이를 위해 개요(Overview) 탭에서 features 옵션을 선택한다.

내용(Contents) 탭을 선택하고 Add…​ 버튼을 통해 다음 features를 추가한다..

  • com.vogella.ide.feature

  • org.eclipse.platform

다음으로 Add Required 버튼을 누른다..

Ctrl + S를 눌러 ide.product 파일을 저장한다.

만약 product에 리스트된 features 중에 하나를 추가 할 수 없다면, product 환경 구성이 features에 기반하는 것을 보장한다.

5.8. 런타임 이클립스를 시작으로 설정을 검증하기

product 파일을 선택하고 Run 버튼을 통해서 이클립스 IDE를 시작한다. .

결과로 런타임 이클립스 IDE가 시작되여야 한다. product에 추가 되여야 하는 그 feature들만을 포함해야 한다. 예를 들면, 현재는 새 Java 프로젝트를 생성할 수 없어야 한다.

5.9. 개발중인 IDE에 Java 툴을 추가 하기

대상 플랫폼에 org.eclipse.jdt.feature.group 을 추가한다. 대상 플랫폼은 다음 목록에 유사하게 해야 한다. 

역자주) 해당 투토리얼에서 Target Profile 파일을 생성 하지 않아서 내용(Contents) 탭에서 Add… 버튼으로 추가 후 테스트 했으며, 별도의 Tartget Profile XML 파일을 생성 후 아래 내용을 입력하거나  File > New > Other…​ > Plug-in Development >  Target Definition으로  생성 후 편집할 수 있다.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde version="3.8"?>
<target name="target-platform">
  <locations>
    <location includeAllPlatforms="false" includeConfigurePhase="true" includeMode="planner" includeSource="true" type="InstallableUnit">
      <repository location="http://download.eclipse.org/releases/latest"/>
      <unit id="org.eclipse.equinox.sdk.feature.group" version="0.0.0"/>
      <unit id="org.eclipse.platform.sdk" version="0.0.0"/>
      <unit id="org.eclipse.jdt.feature.group" version="0.0.0"/>
    </location>
  </locations>
</target>

그 다음에, product에 org.eclipse.jdt feature을 추가하고, product 런타임 IDE를 시작한다. 이제 Java 툴이 설치되었다. 예로들면, 새 마법사를 통해 Java 프로젝트를 생성 할수 있는지를 체크 하라.

6. 연습: 3.x 기반 어플리케이션에 e4 part descriptors 추가 하기

이 연습으로 이클립스 3.x RCP 어플리케이션에 모델 기반 파트 컨트리뷰션을 추가 할 수 있다..

6.1. 파트 디스크립터(part descriptor) 추가하기

com.vogella.plugin.partdescriptor 이름으로 간단한 플러그인을 생성한다.

manifest 파일에 다음 의존 관계를 추가한다..

  • org.eclipse.core.runtime,

  • org.eclipse.jface,

  • org.eclipse.e4.ui.model.workbench,

  • org.eclipse.e4.ui.di

다음 클래스를 생성한다.

package com.vogella.ide.ui; 

import javax.annotation.PostConstruct; 
import org.eclipse.e4.ui.di.Focus; 
import org.eclipse.jface.viewers.ArrayContentProvider; 
import org.eclipse.jface.viewers.LabelProvider; 
import org.eclipse.jface.viewers.TableViewer; 
import org.eclipse.swt.SWT; 
import org.eclipse.swt.widgets.Composite; 

public class PartEclipse4x { 
  private TableViewer viewer; 
  
  @PostConstruct 
  public void createPartControl(Composite parent) { 
    viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); 
    viewer.setContentProvider(ArrayContentProvider.getInstance()); 
    viewer.setLabelProvider(new LabelProvider()); 
    viewer.setInput(new String[] {"One", "Two", "Three"}); 
  } 
  
  @Focus public void setFocus() { 
    viewer.getControl().setFocus(); 
  }
}

File > New > Other…​ > Eclipse 4 > Model > New Model Fragment를 사용하여 이 플러그인에 새 모델 fragment 를 추가한다. 

6.2. 검증

이클립스 IDE 인스턴스를 시작하고 빠른 접근 박스(단축키 CTRL+3)를 사용하여 파트를 열수 있는지를 검증한다. .

Window > Show View 메뉴 입장을 통해 이 파트를 열수 있다. 

7. 3.x API로 부터 IEclipseContext에 접근하기

이클립스 IDE IDE에 대해서 중앙 정보를 저장하기 위해 IEclipseContext 데이타 스트럭처를 사용한다. 이클립스 3.x API 를 통해서 또한 이 정보에 접근 할수 있다. .

예를 들면, 이클립스 3.x API 뷰로 부터 내용에 접근하게 하기 위해, 다음 소스 일부를 사용한다. 

// part의 내용 취득
IEclipseContext partContext = getViewSite().getService(IEclipseContext.class); 

// 그 키를 기반으로 내용에서 직접 값에 접근
EModelService service = getViewSite().getService(EModelService.class);

이 소스 일부는 이클립스 3.x API 핸들러를 통한 접근을 보여준다.

// 다음 핸들러가 있다는 것을 가정한다.  
// 활성화 윈도우로 부터 내용 취득
IEclipseContext windowCtx = HandlerUtil.getActiveWorkbenchWindow(event).getService(IEclipseContext.class); 

// 활성화 된 영역으로 부터 내용 취득
IEclipseContext ctx = HandlerUtil.getActivePart(event).getSite().getService(IEclipseContext.class);

8. 이클립스 IDE 또는 3.x RCP 어플리케이션에 모델 엘리먼트 기여하기

8.1. org.eclipse.ui.views 확장포인트(extension point)를 사용해서 Adding 이클립스 3.x 어플리케이션에 이클립스 4.x 파트 추가 하기.

org.eclipse.ui.views 확장점(extension point)를 확장 extension (탭, 테그)에서 e4 파트를 사용 할 수 있다. 이를 보관하기 위해 extension의 내용 메누에 e4view 엔트리를 사용한다. 그 결과 객체는 의존관계 주입(dependency injection)을 통해서 생성된다. 

기존 툴바 뷰 와 뷰 확장점의 경우에 컨트리뷰션이 동작하지 않는다. 예로 e4view에 툴바를 추가하기 위해, 구현체를 삽입할 MToolbar를 얻고 소스코드에 엔트리를 구성한다.

8.2. e4 메뉴 엔트리들 추가하기

메뉴(Menus), 핸들러(handlers) 그리고 커멘드(commands) 모델 fragments을 통해서 이클립스 어플리케이션에 기여(제공)될 수 있다.

역자주) 어플리케이션에 기여한다는 의미는 이클립스 어플리케이션으로 별도의 기능으로 삽입되어 기능등이 개선 또는 추가 될 수 있는 것을 의미 한다. 즉, 기존 이클립스에 일부로 개발되어 포함 제공 될 수 있다는 의미이다

기여하기를 원하는 엘리먼트의 ID를 지정하기 위해 e4 tools로 부터 모델 spy를 사용할 수있다.

일치하는 ID로 상응하는 어플리케이션 모델 엘리먼트에 기여하는 모델 fragment를 생성 할 수 있다. 다음 스크린샷은 이클립스 IDE의 menuContributions feature에 기여(제공)하는 방법을 보여준다. 

메뉴를 기여(제공)할 수 있는 MenuContribution 항목을 추가 한 후에 . 그 부모-ID는 메뉴가 기여(제공)될 메뉴의 ID 여야 한다.

모델 fragment는 org.eclipse.e4.workbench.model 확장점(extension poin)을 통해서 다음 목록에서 표시되는 것처럼 plugin.xml 파일에 등록되어 있어야 한다.

<?xml version="1.0" encoding="utf-8"?>
<plugin>
  <extension id="id1" point="org.eclipse.e4.workbench.model">
    <fragment apply="notexists" uri="fragment.e4xmi"> </fragment>
  </extension>
</plugin>

8.3. 오류 분석

문제가 발생했을 경우에, plugin.xml의 소스 코드를 확인하고 모델 fragment가 포함 되었는지를 검증하라. 참조된 모델 fragment의 이름을 검증하고, 모든 정의된 ID가 실행중인 환경 구성에서 사용가능한지를 확인하라.

8.4. 어플리케이션 윈도우에 e4 툴바 엔트리 추가하기

툴바 컨트리뷰션을 기여할 메뉴와 유사하다. 

이 접근은 현재 뷰 툴바를 위해서 동작하지 않는다. 

8.5. 이클립스 IDE 에 파트 디스크립터를 지원하기

이클립스 IDE는 fragments와 프로세서를 통해 파트 디스크립터 모델의 기여를 지원한다. 

만약 파트 디스크립터를 기여하기 위해 the org.eclipse.e4.legacy.ide.application ID를 사용한다면, Window > Show View > Other…​ 다이얼로그 또는 바로가기를 통해서 뷰가 오픈 될 수 있다.  파트 디스크립터같은 뷰 테그를 필요로 한다. 

4.5 또는 더 높은 런타임에서 실행되는 이클립스 3.x API RCP 어플리케이션 같은 접근을 사용할 수 있다. 

8.6. 모델 snippet을 통해서 이클립스 IDE에 퍼스펙티브 추가하기

모델 fragment 또는 processor는 snippet을 통해 포스펙티브를 추가 기 위해 이클립스 4.x IDE에 퍼스펙티브를 기여 할 수 있다. 이 확장 엘리먼트 id는 xpath:/ 로 일반화 둘수 있고 feature 명이 snippets 이다.

이 접근은 이클립스 4.x 런타임에 실행하는 RCP 어플리케이션 기반 이클립스 3.x API에 퍼스펙티브를 기여하는데 사용 될 수 있다. .

9. 연습: 이클립스 IDE에 새 퍼스펙티브 추가하기

I이 연습에서, 이클립스 IDE에 새 퍼스펙티브를 기여(제공)한다..

9.1. 모델 엘리먼트를 생성하기

com.vogella.contribute.parts 플러그인에서, 다음 모델 fragment를 생성한다..

퍼스펙티브에 적어도 하나의 파트를 추가한다. 

9.2. 검증

이클립스 IDE를 시작하고 퍼스펙티브 스위쳐 다이얼로그를 통해서 새 퍼스펙티브를 열수 있는지를 검증한다. 

10. 추가 연습: 확장점(extension points) 통해서 POJO 뷰 추가 하기 

이 연습에서, org.eclipse.ui.views 확장점(extension point)의 e4view 엘리먼트를 사용하기 대문에 별도의 플러그인을 통해 e4 기반 뷰 확장을 생성한다.

이 확장(extension) 이클립스 IDE에 기여(제공)될 수 있다. (또는 다른 이클립스  3.x API 기반 RCP 어플리케이션).

10.1. 플러그인 생성하기

com.vogella.ide.e4view 이름의 플러그인을 생성한다.  File > New > Other…​ > Plug-in Project을 사용한다.

com.vogella.ide.e4view 프로젝트를 호출하고 다음 스크린샷에 유사하게 옵션을 선택한다. 

10.2. 플러그인 의존관계 조정하기

새 플러그인의 manifest 파일에 의존관계로 org.eclipse.e4.ui.di 를 추가 하라.

10.3. e4 뷰 생성하기

 다음 생성된 SampleE4View를 조정하라.

package com.vogella.ide.e4view.views; 

import javax.annotation.PostConstruct; 
import javax.inject.Inject; 
import org.eclipse.e4.ui.di.Focus; 
import org.eclipse.jface.action.Action; 
import org.eclipse.jface.action.IMenuListener; 
import org.eclipse.jface.action.IMenuManager; 
import org.eclipse.jface.action.MenuManager; 
import org.eclipse.jface.action.Separator; 
import org.eclipse.jface.dialogs.MessageDialog; 
import org.eclipse.jface.viewers.ArrayContentProvider; 
import org.eclipse.jface.viewers.DoubleClickEvent; 
import org.eclipse.jface.viewers.IDoubleClickListener; 
import org.eclipse.jface.viewers.IStructuredSelection; 
import org.eclipse.jface.viewers.ITableLabelProvider; 
import org.eclipse.jface.viewers.LabelProvider; 
import org.eclipse.jface.viewers.TableViewer; 
import org.eclipse.swt.SWT; 
import org.eclipse.swt.graphics.Image; 
import org.eclipse.swt.widgets.Composite; 
import org.eclipse.swt.widgets.Menu; 
import org.eclipse.ui.ISharedImages; 
import org.eclipse.ui.IWorkbench; 
import org.eclipse.ui.IWorkbenchActionConstants; 

public class SampleE4View { 
	/**
     * 확장에 의해 지정된 뷰의 ID. 
     */ 
    public static final String ID = "com.vogella.ide.e4view.views.SampleE4View"; 
    
    @Inject IWorkbench workbench; 
    
    private TableViewer viewer; 
    
    private Action action1; 
    
    private Action action2; 
    
    private Action doubleClickAction; 
    
    class ViewLabelProvider extends LabelProvider implements ITableLabelProvider { 
    	
        @Override 
        public String getColumnText(Object obj, int index) { 
        	return getText(obj); 
        } 
        
        @Override 
        public Image getColumnImage(Object obj, int index) { 
        	return getImage(obj); 
        } 
        
        @Override 
        public Image getImage(Object obj) { 
        	return workbench.getSharedImages().getImage(ISharedImages.IMG_OBJ_ELEMENT); 
        } 
    } 
    
    @PostConstruct 
    public void createPartControl(Composite parent) { 
    	viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); 
        viewer.setContentProvider(ArrayContentProvider.getInstance()); 
        viewer.setInput(new String[] { "One", "Two", "Three" }); 
        viewer.setLabelProvider(new ViewLabelProvider()); 
        
        // 뷰의 컨트롤을 위한 헬프 컨텍스트 ID를 생성한다. 
        workbench.getHelpSystem().setHelp(viewer.getControl(), "com.vogella.ide.e4view.viewer"); 
        makeActions(); 
        hookContextMenu(); 
        hookDoubleClickAction(); 
    } 
    
    private void hookContextMenu() { 
    	MenuManager menuMgr = new MenuManager("#PopupMenu"); 
        menuMgr.setRemoveAllWhenShown(true); 
        menuMgr.addMenuListener(new IMenuListener() { 
        	
            @Override 
            public void menuAboutToShow(IMenuManager manager) { 
            	SampleE4View.this.fillContextMenu(manager); 
            }
            
       	}); 
        Menu menu = menuMgr.createContextMenu(viewer.getControl()); 
        viewer.getControl().setMenu(menu); 
    } 
    
    private void fillContextMenu(IMenuManager manager) { 
    	manager.add(action1); 
        manager.add(action2); 
        // 다른 플러그인이 여기에 액션을 기여(제공) 할 수 있다.
        // (역자주, 내부에 생성된 action1, action2 이외에 기존 다른 플러그인 액션을 사용 할 수 있다)
        manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); 
    } 
    
    private void makeActions() { 
    	action1 = new Action() { 
        	@Override 
            public void run() { 
            	showMessage("Action 1 executed"); 
            } 
         }; 
         
         action1.setText("Action 1"); 
         action1.setToolTipText("Action 1 tooltip"); 
         action1.setImageDescriptor(workbench.getSharedImages().getImageDescriptor(ISharedImages.IMG_OBJS_INFO_TSK)); 
         
         action2 = new Action() { 
         	@Override 
            public void run() { 
            	showMessage("Action 2 executed"); 
            } 
         }; 
         
         action2.setText("Action 2"); 
         action2.setToolTipText("Action 2 tooltip"); 
         action2.setImageDescriptor(workbench.getSharedImages().getImageDescriptor(ISharedImages.IMG_OBJS_INFO_TSK)); 
         
         doubleClickAction = new Action() { 
         	@Override 
            public void run() { 
            	IStructuredSelection selection = viewer.getStructuredSelection(); 
                Object obj = selection.getFirstElement(); 
                showMessage("Double-click detected on " + obj.toString()); 
            } 
         }; 
         
    } 
    
    private void hookDoubleClickAction() { 
    	viewer.addDoubleClickListener(new IDoubleClickListener() { 
        	@Override 
            public void doubleClick(DoubleClickEvent event) { 
            	doubleClickAction.run(); 
            } 
        });
    } 
    
    private void showMessage(String message) { 
    	MessageDialog.openInformation(viewer.getControl().getShell(), "e4view View", message); 
    } 
    
    @Focus 
    public void setFocus() { 
    	viewer.getControl().setFocus(); 
    } 
}

만약 바로 플러그인을 시작하고 뷰를 연다면, com.vogella.ide.e4view.views.SampleE4View를 org.eclipse.ui.IViewPart로 캐스팅 할 수 없다는 에러 메시지를 받을 것이다. 정확하게 조정된 클래스를 사용하기 위해서 다음단계를 필요로 한다. 

10.4. 확장점(extension point)에서 엔트리 조정하기

org.eclipse.ui.views 확장점(extension point)을 위해 e4view 속성으로 뷰 속성을 교체한다..

plugin.xml 에디터의 확장(Extensions)탭을 사용하라

이미지 문구 번역) view 엔트리를 삭제 하고 새 엘리먼트로 e4view를 추가 한다.

plugin.xml 파일에서 org.eclipse.ui.views 확장을 위한 마지막 관련 소스는 다음과 유사하게 보여야 한다.:

<?xml version="1.0" encoding="UTF-8"?> 
<?eclipse version="3.4"?> 
<plugin> 
   	<extension point="org.eclipse.ui.views"> 
       	<category name="Sample Category" id="com.vogella.ide.e4view"> 
        </category> 
        <e4view category="com.vogella.ide.e4view" 
           		class="com.vogella.ide.e4view.views.SampleE4View" 
                icon="icons/sample.png" 
                id="com.vogella.ide.e4view.e4view1" 
                name="e4view View" restorable="true"> 
        </e4view> 
	</extension> 
        
    <!-- MORE ENTRIES, LEFT OUT FOR BREVITY --> 
</plugin>

10.5. Validate the usage of the e4 뷰의 사용 검증하기

이클립 IDE에서 플러그인을 시작하라 그리고Windows > Show View 메뉴 엔트리를 통해서 뷰를 열수 있는지를 검증하라. 이 뮤는 다음과 유사하게 보일 것이가 항목들상에 컨텍스트 메뉴가 제공되어야 한다. 

모델 지속성 데이타를 보는 것을 피하기 위해, 런타임 환경 구성에 -clearPersistedState 플래그 설정해서 추가한다.

10.6. 퍼스펙티브 확장에 뷰를 추가 하기

퍼스펙트브에 뷰를 추가 하기 위해, 다음 스크린샷에서 표시되는 것처럼 존재하는 org.eclipse.ui.perspectiveExtensions 엔트리를 조정한다. 

이미지 문구 번역) 새 뷰 ID를 사용한다. (com.vogella.ide.e4view.e4view1) 

10.7. 추가 연습: e4 뷰에 툴바 추가하기

현재 확장점(extension point) 처리는 일반 뷰 확장을 제외하고 e4view 확장이 아니다. e4view 에 툴바를 추가 하기 위해, 스스로 관련 정보를 처리 해야 한다. 예를 들면, 주입된 EModelService를 얻을 수 있고,  확장 레지스터 또는 모델 snippet에 기반으로 툴바를 추가한다. 시연의 목적으로 다음처럼 하나의 툴바 엔트리를 간단하게 생성한다.

@PostConstruct 
public void createPartControl(Composite parent, MPart part, EModelService modelService) { 
	viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); 
    viewer.setUseHashlookup(true); 
    
    // 데이타 모델의 빠른 해시 참조를 사용한다. 
    viewer.setContentProvider(ArrayContentProvider.getInstance()); 
    viewer.setInput(new String[] { "One", "Two", "Three" }); 
    viewer.setLabelProvider(new ViewLabelProvider()); 
    
    // 뷰어의 컨트를을 위한 도움말 컨텍스트 ID를 생성한다. 
    workbench.getHelpSystem().setHelp(viewer.getControl(), "com.vogella.ide.e4view.viewer"); 
    makeActions(); 
    hookContextMenu(); 
    hookDoubleClickAction(); 
    
    MToolBar toolbar = modelService.createModelElement(MToolBar.class); 
    MHandledToolItem toolitem = modelService.createModelElement(MHandledToolItem.class); 
    toolitem.setLabel("View Tool Item"); 
    
    // TODO 툴바 아이템을 위한 관련 아이콘과 커멘트를 설정한다. 
    toolbar.getChildren().add(toolitem); part.setToolbar(toolbar); 
}

11. 연습: 이클립 IDE에 e4 메뉴 추가하기

이 연습에서 com.vogella.tasks.ui 플러그인을 통해 IDE에 메뉴와 툴바 컨트리뷰션을 추가 하게 될 것이다..

11.1. 플러그인 의존관계 추가하기

com.vogella.tasks.ui의 manifest 파일에 다음 의존관계가 있는지 확인하라..

  • org.eclipse.e4.core.di,

  • org.eclipse.jface

11.2. 핸들러 클래스 생성하기

생성된 핸들러 클래스에 기반해서 다음 클래스를 생성한다.

package com.vogella.tasks.ui.handlers; 

import org.eclipse.e4.core.di.annotations.Execute; 
import org.eclipse.jface.dialogs.MessageDialog; 
import org.eclipse.swt.widgets.Shell; 

public class TestHandler { 
	
    @Execute 
    public void execute(Shell shell) { 
    	MessageDialog.openInformation(shell, "First", "Hello, e4 API world"); 
    } 
}

11.3. 컨트리뷰션을 위한 모델 fragment를 생성한다.

플러그인 프로넥트의 컨텍스트 메뉴로 부터 New > Other…​ > Eclipse 4  Model > New Model Fragment 를 통해 새 모델 fragment를 생성한다.

이 파일 명은 commands.e4xmi을 입력한다.

Finish 버튼을 누른다..

새 fragment가 plugin.xml에 확장으로 정확하게 등록되었다는 것을 확인 하라.

11.4. 커멘트 컨트리뷰션을 정의하라

xpath:/ 엘리먼트 ID를 사용하여 최상위 레벨 엘리먼트의 커멘트에 커멘트들 기여(제공)하기 위한 모델 fragment 를 생성한다. 

새 컨트리뷰션을 정의 학 ㅣ위해 다음 스크린샷을 사용한다.

persistState 플래그를 false로 설정하는 것을 확인하라. 만약 플러그인이 설치 되지 않았다면 그 엘리먼트가 가능하지 않다.

11.5. 핸들러 컨트리뷰션 정의하기

정의된 커멘트로 핸들러를 추가 하고, 이행으로 TestHandler를 사용한다. 

persistState 플래그를 false로 설정하는 것을 확인하라. 만약 플러그인이 설치 되지 않았다면 그 엘리먼트가 가능하지 않다.

11.6. menuContributions 컨트리뷰션을 정의하기

persistState 플래그를 false로 설정하는 것을 확인하라. 만약 플러그인이 설치 되지 않았다면 그 엘리먼트가 가능하지 않다.

메뉴에 적어도 하나의 메뉴를 추가하고 반면 표시되지는 않을 것이다. 

11.7. 메뉴의 존재 검증하기

이클립스 IDE의 새 인스턴스를 시작하고 메뉴가 보이고 동작하는지를 검증한다. 만약 보이지 않는다면, 잠재적 이슈를 위한 모델 스파이를 통해서 체크하라 

12. 연습: 이클립스 IDE에 트림바(trimbar )를 추가 하기

12.1. trimbar 컨트리뷰션 추가하기

또한 같은 커멘트를 위한 trimbar 컨트리뷰션을 추가 할 수 있다.

persistState 플래그를 false로 설정하는 것을 확인하라. 만약 플러그인이 설치 되지 않았다면 그 엘리먼트가 가능하지 않다.

trimbar를 툴바에 추가하기

컨트리뷰션에 처리된 툴 항목을 추가한다.

12.2. 툴바 컨트리뷰션의 존재를 검증하기 

이클립스 IDE의 새 인스턴스를 시작하고, 새 툴바가 가능한지를 검증한다. 만약 윈도우에 보이지 않는다면, 잠재적이슈를 위해서 모델 spy를 통해서 체크한다.

13. 추가 연습: 툴바 엔트리에 아이콘 추가하기

아이콘 폴더를 생성한다.  새 프로젝트에 아이콘을 저장하기 위해 플러그인 이미지 브라우져(Plug-in Image Browser)를 사용한다.

툴바 엔트리에 이 아이콘을 할당한다. 

14. 리소스와 마커

이클립스는 IResource로 프로젝트, 파일, 폴더, 패키지 같은 리소스를 나타낸다. 

마커(Marker)는 추가적으로 리소스를 위한 정보를 나타낸다. 즉, 에러 마커이다. 이 정보는 리소스(파일) 내 저장이 아니라 워크스페이스 메타데이타에 추가 정보로 저장된다. 

모든 마커는 속성을 가질 수 있다. (key / value 조합). 마커는 표준 뷰에서 표시 될 수 있다. 즉, 테스크(Task), 북마크(Bookmark) 또는 문제점 (Problems) 뷰이다. 그 뷰에서 표시되게 되기 위해, 사전에 정의된 속성을 사용해야 한다.

15. 연습: 리소스를 위한 마커를 생성하기

e4 핸들러 템플릿에 기반한 com.vogella.plugin.markers 플러그인 프로젝트를 생성한다.

이 템플릿은 선택된 리소스 위한 IMarker를 생성하기 위해 사용되어야 하는 AddMarkerHandler 핸들러를 생성하게 될 것이다.

다음 의존 관례를 추가 한다.:

  • org.eclipse.jface

  • org.eclipse.e4.ui.services

  • org.eclipse.e4.core.di.annotations

  • org.eclipse.core.resources

  • org.eclipse.core.runtime

  • org.eclipse.e4.core.services

다음에 AddMarkerHandler 코르를 변경한다.:

package de.vogella.plugin.markers.handler; 

import javax.inject.Named; 
import org.eclipse.core.resources.IMarker; 
import org.eclipse.core.resources.IResource; 
import org.eclipse.e4.core.di.annotations.Execute; 
import org.eclipse.e4.core.services.adapter.Adapter; 
import org.eclipse.e4.ui.services.IServiceConstants; 
import org.eclipse.jface.viewers.IStructuredSelection; 

public class AddMarkerHandler { 
	
    @Execute 
    public void execute(@Named(IServiceConstants.ACTIVE_SELECTION) IStructuredSelection selection, Adapter adapter) { 
    	if (selection == null || selection.isEmpty()) { 
        	return; 
        } 
        
        Object firstElement = selection.getFirstElement(); 
        IResource resource = adapter.adapt(firstElement, IResource.class); 
        
        if (resource != null) { 
        	writeMarkers(resource); 
        } 
	} 
   
	private void writeMarkers(IResource resource) { 
    	try { 
        	IMarker marker = resource.createMarker(IMarker.TASK); 
            marker.setAttribute(IMarker.MESSAGE, "This is a task"); 
            marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH); 
        } catch (Exception e) { 
        	e.printStackTrace(); 
        } 
    }
}

fragment.e4xmi 파일에 커멘트의 명이 "Hello World" 에서 "Add Task Marker"로 변경되어야 한다.

만약 실행하고 Java 프로젝트나 다른 IResource를 선택하면 메뉴 엔트리를 클릭한다면 테스트 뷰(Tasks View) 에서 마커를 생성할 수 있다. 

16. 어댑터(Adaptors)

어댑터는 존재하는 뷰를 조정해야 하는 것 없이 이 뷰에서 객체에 대한 정보를 표시하는 것을 돕는다. 

어댑터는 몇몇 장소에서 사용된다. 예를 들면, 아웃라인 뷰에서 데이타가 표시하기 위해 사용 될 수 있다. 예제로 FAQ 언어 에디터를 위한 아웃라인 뷰를 어떻게 생성하는가 를 보라.

17. 연습- 프로퍼티 뷰를 위한 어댑터(Adaptors)

프로퍼티 뷰에 데이타를 보여주기 위해 간단하게 어댑터를 사용하게 될 것이다. 

de.vogella.plugin.adapter 이름의 새 플러그인 프로젝트를 생성한다..

MANIFEST.MF 파일의 의존관계(dependencies) 탭에 다음 의존 관계를 추가한다.:

  • org.eclipse.core.runtime

  • org.eclipse.e4.ui.model.workbench

  • org.eclipse.e4.ui.services

  • org.eclipse.e4.ui.workbench

  • org.eclipse.ui

  • org.eclipse.ui.views

데이타 모델로 다음 Todo 클래스를 생성한다.

package de.vogella.plugin.adapter.model; 

public class Todo { 
	
    private String summary; 
    private String description; 
    private boolean done; 
    
    public String getSummary() { 
    	return summary; 
    } 
    
    public void setSummary(String summary) { 
    	this.summary = summary; 
    } 
    
    public String getDescription() { 
    	return description; 
    } 
    
    public void setDescription(String description) { 
    	this.description = description; 
    } 
    
    public boolean isDone() { 
    	return done; 
    } 
    
    public void setDone(boolean done) { 
    	this.done = done; 
    } 
}

모델 fragment (fragment.e4xmi)를 생성하고 샘플뷰로 불리는 PartDescriptor를 추가한다.

PartDescriptor 구현체를 위한 파트로서 SampleView.java를 생성한다.

package de.vogella.plugin.adapter.views; 

import java.util.ArrayList; 
import java.util.List; 
import javax.annotation.PostConstruct; 
import org.eclipse.e4.ui.workbench.modeling.ESelectionService; 
import org.eclipse.jface.viewers.ArrayContentProvider; 
import org.eclipse.jface.viewers.ISelectionChangedListener; 
import org.eclipse.jface.viewers.ITableLabelProvider; 
import org.eclipse.jface.viewers.LabelProvider; 
import org.eclipse.jface.viewers.SelectionChangedEvent; 
import org.eclipse.jface.viewers.TableViewer; 
import org.eclipse.swt.SWT; 
import org.eclipse.swt.graphics.Image; 
import org.eclipse.swt.widgets.Composite; 
import org.eclipse.ui.ISharedImages; 
import org.eclipse.ui.PlatformUI; 
import de.vogella.plugin.adapter.model.Todo; 

public class SampleView { 
	
    private TableViewer viewer; 
    
    class ViewLabelProvider extends LabelProvider implements ITableLabelProvider { 
    	
        public String getColumnText(Object obj, int index) { 
        	Todo todo = (Todo) obj; 
            return todo.getSummary(); 
        } 
        
        public Image getColumnImage(Object obj, int index) { 
        	return getImage(obj); 
        } 
        
        public Image getImage(Object obj) { 
        	return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_ELEMENT); 
        } 
    } 
    
    /**
     * 뷰를 생성하고 초기화 하기 위해 허용되게 될 콜백이다. 
     */ 
    @PostConstruct 
    public void createPartControl(Composite parent, ESelectionService selectionService) { 
    	
        viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); 
        viewer.setContentProvider(new ArrayContentProvider()); 
        viewer.setLabelProvider(new ViewLabelProvider()); 
        
        viewer.addSelectionChangedListener(new ISelectionChangedListener() { 
        	@Override 
            public void selectionChanged(SelectionChangedEvent event) { 
            	selectionService.setSelection(event.getSelection()); 
            } 
        }); 
        viewer.setInput(getElements()); 
    } 
    
    // 단순한 데이타 모델을 구축한다. 
    private List<Todo> getElements() { 
    	List<Todo> todos = new ArrayList<>(); 
        
        Todo todo = new Todo(); 
        todo.setSummary("First Todo"); 
        todo.setDescription("A very good description"); 
        todo.setDone(true); 
        todos.add(todo); 
        
        todo = new Todo(); 
        todo.setSummary("Second Todo"); 
        todo.setDescription("Second super description"); 
        todos.add(todo); 
       	return todos; 
    } 
}

이렇게 변경한 뒤에, 프로젝트를 실행할 수 있고 뷰을 열고 to-do 항목을 볼수 있어야 한다. 

프로퍼티 뷰에 Todo 값을 표시하기 위해, 프로젝트에 org.eclipse.core.runtime.adapters 확장점(extension point)울 추가 한다.

확장점(extension point)의 데이타는 다음과 같아야 한다.:

<extension point="org.eclipse.core.runtime.adapters"> 
	<factory adaptableType="de.vogella.plugin.adapter.model.Todo" 
    		class="de.vogella.plugin.adapter.TodoAdapterFactory"> 
    	<adapter type="org.eclipse.ui.views.properties.IPropertySource"> 
        </adapter> 
    </factory>
</extension>

프로퍼티 뷰에 확장점을 제공하기 위해 IPropertySource 인터페이스 구현한다.

package de.vogella.plugin.adapter; 

import org.eclipse.ui.views.properties.IPropertyDescriptor; 
import org.eclipse.ui.views.properties.IPropertySource; 
import org.eclipse.ui.views.properties.TextPropertyDescriptor; 
import de.vogella.plugin.adapter.model.Todo; 

public class TodoPropertySource implements IPropertySource { 
	
    private final Todo todo; 
    
    public TodoPropertySource(Todo todo) { 
    	this.todo = todo; 
    } 
    
    @Override 
    public boolean isPropertySet(Object id) { 
    	return false; 
    } 
    
    @Override 
    public Object getEditableValue() { 
    	return this; 
    } 
    
    @Override 
    public IPropertyDescriptor[] getPropertyDescriptors() { 
    	return new IPropertyDescriptor[] { 
        	new TextPropertyDescriptor("summary", "Summary")
            , new TextPropertyDescriptor("description", "Description") 
        };
    } 
    
    @Override 
    public Object getPropertyValue(Object id) { 
    	if (id.equals("summary")) { 
        	return todo.getSummary(); 
        } 
        
        if (id.equals("description")) { 
        	return todo.getDescription(); 
        } 
        
      	return null; 
    } 
    
    @Override 
    public void resetPropertyValue(Object id) { 
    
    } 
    
    @Override 
    public void setPropertyValue(Object id, Object value) { 
        String s = (String) value; 
        if (id.equals("summary")) { 
        	todo.setSummary(s); 
        } 
        
        if (id.equals("description")) { 
        	todo.setDescription(s); 
        } 
    } 
}

IPropertySource 구현체로 팩토리와 TodoPropertySource 클래스를 구현한다..

package de.vogella.plugin.adapter; 

import org.eclipse.core.runtime.IAdapterFactory; 
import org.eclipse.ui.views.properties.IPropertySource; 
import de.vogella.plugin.adapter.model.Todo; 

public class TodoAdapterFactory implements IAdapterFactory { 

	// static final 필드를 사용한다 그래서adapterList는 단지 한번 실행된다. 
    private static final Class<?>[] adapterList = new Class<?>[] { IPropertySource.class }; 
    
    @Override 
    public <T> T getAdapter(Object adaptableObject, Class<T> adapterType) { 
    	if (adapterType== IPropertySource.class && adaptableObject instanceof Todo){ 
        	return adapterType.cast(new TodoPropertySource((Todo) adaptableObject)); 
        } 
        
        return null; 
    } 
    
    @Override 
    public Class<?>[] getAdapterList() { 
    	return adapterList; 
    } 
}

만약 워크벤치를 실행하고 Windows > Show > View > Others > Sample Category > Sample View 를 통해 뷰를 열고 그리고 뷰어에 데이타 엘리먼트를 선택할때 프로퍼티 뷰에 데이타를 볼 수 있어야 한다. 

18. 연습 - 어댑터와 워크벤치라벨프로바이더(WorkbenchLabelProvider)

연습 - 프로퍼티 뷰를 위한 어댑터(Adapters)에서 de.vogella.plugin.adapter 플러그인은 이 연습을 위해 재 사용되어야 한다. 

WorkbenchAdapter Sample로 이름이 지어진 또 Sample Category 라는 카테고리를 가지고 있는 다른 파트디스크립터(PartDescriptor)를 생성하다.

파트디스크립터(PartDescriptor) 구현체를 위한 파트로 SampleWorkbenchAdapterView.java 를 생성한다. 이전 SampleView와 차이점은 단지 IStyledLabelProvider 구현체로 WorkbenchLabelProvider를 가지는 DelegatingStyledCellLabelProvider가 LabelProvider로 사용된다는 것이다.

package de.vogella.plugin.adapter.views; 

import java.util.ArrayList; 
import java.util.List; 
import javax.annotation.PostConstruct; 
import org.eclipse.e4.ui.workbench.modeling.ESelectionService; 
import org.eclipse.jface.viewers.ArrayContentProvider; 
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider; 
import org.eclipse.jface.viewers.ISelectionChangedListener; 
import org.eclipse.jface.viewers.SelectionChangedEvent; 
import org.eclipse.jface.viewers.TableViewer; 
import org.eclipse.swt.SWT; 
import org.eclipse.swt.widgets.Composite; 
import org.eclipse.ui.model.WorkbenchLabelProvider; 
import de.vogella.plugin.adapter.model.Todo; 

public class SampleWorkbenchAdapterView { 
	
    private TableViewer viewer; 
    
    /**
     * 뷰어를 생성하고 초기화하는 것을 허용하게될 콜백이다.
     */ 
    @PostConstruct 
    public void createPartControl(Composite parent, ESelectionService selectionService) { 
    	viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); 
        viewer.setContentProvider(new ArrayContentProvider()); 
        
        // IStyledLabelProvider 구현체로 WorkbenchLabelProvider로 DelegatingStyledCellLabelProvider의 활용한다. 
        viewer.setLabelProvider(new DelegatingStyledCellLabelProvider(new WorkbenchLabelProvider())); 
        viewer.addSelectionChangedListener(new ISelectionChangedListener() { 
        	@Override 
            public void selectionChanged(SelectionChangedEvent event) { 
            	selectionService.setSelection(event.getSelection()); 
            } 
        }); 
        viewer.setInput(getElements()); 
    } 
    
    // 단순 데이타 모델을 구축한다.
    private List<Todo> getElements() { 
    	List<Todo> todos = new ArrayList<>(); 
        
        Todo todo = new Todo(); 
        todo.setSummary("First Todo"); 
        todo.setDescription("A very good description"); 
        todo.setDone(true); 
        todos.add(todo); 
        
        todo = new Todo(); 
        todo.setSummary("Second Todo"); 
        todo.setDescription("Second super description"); 
        todos.add(todo); 
        
        return todos; 
    } 
}

WorkbenchLabelProvider를 사용할 때, 뷰어에서 보여질 수있도록 지원되는 객체는 IWorkbenchAdapter, IWorkbenchAdapter2  IWorkbenchAdapter3 인터페이스를 위한 어댑터 적어도IWorkbenchAdapter를 위한 어뎁터는 제공해야 한다. 3가지 인터페이스는 객체들이 보여져야 하는 방법을 결정하기위해 WorkbenchLabelProvider에 의해서 사용될 수 있다. 

WorkbenchLabelProvider를 사용해서 위해 3개의 어댑터를 제공하기 위해, TodoWorkbenchAdapter가 생성된다. TodoWorkbenchAdapter는 모든 3개의 인터페이스를 구현한 추상 클래스인 WorkbenchAdapter로 부터 얻는다.

package de.vogella.plugin.adapter; 

import org.eclipse.jface.resource.ImageDescriptor; 
import org.eclipse.jface.viewers.StyledString; 
import org.eclipse.jface.viewers.StyledString.Styler; 
import org.eclipse.swt.SWT; 
import org.eclipse.swt.graphics.TextStyle; 
import org.eclipse.swt.widgets.Display; 
import org.eclipse.ui.ISharedImages; 
import org.eclipse.ui.PlatformUI; 
import org.eclipse.ui.model.WorkbenchAdapter; 
import de.vogella.plugin.adapter.model.Todo; 

public class TodoWorkbenchAdapter extends WorkbenchAdapter { 
	
    @Override 
    public ImageDescriptor getImageDescriptor(Object object) { 
    	return PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_OBJ_ELEMENT); 
    } 
    
    @Override 
    public StyledString getStyledText(Object object) { 
    	if (object instanceof Todo) { 
        	Todo todo = (Todo) object; 
            StyledString styledString = new StyledString(todo.getSummary()); 
            
            if (todo.isDone()) { 
            	Styler styler = new Styler() { 
            		@Override 
                	public void applyStyles(TextStyle textStyle) { 
                    	// 완료된 Todo는 녹색 배경을 가져야 한다.
                   	 textStyle.background = Display.getCurrent().getSystemColor(SWT.COLOR_GREEN); 
                 	} 
               	}; 
                
                styledString.setStyle(0, todo.getSummary().length(), styler); 
            } 
            
            return styledString; 
    	} 
    
    	return super.getStyledText(object); 
    } 
}

이제 TodoAdapterFactory TodoWorkbenchAdapter를 제공하기 위해 확장되어야 한다. 

package de.vogella.plugin.adapter; 

import org.eclipse.core.runtime.IAdapterFactory; 
import org.eclipse.ui.model.WorkbenchAdapter; 
import org.eclipse.ui.views.properties.IPropertySource; 
import de.vogella.plugin.adapter.model.Todo; 

public class TodoAdapterFactory implements IAdapterFactory { 

	// static final 필드를 사용한다 그래서 adapterList는 단지 한번만 실행된다. 
    private static final Class<?>[] adapterList = new Class<?>[] { 
    	IPropertySource.class, WorkbenchAdapter.class 
    }; 
    
    @Override 
    public <T> T getAdapter(Object adaptableObject, Class<T> adapterType) { 
    	
        if (adapterType== IPropertySource.class && adaptableObject instanceof Todo){ 
        	return adapterType.cast(new TodoPropertySource((Todo) adaptableObject)); 
        } else if (adapterType.isAssignableFrom(WorkbenchAdapter.class) && adaptableObject instanceof Todo) { 
        	return adapterType.cast(new TodoWorkbenchAdapter()); 
        } 
        
        return null; 
    } 
    
    @Override 
    public Class<?>[] getAdapterList() { 
    	return adapterList; 
    } 
}

이제 TodoAdapterFactory는 WorkbenchAdapter 객체들을 반환해야 하고, 또 org.eclipse.core.runtime.adapters 확장점(extension point)에 지정되여야 한다.

그 결과는 이것과 유사하게 보여야 한다.:

19. 이클립스 리소스(Resources)

이클립스 리소스에 IResourceChangeListener를 등록 할 수 있다. 예를 들면, 만약 프로젝트를 가지고 있다면, 그 프로젝트에 또는 그 프로젝트로 부터 리소스 리스너를 추가 하더나 제거 할 수 있다. 

// 리스너 추가하기 
project.getWorkspace().addResourceChangeListener(listener); 

// 리스너 제거하기 
project.getWorkspace().removeResourceChangeListener(listener); 

// 리소스 리스너 예제
private IResourceChangeListener listener = new IResourceChangeListener() { 
	public void resourceChanged(IResourceChangeEvent event) { 
    	if (event.getType() == IResourceChangeEvent.PRE_CLOSE || event.getType() == IResourceChangeEvent.PRE_DELETE) {
        	if (event.getResource().equals(project)) { 
            	// 프로젝트 삭제됨 또는 종료됨 
                // 추가 작업 구현
                
           	} 
            return; 
        } 
        
        if (resource == null) 
        	return; 
            
        IResourceDelta delta = event.getDelta().findMember(new Path(resource.getURI().toPlatformString(false))); 
        
        if (delta == null) { 
        	return; 
        } 
        
        if (delta.getKind() == IResourceDelta.REMOVED) { 
        	// 리소스 삭제
            // 추가 작업 구현 
        } 
	} 
};

20. 연습: 플러그인읠 위한 feature 생성하기

20.1. feature 프로젝트 생성하기

플러그인을 위한 feature 프로젝트를 생성하고 그 feature에 플러그인을 추가 할 수 있다. File > New > Other…​ > Plug-in Development > Feature Project를 통해서 feature 프로젝트를 생성한다.

다음 스크린샷을 따라 feature 프로젝트를 생성한다. 

20.2. 카테고리 정의(Category Definition) 생성하기 

feature 프로젝트 내에 File > New > Other…​ > Plug-in development > Category Definition를 메뉴 엔트리를 통해 새 카테고리 정의를 생성할 수 있다.  

New Category 버튼을 누르고 기능을 설명할 이름으로 카테고리를 생성하다. 이 카테고리에 feature를 추가한다.

이미지 문구 번역) 카테고리를 생성하기를 클릭한다. 보여질 이름을 입력한다.

21. 연습: 플러그인의 업데이트 사이트 생성하기

21.1. 업데이트 사이트 생성하기

장비에 로컬 디렉토리에 feature를 위한 업데이트 사이트를 생성 할 수 있다. 그러기 위해, File > Export > Deployable features를 선택한다.

카테고리를 사용하기 위해, 옵션(Options) 탭으로 변경하고 저장소 분류(Categorize repository) 옵션에 category.xml 파일의 경로를 선택한다. 

21.2. 이클립스 업데이트 관리자를 통해서 feature 설치하기

이클립스 IDE에 이 새 feature를 설치 하기 위해 Help > Install New Software…​ 를 통해 이클립스 업데이트 관리자를 사용하라.

로컬 디렉렉토리를 지정해서 업데이트 관리자를 사용하라. 그리고 feature를 선택하고 설치 하라.  feature를 볼수 없을 경우에, 카테고리 플래그로 항목( Group items by category)을 선택 취소를 시도하라. 이렇게 하면, 내보내기 하는 동안 카테고리를 설정하는 것을 잃어버리게 된다.

공개된 접속 URL 하위에 웹서버에 결과 바칠을 올려 놓는다면 , 사용자가 그 URL로 부터 features를 설치 할 수 있을 것이다. 

21.3. 설치 검증하기

설치 후에 이클립스 IDE를 재시작하라. 플러그인이 이클립스 설치에서 가능한지 그리고 사용할 수 있는지 확인하라. 

22. 이클립스 플러그인 개발 리소스

이클립스 플러그인 개발 FAQ

컬러와 폰트 환경 구성 추가하기 blog post

이클립스 기사, 튜토리얼, 시연, 책들에 목록을 가진 Wiki

이클립스 튜토리얼

이클립스에 어뎁터들

이클립스 리소스 델타 튜토리얼

이클립스에서 마커, 어노테이션, 그리고 데코레이션 사용하기 (IBM)

 

[원문 출처] swagger.io/docs/specification/about/


해당 문서의 경우 SMARTBEAR 사의 Swagger Specirication
Document 문서 일부를 번역 한 것입니다. 

번역본인 해당 문서의 경우 역자에게 있음을 알리며 상업적 이용을 불허합니다. 

www.sogomsoft.co.kr (주) 소곰소프트

 

OpenAPI란 무엇인가?

OpenAPI 규격서 (이전에 Swagger 규격서로 알려진) 는 REST API을 위한 API 설명 서식이다. OpenAPI 파일은 아래를 포함해서 전체 API를 설명하기 위하는 것을 가능하게 한다 :

  • 가능한 엔드포인트 (/users 등등) 와 각각의 엔드포인트에서 동작( GET /users, POST /users 등등)
  • 각각의 작업을 위한 입력과 출력 작업 파라미터들
  • 인증 방법
  • 연락처 정보, 라이선스, 사용 약관과 기타 정보.

 

API 규격서는 YAML 또는 JSON으로 작성 될 수 있고. 이 서식은 쉽게 배울수 있고 개발자와 컴퓨터 양쪽이 읽을 수 있다. 완전한 OpenAPI 규격서는 GitHub에서 볼 수 있다. : OpenAPI 3.0 규격서

Swagger는 무엇인가?

Swagger REST API들을 설계하고 빌드하고 문서화하고 소비하는데 도움을 줄 수 있는 OpenAPI 규격서을 중심으로 빌드된 오픈소스 툴 세트이다. 주요 Swagger 툴은 아래를 포함한다. :

왜 OpenAPI를 사용하는가?

API 자체의 구조를 기술하는 API들의 능력은 OpenAPI에서 매우 엄청난 것의 뿌리가 된다. 일단 작성되면, OpenAPI 규격서와 Swagger 툴은 다양한 방법에서 더 나은 API 개발 하게 만들 수 있다. :

  • 설계-우선 사용자들: 개발 할 API의 서버 Stub을 생성하기 위해 Swagger 코드생성기를 사용한다. 단지 서버 로직을 구현 하는것만 남겨저 있고 이미 사용할 준비가 되어 있다.
  • 40개 다른 개발 언어에서 사용할 클라이언트 라이블러리를 생성하기 위해 Swagger 코드생성기를 사용한다.
  • 사용자가 브라우저에서 직접 개발한 API 호출을 시도 할 수 있게 하기 위해 API문서와 상호작용을 생성하는 Swagger 사용자인터페이스를 사용한다. 
  • 개발된 API에 API 관련 툴을 접속하기 위한 규격으로 사용한다. 예를들면, API에 자동화된 테스트 생성을 위해 SoapUI에 규격을 불러들인다. 
  • 그리고 추가로! Swgger와 통합된 open-source commercial tools 을 확인해 보라.

'WebService, OpenAPI' 카테고리의 다른 글

YAML 1.2 규격  (0) 2017.10.17

[원문 출처] http://activemq.apache.org/slow-consumer-handling.html


해당 문서의 경우 Apache 재단의 ActiveMQ Document 문서 일부를 번역 한 것입니다. 
번역본인 해당 문서의 경우 역자에게 있음을 알리며 상업적 이용을 불허합니다. 

www.sogomsoft.co.kr (주) 소곰소프트



속도가 느린 컨슈머는 일단 채워진 RAM에 오래된 메시지를 유지하기 위해 프로듀서에 속도를 늦추도록 브로커에 강제할 수 있고 빠른 컨슈머가 속도가 느려지게 되기 때문에 비영속성 토픽에서 문제가 발생 할 수 있다. 이후에 우리가 구현 할수 있는 하나의 옵션은 디스크에 스플링(디스크에 쓰기와 동시에 처리) 하는 것이다.  그러나 디스크에 스플링 하는 것 역시 속도가 빠른 컨슈머를 속도를 감소 시킨다.

현재 브로커의 프리패치 버퍼에 추가된 컨슈머를 위해 브로커가 유지하게 되는 일치하는 메시지의 최대 수를 환경 구성하는 전략을 가지고 있다. 최대 값에 도달 할 때, 새 메시지가 들어 오기 때문에 오래된 메시지들은 폐기 된다. 이것은 현재 메시지를 위해 RAM을  유지하고 속도가 느린 컨슈머에 메시지를 보내는 것을 유지하는 것을 허용한다. 그러나 오래된 메시지는 폐기한다.

지연 메시지 제한 전략(Pending Message Limit Strategy)

목적지 Map에 PendingMessageLimitStrategy 구현체 클래스를 환경 구성할 수 있다. 그래서 토픽 네임스페이스의 다른 영역이 속도가 늦은 컨슈머를 다루기 위한 다른 지연 전략을 가질 수 있다. 예를 들면,  매우 부피가 큰가격을 위해 지연 전략을 사용하기를 원할지 모른다. 그러나 매우 작은 부피를 가지는 주문과 거래를 위해 오래된 메시지는 폐기하는 것을 원하지 않을지 모른다.

이 전략은 컨슈머를 위해 RAM에 유지 되어야 하는 지연되는 메시지의 최대 량을 계산한다. (위 프리패치 사이즈). 0 값은 프리패치 량보다 더 이상의 메시지를 유지 하지 않는 다는 것을 의미한다. 0보다 큰 값은 메시지의 량에 들어 온 새 메시지로 오래된 메시지를 폐기하면서 프리패치 량보더 더 메시지를 유지 하게 될 것이다.  -1 값은 메시의 폐기를 비활성화 한다. 

현재 이 전략은 다른 두 구현체가 있다. :

  • 정해진 상수 지연 메시지 제한 전략 (ConstantPendingMessageLimitStrategy)
  • 프리패치 비율 지연 메시지 제한 전략 (PrefetchRatePendingMessageLimitStrategy)

정해진 상수 지연 메시지 제한 전략 (ConstantPendingMessageLimitStrategy)

이 전략은 모든 컨슈머를 위해 정해진 상수 제한값을 사용한다.  (위 프리패치 사이즈).

예제:

xml<constantPendingMessageLimitStrategy limit="50"/>

프리패치 비율 지연 메시지 제한 전략 (PrefetchRatePendingMessageLimitStrategy)

이 전략은 컨슈머들 프리패치 사이즈의 곱셈을 사용하여 지연 메시지의 최대 량을 계산한다. 그래서 예를 들면 각각의 컨슈머를 위한 프리패치 수를 2.5배 유지 할 수 있다. 

예제:

xml<prefetchRatePendingMessageLimitStrategy multiplier="2.5"/>

제한을 환경 구성하기 위해 프리패치 정책 사용하기 

JMS 클라이언트는 지속성 또는 비지속성 큐와 토픽을 위한 다양한 프리패치 정책을 환경 구성하기 위해 사용될 수 있는 프리패치 정책 을 가진다. 이 프리패치 정책은 또한 커넥션 단위 또는 컨슈머 단위로 maximumPendingMessageLimit 을 지정하는 것을 허용한다. 이 값을 환경 구성하는 하나의 작은 차이가 있다; OpenWire 같은 non-JMS 클라이언트로 동작을 단순화 하기 위해,  0값이 무시된다.; 그래서 환경구성 될수 있는 가장 낮은 값은 1이다.

퇴출 정책 환경 구성하기

속도가 느린 컨슈머에 메시지가 퇴출되는 것을 결정할때 사용하는 MessageEvictionStrategy  전략이 있다. 

하나는 가장 오래된 메시지를 퇴출 하는 것이 기본 값이고 설정은 다음과 같다:

xml<oldestMessageEvictionStrategy/>

그러나, 몇몇 어플리케이션이 퇴출을 위한 메시지를 선택하는 특별한 방법을 사용하기 위해 사용할 수 있다. 예를들면, 만약 가격을 업데이트 할 시장 데이타를 보낸다면, 가장 오래된 메시지가 아닌 오래된 가격 값을 찾는 것을 원할지 모른다.

예제:

xml<uniquePropertyMessageEvictionStrategy propertyName="STOCK"/>

propertyName 은 가격을 지정한 JMS 메시지 프로퍼티이다. 

역자주) propertyName 에 지정된 이름에 가장 오래된 메시지를 퇴출 한다.


다른 하나의 옵션은 우선 순위가 가장 낮은 메시지로 가장 오래된 메시지를 사용할 수있다. 그러므로 만약 몇몇 높은 우선순위 메시지가 있다면 심지어 새 메시지라고 하더라도 우선순위가 낮은 메시지가 퇴출된다. 


xml<oldestMessageWithLowestPriorityEvictionStrategy/>

예제

다음 예지는 ActiveMQ 브로커의 환경 구성 파일을 보여준다. Notice that for topics in the PRICES.> wildcard range the pendingMessageLimitStrategyproperty is set to only keep around 10 messages for each consumer above their prefetch buffer size.

xml

<beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:amq="http://activemq.apache.org/schema/core"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://activemq.apache.org/schema/core  http://activemq.apache.org/schema/core/activemq-core.xsd">

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
    <broker xmlns="http://activemq.apache.org/schema/core" persistent="false" brokerName="${brokername}">
      <!-- lets define the dispatch policy -->
      <destinationPolicy>
        <policyMap>
          <policyEntries>

            <policyEntry topic="FOO.>">
              <dispatchPolicy>
                <roundRobinDispatchPolicy/>
              </dispatchPolicy>
              <subscriptionRecoveryPolicy>
                <lastImageSubscriptionRecoveryPolicy/>
              </subscriptionRecoveryPolicy>
            </policyEntry>

            <policyEntry topic="ORDERS.>">
              <dispatchPolicy>
                <strictOrderDispatchPolicy/>
              </dispatchPolicy>
              <!-- 1 minutes worth -->
              <subscriptionRecoveryPolicy>
                <timedSubscriptionRecoveryPolicy recoverDuration="60000"/>
             </subscriptionRecoveryPolicy>
          </policyEntry>
  
          <policyEntry topic="PRICES.>">
            <!-- lets force old messages to be discarded for slow consumers -->
            <pendingMessageLimitStrategy>
              <constantPendingMessageLimitStrategy limit="10"/>
            </pendingMessageLimitStrategy>
            <!-- 10 seconds worth -->
            <subscriptionRecoveryPolicy>
              <timedSubscriptionRecoveryPolicy recoverDuration="10000"/>
            </subscriptionRecoveryPolicy>
          </policyEntry>
  
          <policyEntry tempTopic="true" advisoryForConsumed="true"/>
          <policyEntry tempQueue="true" advisoryForConsumed="true"/>

        </policyEntries>
      </policyMap>
    </destinationPolicy>
  </broker>
</beans>

사용 팁

만약 특정한 컨슈머가 속도가 느리다면 빠른 컨슈머보다 프리패치 사이즈를 더 작게 설정하는 것이 좋다!

예를 들면, 만약 특정 서버가 매우 느리고 높은 메시지 비율을 가지고 있고, 몇몇 속도가 빠른 컨슈머가 있다면, 빠른 서버 보다 더 작게 속도가 느린 서버에 프리패치를 설정하고 이 기능을 활성화 하는 것을 원할지 모른다.

속도가 느린 컨슈머 상태 모니터링 하기

활성화된 구독의 상태를 보기 위해 JMX 콘솔을 사용 할 수 있다. 이 방법은 TopicSubscriptionViewMBean에 다음 통계를 보는것을 허용한다.  

Statistic

Definition

discarded

속도가 느린 컨슈머에 구독 구독라이프 사이클 동안 폐기된 많은 메시지가 수.

matched

프리패치 버퍼에 가용량이 사용가능하자 마자 구독에 전파되게 되고 일치된 메시지의 현재 수. 그래서 0이 아닌 값은 구독을 위해 프리패치 버커가 가득 찬 것을 암시한다. 


[원문 출처] http://activemq.apache.org/vm-transport-reference.html


해당 문서의 경우 Apache 재단의 ActiveMQ Document 문서 일부를 번역 한 것입니다. 
번역본인 해당 문서의 경우 역자에게 있음을 알리며 상업적 이용을 불허합니다. 

www.sogomsoft.co.kr (주) 소곰소프트





VM 전송

VM 전송은 클라이언크가 네트워크 커넥터의 오버헤드 없이 VM 내부에 다른 곳에 접속 하는 것을 허용한다. 이 커넥션은 소켓 커넥션이 사용되지 않는다. 그러나 고성능의 내장된 메시징 시스템을 사용 가능하게 직접 메서드 호출을 사용한다. .

VM 커넥션을 사용하기 위한 첫번째 클라이언트는 내장된 브로커를 구동하게 된다.  그 뒤의 클라이언트 커넥션은 같은 브로커에 접속한다. 일단 브로커에 접속하기 위한 모든 VM 커넥션이 모두 닫히게 되면, 내장된 브로커는 자동적으로 셧다운 된다.


심플 브로커 환경 구성 구문

VM 커넥션을 위한 일반적인 구문인 simple 이고, 단지 제한된 내장된 브로커의 환경 구성을 제공한다. 


vm://brokerName?transportOptions


만약 이미 인스턴스화된 브로커에 접속을 원하면, 내장된 브로커(예를 들면. Apache ServiceMix 같은 경우에 ) brokerName에  이미 실행중인 브로커의 brokerName 과 일치하는 것이 사용 된 vm://brokerName URI를 사용하는 것을  보장한다. 

전송 옵션

옵션 명

기본값

상세 설명

marshal

false

만약 true 이면, 각각의 명령어가 WireFormat을 사용하여 강제로 marshalled 와 unmarshalled 된 VM 전송 통해 보낸다. 

wireFormat

default

사용할  WireFormat 의 이름

wireFormat.*

 

wireFormat을 환경 구성하기 위해 사용되는 이 접두사로 된 모든 프로퍼티.

create

true

만약 브로커가 존재 하지 않는다면 요청에 의해 브로커를 생성하게 될 것 인지 여부

waitForStart

-1

만약 0보다 크면, 브로커를 시작하기 위해 기다리는 밀리세컨드 타임아웃을 나타낸다. -1 과 0 은 기다리지 않는다는 것을 의미한다. 단지 ActiveMQ 5.2+ 에서 지원된다.

broker.*

 

브로커를 환경 구성하기 위해 사용되는 이 접두어를 사용하는 모든프로퍼티 . 더 많은 정보는 Wire Formats 환경구성하기 를 보라.

예제 URI
vm://broker1?marshal=false&broker.persistent=false

내장된 브로커 주의

만약 VM 전송을 사용하고 명시적으로 내장된 브로커 를 환경구성하는 것을 원하면, 브로커를 시작하기 전에 JMS 커넥션을 생성할 기회가 있다. 만약 VM 전송을 사용하고 이미 환경 구성된 브로커가 없다면, 현재 ActiveMQ는 브로커를 자동생성하게 될 것이다. (ActiveMQ 5.2 이상에서,  커넥션 URI를 사용을 위해 waitForStart 와 create=false 옵션을 사용하는 것이 가능하다.)

만약 스프링을 사용한다면 이 문제를 해결하기 위해,  내장된 브로커에서 이 문제가 발생하는 것을 피하기 위해 JMS ConnectionFactory가 depends-on 속성을 사용해야 할지 모른다. 

예를 들면.

<bean id="broker" class="org.apache.activemq.xbean.BrokerFactoryBean">
    <property name="config" value="classpath:org/apache/activemq/xbean/activemq.xml" />
    <property name="start" value="true" />
  </bean>
 
  <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory" depends-on="broker">
    <property name="brokerURL" value="vm://localhost"/>
  </bean>

어드벤스드 브로커 환경 구성 구문

VM 커넥션을 위한 어드벤스드 구문이다. Broker 브로커 환경  구성 URI를 사용하여 좀더 광범위하게 브로커를 환경 구성하는 것을 허용한다. 

vm:(broker:(tcp://localhost)?brokerOptions)?transportOptions

또는 
vm:broker:(tcp://localhost)?brokerOptions

전송 옵션 

옵션명

기본값

상세 설명

marshal

 false

만약 true 이면, 각각의 명령어가 WireFormat을 사용하여 강제로 marshalled 와 unmarshalled 된 VM 전송 통해 보낸다. 

wireFormat

default

사용할  WireFormat 의 이름

wireFormat.*

 

WireFormat을 환경 구성하기 위해 사용되는 이 접두사를 사용하는 모든 프로퍼티

VM 전송을 사용을 최적화 하는 더 많은 옵션이 있다.

예제 URI
vm:(broker:(tcp://localhost:6000)?persistent=false)?marshal=false

외부 환경 구성 파일을 사용하여 내장된 브로커 환경 구성하기 

 VM 전송과 외부 환경 구성 파일을 사용하여 (i.e. activemq.xml) 내장된 브로커를 시작하기 위해, 다음 URI를 사용하라:


vm://localhost?brokerConfig=xbean:activemq.xml


+ Recent posts