정말 황당한 현상이 발생했다. 바로 직전까지 잘 보이던 로그(commons-logging-1.1.1, log4j-1.2.16)가 갑자기 안보인다. 그래서 구글링도 해보고 Apache Commons Logging(JCL), Log4j 의 문서도 찾아 봤지만 내가 설정한 것과 별반 다를것이 없었다.
클래스 로더 순서에 대한 내용은 http://cafe.naver.com/devmaster/279 를 참고하세요.
Parent Last로 놓을 수 없는 경우에는 아래와 같은 방법을 사용한다.
일단 Apache 홈페이지에서 다운 받은 commons logging을 사용하여 WebSphere Application Server에서 돌릴경우 동작을 하지 않는 이유는 <WAS_HOME>/plugins/com.ibm.ws.prereq.commons-logging.jar 에 (WAS v7.0기준) 있는 org.apache.commons.logging.Log 클래스와 org.apache.commons.logging.LogFactory 클래스가 먼저 로딩이 되기 때문이다.
즉, 동일한 클래스가 존재하고 원래의 클래스보다 다른 클래스가 먼저 로딩이 되기 때문에 동작하지 않을 것이라는 말인것 같다. 그래서 찾아봤다.
LogFactory로 검색을 해보면 두개가 검색이 된다. 하나는 commons-logging-1.1.1.jar 이고 또 하나는 jcl-over-slf4j-1.5.8.jar 이다. 흠. jcl-over-slf4j-1.5.8.jar 가 왜 갑자기 딸려 들어 왔을까? 궁금했다.
위의 종속관계 그래프에서 알 수 있듯이 로그가 안나오기 전에 Apache Tiles2를 적용하는 과정에서 jcl-over-slf4j-1.5.8.jar 가 딸려왔고 잘나오던 로그가 갑자기 먹통이 되었었던것 같다.
그래서 다음과 같이 메이븐 pom.xml 설정을 변경했다.
<dependency>
<groupId>org.apache.tiles</groupId>
<artifactId>tiles-core</artifactId>
<version>${tiles.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</exclusion>
</exclusions>
</dependency>
적용결과 로그가 잘나온다. 예전에도 이런일이 있었는데 그땐 commons-logging을 포기하고 Log4j로 선언했던것 같다. 이 글이 많은 사람들에게 해결의 실마리가 되었으면한다.
WTP로 Dynamic Web Project를 생성하고 Maven2로 빌드 체계를 만든 목적은 변경한 내용을 즉시 확인하기 위함일 것이다. Eclipse 3.2 부터 maven 및 WTP를 통합해왔던 터라 이 기능을 활용한 개발 생산성은 아주 높았다. 그런데 Eclipse 3.6 helios 부터 이 기능에 버그가 있는 듯 하다.
WTP는 워크스페이스내에 열려있는 프로젝트를 웹 애플리케이션(war)의 WEB-INF/lib 에 배포하기 위해 Deployment Assembly 라는 기능을 추가했다. 그런데 이 기능이 maven과 통합되어 움직이는 것이 아니라 따로따로 동작했다. 즉, 수동으로 Deployment Assembly에 열려있는 프로젝트를 등록하고 삭제하는 작업을 해줘야 한다.
자 이제 Maven Dependencies 에 있는 참조컴포넌트 또는 라이브러리들이 워크스페이스의 프로젝트로 열리는 순간 Deployment Assembly에 자동 등록하는 방법을 알아본다.
1. 설치된 m2eclipse 버전을 확인한다.
Maven Integration for Eclipse (Required) 0.12.1.20110112-1712 org.maven.ide.eclipse.feature.feature.group
2. m2e-extras 를 설치한다.
Name: m2eclipse extra
Location: http://m2eclipse.sonatype.org/sites/m2e-extras
3. 설치된 버전확인
4. 해당 웹프로젝트를 선택하고 마우스 오른쪽 버튼을 클릭 "Properties -> Deployment Assembly -> Add"
5. Java Build Path Entries -> Next
6. Maven Depencencies -> Finish
7. 웹 애플리케이션 프로젝트 clean 을 하면 완료된다.
※ 4 ~ 6번 까지의 과정은 아래 그림과 같이 경고(Warning)가 하나 보일 것이다. 이 경고를 해결하기 위해 Ctrl +1 을 해보면 Quick Fix 창이 뜨는데 첫번째 것을 선택하고 확인해도 동일한 결과가 된다. 만약 이미 추가된 경우라면 경고도 보이지 않으며 Java Build Path Entries에도 보이지 않을 것이다.
메이븐을 이용하면서 레파지토리로 Nexus나 Artifactory를 이용할 수 있다.
필자의 경우 최근엔 Nexus를 이용하고 있지만 아직도 Artifactory를 사용하는 프로젝트가 많이 있어서 종종 사용하게 된다.
오늘은 다음과 같은 목적으로 Artifactory의 설정을 바꿔보았다.
1. 새로운 레파지토리가 필요하여 아이디를 지정하고 생성함.
2. 보안을 위해서 기존 사용자는 모든 레파지토리를 이용할 수 있지만 신규 사용자는 새로 만들어진 레파지토리만 이용하게하려고 함.
3. 사용자와 그룹 및 퍼미션을 생성하고 관계를 설정.
4. 생성된 레파지토리에 퍼미션을 할당함.
5. Admin -> Security -> General 메뉴에서 "Allow Anonymous Access"를 Unchecked 함.
기대했던 동작은 기존 사용자는 모든 레파지토리에 접근할 수 있고 신규 사용자만 새로이 생성된 레파지토리에 접근했어야 했는데 어찌된 일인지 모든 사용자에게서
[WARNING] repository metadata for: 'snapshot kr.flowerteam.pom:BasicPOM:1.1.2-SNAPSHOT' could not be retrieved from repository: snapshots due to an error: Authorization failed: Access denied to: http://{myhost}/artifactory/repo/kr/flowerteam/pom:BasicPOM/7.13-SNAPSHOT/maven-metadata.xml
위의 환경에서 2개이상의 DataSource를 설정하고 JTA 트랜잭션을 설정했을 때 아래와 같은 에러가 발생했다.
[com.arjuna.ats.internal.jta.transaction.arjunacore.lastResource.disallow] Adding multiple last resources is disallowed. Current resource is org.jboss.resource.connectionmanager.TxConnectionManager$LocalXAResource@473fc2
org.jboss.util.NestedSQLException: Could not enlist in transaction on entering meta-aware object!; - nested throwable: (javax.transaction.SystemException: java.lang.Throwable: Unabled to enlist resource, see the previous warnings. tx=TransactionImple < ac, BasicAction: -3f57fff1:c23b:4d420dcd:e status: ActionStatus.ABORT_ONLY >); - nested throwable: (org.jboss.resource.JBossResourceException: Could not enlist in transaction on entering meta-aware object!; - nested throwable: (javax.transaction.SystemException: java.lang.Throwable: Unabled to enlist resource, see the previous warnings. tx=TransactionImple < ac, BasicAction: -3f57fff1:c23b:4d420dcd:e status: ActionStatus.ABORT_ONLY >))
at org.jboss.resource.adapter.jdbc.WrapperDataSource.getConnection(WrapperDataSource.java:96)
at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:113)
1. You have 2 EAR files in a single server instance. Not just 2 ear files, but from within a transaction of a deployed EAR file, when you try to access the other EAR file, get some data from there and try to commit the transaction. This is not allowed in arjuna API (AFAIK). To fix this, edit JBOSS_HOME/server/default/conf/jbossjta-properties.xml and add the following line
The problem does not stop here..The datasource has to support it.
Modify the *ds.xml to look like this ( localtxdatasource would not work) :
<?xml version="1.0" encoding="UTF-8"?> <datasources> <xa-datasource> <jndi-name>CatMgrDS</jndi-name> <xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class> <xa-datasource-property name="URL">jdbc:mysql://192.168.157.57:3306/30cm</xa-datasource-property> <xa-datasource-property name="User">root</xa-datasource-property> <xa-datasource-property name="Password">root</xa-datasource-property> <!– the minimum size of the connection pool –> <min-pool-size>1</min-pool-size> <!– The maximum connections in a pool/sub-pool –> <max-pool-size>4</max-pool-size> </xa-datasource> </datasources>
Next step is to make sure you have the latest version of mysql-connector(in my case atleast). Versions prior to 5.1.10 doesn’t support XA datasource(ref :http://marc.info/?l=kandula-dev&m=113945114400475&w=2) I replaced my old mysql connector with the latest one.
Things should work fine then. will keep this writing up to date to my best possible extent
필자의 경우 첫번째 방법인
JBOSS_HOME/server/{instance}/conf/jbossjta-properties.xml 에
apache2.2-common 패키지를 푸는 중입니다 (.../apache2.2-common_2.2.14-5ubuntu8_i386.deb에서) ...
ufw에 대한 트리거를 처리하는 중입니다 ...
ureadahead에 대한 트리거를 처리하는 중입니다 ...
man-db에 대한 트리거를 처리하는 중입니다 ...
apache2.2-common (2.2.14-5ubuntu8) 설정하는 중입니다 ...
ERROR: Module reqtimeout does not exist!
dpkg: apache2.2-common을(를) 처리하는 데 오류가 발생했습니다 (--configure):
설치한 post-installation 스크립트 하위 프로세스가 오류 1번을 리턴했습니다
처리하는 데 오류가 발생했습니다:
apache2.2-common
E: Sub-process /usr/bin/dpkg returned an error code (1)
위의 에러 메시지는 apache2.2-common 패키지를 설치하는 것이지만 에러메시지는 동일하다.
ERROR: Module reqtimeout does not exist!
사실 위의 에러를 검색했으면 엄청난 삽질(무려 8시간 이상)을 하지 않았을 것이다.
아파치 소스를 컴파일하여 설치하는 것과 패키지 설치 중에 고민을 하다가 결국 소스컴파일쪽으로 마음이 기울었다. 그리고 소스를 다운받아 정상적으로 설치되었고 실행도 잘되었다. 그러나 활용대비 모듈추가시에 너무 손이 많이 간다는게 문제였다. 그래서 결국 언인스톨하고 다시 패키지 설치를 하려고 했는데 위의 에러상황을 맞이하게 된것이다.
It looks like you've deleted /etc/apache2/mods-available/cgid.load, so mod_cgid cannot be enabled. To fix this, please purge and reinstall apache2.2-common.
apache2 (2.2.14-5ubuntu8) 설정하는 중입니다 ...
붉은색 메시지를 통해 알수 있듯이 설정에 문제가 있으니 apache2.2-common 을 설정파일을 포함하여 완전히 삭제해줄 것을 요구한다. 이는 이전에 설치한것에 오류가 있기 때문이다.
sudo apt-get -f purge apache2.2-common
를 실행한다음
sudo apt-get -f install apache2
와 같이 다시 설치하면 이제 정상적으로 설치가 될 것이다.
/var/lib/dpkg/info/apache2.2-common.postinst 의 내용을 다시 확인해보면 원래대로 돌아와 있다.
10년의 개발기간동안 많은 후배개발자들 뿐만 아니라 수강생들에게도 늘 강조했던 말이다. 그런데 이클립스 WTP 개발환경에서 WST, JST 등의 Validator 가 많은 경고를 내고있다. 그 내용을 보면 정말 실수에 의해서 고쳐야 할 내용도 있지만 이건 이럴수밖에 없는데 하는 내용도 있다. 각 경우마다 허용범위를 설정하는 방법이 없기 때문에 부득이 해당 경고내용을 무시하도록 할 수 밖에 없다.
최근 HTML5가 많은 개발자들에게 관심을 받고 있는데 아직 이클립스 WTP에서는 지원하지 않는다. DTD나 Schema같은 것이 제공된다면 해결될 수도 있는 문제이지만 제공하지 않는다고 하니 이클립스가 버전업이 될때까지 경고를 제거하고 보는 방법밖에 없어 보인다.
일단 본론으로 돌아와서 "Undefined attribute name"은 HTML에서 지정하지 않은 속성을 사용하고 있다는 경고인데 추가속성을 사용해야 하는 상황이라면 이 경고를 제거할 수 없을 것이므로 경고가 많아 질 수 있다. 이로인해 더 중요한(심각한 문제를 일으킬수 있는) 경고를 인지하지 못하게 될 수있다. 결국 HTML에서 발생하는 경고는 일부 무시할 수 밖에 없다.
그래서 다음과 같은 방안을 제시한다.
Window -> Preferences -> Web -> HTML files -> Validation 을 선택하고 Attributes 항목을 클릭한다.
그런다음 아래 그림과 같이 Undefined attribute name: Ignore 와 같이 설정하고 확인버튼을 선택한다.
위에서 두가지 방식을 보여주고 있는데 첫번째는 특정 자원을 지정하는 방식 즉, html 파일을 직접 지정하는 방식이고 두번째는 #show the first help contents. 라고 주석을 달아 놓은 것처럼 help system에 의해 생성되는 컨텐츠 목록의 첫번째를 보여줍니다. 그닥 중요하지는 않지만 둘사이에는 미묘한 차이가 있으므로 확인해 보시기 바랍니다. 두번째 방식은 목차를 생성하는 도움말 기능을 이해하면 될 것 같습니다.
RCP를 개발할 때 설치되지 않은 플러그인이나 프라그먼트를 배포할 때 포함시켜야 할때가 있다. 예를 들면 개발하고 있는 OS와 다른
OS환경으로 배포해야 하거나 배포 국가에 따라 다국어를 지원해야 하는 경우를 들 수 있다. 물론 모든 플러그인이나 프라그먼트를 포함하여 배포할
수도 있으나 비효율적이다. 설치하는 사람의 입장에서 필요하지 않은 플러그인을 설치하는 것이기 때문이다.
이렇게 배포환경에 따라 설치되지 않은 플러그인이나 프라그먼트를 포함하여 배포할 수 있는 방법은 없을까?
방법은 OS환경의 경우 DeltaPack을 이용하고 다국어의 경우 LanguagePack을 이용하는 것이다.
나의 경우 Galileo를 선택했고 아래와 같이 한국어를 검색하여 이클립스 코어 부분만 다운받았다.
Language:
Korean
...
BabelLanguagePack-eclipse-ko_3.5.0.v20091121043401.zip
(92.82%)
...
다운받은 파일을 ${eclipse_home}/languagepack 에 압축을 해제한다. 디렉토리 구조는
아래와 같다.
languagepack
|-- eclipse
|-- features
|-- plugins
배포방법:
RCP를 빌드하는 방법은 크게 두가지가 있다. 이클립스를 이용하는 방법과 Headless Build 방법(이클립스를 사용하지 않고 Ant,
Maven과 같은 빌드 스크립트를 이용)이 있다. 이글에서는 이클립스를 이용하여 빌드하고 배포하는 방법을 알아보도록 하고 Headless
Build는 나중에 알아보도록 하자.
Window -> Preferences -> Plug-in Development -> Target Platform 을
선택하고 아래 그림과 같이 Running Platform (Active)를 선택한다. 만약 선택할 대상이 없다면 Add... 버튼을 선택하여
기본으로 대상을 하나 생성하기 바란다.
아래 내용을 이해하고 있는 개발자의 경우 각 상황에 맞게 대상을 생성하도록 하고 그렇지 않는 경우는 기본으로만 생성하도록 한다. 잘못
생성하는 경우 클래스를 찾을 수 없다는 에러와 한참동안 씨름할 것이다.(^^ 이것 때문에 2시간을 고생했다는...)
Edit... 버튼을 선택하여 Edit Target Definition 창이 뜨면 Add... 버튼을 선택한다.
Add Content에서 Inatallation 을 선택하고 Next > 버튼을 선택한 다음 Location에
${eclipse_home}/languagepack/eclipse 과 같이 입력하고 Next > 버튼을 선택한다.
그러면 아래와 같이 현재 포함되어 있는 플러그인이나 프라그먼트의 목록을 보여주며 Finish 버튼을 선택하여 종료한다.
위와 같이 하면 Plug-ins 뷰에 방금 추가한 플러그인이나 플라그먼트가 보일 것이다. 만약 Plug-ins 뷰가 보이지 않는다면
Window -> Show View -> Other... -> Plugin Development -> Plug-ins
를 선택하고 확인한다.
이제 실제 이클립스에는 LanguagePack이 설치되어 있지 않지만 해당 플러그인들을 이용할 수 있다.
배포를 Plug-in 기반으로 하는 경우는 직접 해당 플러그인을 추가하면 되고 피처 기반으로 하는 경우는 해당 피처에 추가하거나 nls
피처를 만들어 추가하면 된다.
nls의 경우 거의 대부분이 프라그먼트이므로 관리의 편리상 nls 피처를 만들어 관리할 것을 권고한다.
이 페이지는 3.5에 있는 p2 임시 API를 사용하여 RCP 애플리케이션에 p2 self-updating 지원을 추가하는 방법에 대한 정보를 통합한다.
먼저 RCP 메일 예제 애플리케이션을 만든 다음 다양한 방법으로 변경해 본다.
p2 추가하기, 빌드하기, p2로 애플리케이션 제공하기
p2로 업데이트를 하기위한 애플리케이션 설정 절차는 다음과 같이 몇 단계를 거친다.
애플리케이션에 p2 번들을 추가해야한다. 가이드와 같이 org.eclipse.equinox.p2.user.ui 피처를 사용하여 제품 피처(product's features)중의 하나로 p2 번들을 추가함으로써 나타낼 수 있다. 이것은 또한 피처에서 필요한 것들을 나타낼 것이고 애플리케이션에 p2를 설치함으로써 올바른 코드를 작성할 수 있게 한다.
처음에 애플리케이션 안에 있어야만 하는 저장소(레파지토리)를 터치포인트 명령(touchpoint instructions) 사용으로 관리할 수 있다. addRepository 명령은 각 저장소에 대해 사용해야 한다. p2 UI에서, 메타데이터(metadata)와 산출물(artifact) 저장소를 같은 장소에 지정하는 것으로 간주한다. 사용자가 UI에서 저장소를 추가하면 메타데이터와 산출물 저장소에는 그 위치가 추가된다. 터치포인트 명령을 사용할 때면, 반드시 메타데이터와 산출물 저장소에 추가되어야만 한다.
여러분의 목적이 단순히 RCP 앱(app) 내부의 SDK에서 사용하고 있는 업데이트 UI와 동일한 것을 사용하는 것이라면, 변경할 것이 거의 없다. 이렇게 하는 방법을 보여주는 RCP 메일 애플리케이션 샘플은 here 에서 찾을 수 있다. 코드안에서 'XXX' 태스크 태그를 찾아 봄으로써 표준 RCP 메일 애플리케이션에 요구된 변화를 볼 수 있다.
애플리케이션에 org.eclipse.equinox.p2.user.ui 피처를 포함해야 할 것이다. 이것은 p2 core 및 다른 요구되는 모든 번들외에도 여러분의 애플리케이션에 다음과 같은 UI 번들을 추가할 것이다.:
org.eclipse.equinox.p2.ui
org.eclipse.equinox.p2.ui.sdk
org.eclipse.equinox.p2.ui.sdk.scheduler
변경되지 않은 p2 SDK UI 번들을 사용하기 위해서는 p2 UI로 여길만큼 동일한 UI 서비스를 제공할 필요가 있다. 이것은 다음을 포함한다:
ApplicationWorkbenchWindowAdvisor 는 p2 자동 업데이트 상태 및 진행상황을 보여주는 상태라인(status line)과 진행상황 영역(progress area)을 정의한다.
public void preWindowOpen() {
IWorkbenchWindowConfigurer configurer = getWindowConfigurer();
configurer.setInitialSize(new Point(600, 400));
configurer.setShowCoolBar(true);
// XXX We set the status line and progress indicator so that
// update information can be shown there
configurer.setShowStatusLine(true);
configurer.setShowProgressIndicator(true);
}
ApplicationActionBarAdvisor 는 메뉴바에 Help 메뉴가 있어야 하고 p2 기여(contributions)가 만들어 질 수 있도록 additions 그룹이 정의되어 있어야만 한다. In order to make the update preferences available to the user, there must also be access to the preferences from some menu.
protected void fillMenuBar(IMenuManager menuBar) {
MenuManager fileMenu = new MenuManager("&File", IWorkbenchActionConstants.M_FILE);
// XXX Window menu
MenuManager windowMenu = new MenuManager("&Window", IWorkbenchActionConstants.M_WINDOW);
MenuManager helpMenu = new MenuManager("&Help", IWorkbenchActionConstants.M_HELP);
menuBar.add(fileMenu);
// XXX Window menu
menuBar.add(windowMenu);
...
// XXX Window menu
windowMenu.add(preferencesAction);
// Help
// XXX add an additions group because this is what SDK UI expects
helpMenu.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
helpMenu.add(new Separator());
helpMenu.add(aboutAction);
}
Since the RCP Mail application does not define preferences of its own, the resulting preference dialog will only show the preferences contributed by p2 and its required bundles.
...
애플리케이션을 시작할 때 UI 없이 업데이트하기
때때로 가장 간단한 UI는 UI가 없는 것이다. 고도로 관리되는 제품 설치 중에 사용자의 개입없이 애플리케이션이 시작될 때마다 자동으로 업데이트 되는 것이 가치있는 것일 수도 있다.
업데이트가 정말 UI가 없는 것(headless)은 아닌데 그 이유는 진행지시기(progress indicator)가 보이기 때문이다. 진행지시기는 스플래쉬 화면이후 업데이트를 찾을 때 보인다. 사용자가(그리고 애플리케이션으로 인하여) 업데이트를 취소할 수도 있다. 그렇지 않고서는 업데이트에 개입할 수 없다. 애플리케이션을 실행하기 위해서, 사용자는 실행전에 업데이트를 찾을 때까지 그리고 찾은 업데이트를 설치할 때까지 기다려야만 할 것이다.
이 설정에서, p2 UI 클래스 라이브러리 번들(org.eclipse.equinox.p2.ui)은 전체가 필요하지 않다. 핵심코드만이 업데이트에 사용된다. 다음과 같은 변경이 필요하다.:
Activator 시작코드는 p2 기반 번들이 시작된 것을 보장해야만 한다. 그렇지 않으면, 업데이트를 체크할 때 요구되는 서비스를 찾지 못할 수도 있다.
public void start(BundleContext context) throws Exception {
super.start(context);
plugin = this;
bundleContext = context;
packageAdminRef = bundleContext.getServiceReference(PackageAdmin.class.getName());
packageAdmin = (PackageAdmin) bundleContext.getService(packageAdminRef);
// XXX start up the p2 infrastructure. Normally the p2 UI class library does
// this, but we are running without UI.
getBundle("org.eclipse.equinox.p2.exemplarysetup").start(Bundle.START_TRANSIENT); //$NON-NLS-1$
getBundle("org.eclipse.equinox.frameworkadmin.equinox").start(Bundle.START_TRANSIENT); //$NON-NLS-1$
getBundle("org.eclipse.equinox.simpleconfigurator.manipulator").start(Bundle.START_TRANSIENT); //$NON-NLS-1$
}
애플리케이션을 시작할 때 위 코드가 실행되는 것을 보장하기 위해서, 예제 번들 자신은 애플리케이션을 실행할 때 반드시 시작되어야만 한다. 이는 예제를 어떻게 실행하느냐에 따라서, 여러가지 방법으로 명시된다.:
번들 시작 레벨을 실행(launch) 환경으로 설정한다.
번들 시작 레벨을 프러덕트 편집기의 환경설정 탭에서 설정한다.(빌드를 위한 .product 파일)(releng 프로젝트 예에서 필요했던...)
이미 빌드된 앱(app)의 config.ini에서 번들이 시작되도록 한다.
ApplicationWorkbenchAdvisor는 preStartup() 메소드를 정의해야만 한다.
public void preStartup() {
// XXX check for updates before starting up.
// If an update is performed, restart.
if (P2Util.checkForUpdates()) {
PlatformUI.getWorkbench().restart();
}
}