티스토리 툴바


Amazon Linux AMI에서 Apache2.2 + Tomcat7 연동

2012/02/02 17:24 | Posted by 포데브(엄지사랑) 엄지사랑
: 서버구축환경

OS: Amazon Linux AMI release 2011.09 <== CentOS 계열(5.x)
WS: Apache/2.2.21 (Unix)
WAS: tomcat-7.0.22


: Apache2(httpd) 설치

$ sudo yum install httpd


: 설치확인

$ chkconfig  --list | grep httpd
httpd           0:off   1:off   2:off   3:off   4:off   5:off   6:off
$ apachectl -v
Server version: Apache/2.2.21 (Unix)
Server built:   Oct 26 2011 22:12:40


: 설치정보

$ cd /etc/httpd
$ ll
total 8
drwxr-xr-x 2 root root 4096 Feb  2 05:33 conf
drwxr-xr-x 2 root root 4096 Feb  2 05:33 conf.d
lrwxrwxrwx 1 root root   19 Feb  2 05:33 logs -> ../../var/log/httpd
lrwxrwxrwx 1 root root   29 Feb  2 05:33 modules -> ../../usr/lib64/httpd/modules
lrwxrwxrwx 1 root root   19 Feb  2 05:33 run -> ../../var/run/httpd


: apache 실행

$ sudo /usr/sbin/apachectl start
$ netstat -lntp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address               Foreign Address             State
tcp        0      0 0.0.0.0:22                  0.0.0.0:*                   LISTEN
tcp        0      0 127.0.0.1:25                0.0.0.0:*                   LISTEN
tcp        0      0 :::80                       :::*                        LISTEN
tcp        0      0 :::8080                     :::*                        LISTEN
tcp        0      0 :::22                       :::*                        LISTEN
tcp        0      0 ::ffff:127.0.0.1:8005       :::*                        LISTEN
tcp        0      0 :::8009                     :::*                        LISTEN


: apache2.2 + tomcat7 연동 설정(mod_proxy_proxy)

> 참고: http://bugcide.blogspot.com/2011/07/centos-apache-tomcat-yum.html

> /etc/httpd/conf/httpd.conf 에 mod_proxy 활성화 되어있는지 확인 (기본 설치값은 활성화되어 있음)
> * LoadModule proxy_module modules/mod_proxy.so
> conf/httpd.conf 에 주석처리된 mod_proxy.c 부분을 설정해도 좋지만
> /etc/httpd/conf.d/ 에 tomcat.conf 파일을 새로 생성함


$ sudo vi /etc/httpd/conf.d/tomcat.conf

<IfModule mod_proxy.c>

ProxyRequests off

ProxyPass / http://localhost:8080/
ProxyPassReverse / http://localhost:8080/

<IfModule mod_disk_cache.c>
   CacheEnable disk /
   CacheRoot "/var/cache/mod_proxy"
</IfModule>

</IfModule>


$ sudo /usr/sbin/apachectl restart 


끝.
저작자 표시
크리에이티브 커먼즈 라이선스
Creative Commons License

Amazon Linux AMI에 대하여...

2012/02/01 13:48 | Posted by 포데브(엄지사랑) 엄지사랑
AWS 에서 사용하고 있는 OS는 Amazon Linux AMI 입니다.
Amazon Linux AMI는 CentOS를 기반으로 하고 있으며
CentOS는 RedHat Enterprise Linux AS 기반의 리눅스 배포판입니다.

참고: 
http://centos.pe.kr/owiki/index.php?url=centos
http://www.jopenbusiness.com/mediawiki/index.php/CentOS

또한 Amazon Linux AMI User Guide 문서에는 아래와 같은 내용이 있습니다.

Is the Amazon Linux AMI compatible with other Linux distributions?
The Amazon Linux AMI is very similar to CentOS in file system layout and package management. 
Applications built for CentOS 5 and later should run without recompilation of the Amazon Linux AMI. 
However, the Amazon Linux AMI is configured for use only in Amazon EC2.

Does the Amazon Linux AMI support third-party independent software vendor (ISV) applications?
Yes. Most ISV applications that are designed to run on CentOS 5 or later will run on the Amazon Linux AMI. 
However, the Amazon Linux AMI has not gone through any formal certification efforts with ISVs. 
If you have a specific question about whether an ISV will officially support running its application on the Amazon Linux AMI, 
please contact the ISV directly.


즉, Amazon Linux AMI는 CentOS와 호환되며 
ISV들에 의해 CentOS 5 또는 이후 버전에서 동작하는 것들은 Amazon Linux AMI에서 동작될 것이나
ISV들과 공식적인 인증 노력을 하지 않기 때문에 ISV 들의 애플리케이션이 지원하는지 여부는 직접 문의해라.

그리고 해당 OS 버전을 확인할때는 아래와 같이 하되 정확한 CentOS 버전을 확인할 방법은 없어 보입니다.

$ cat /etc/issue
Amazon Linux AMI release 2011.09

$ cat /etc/image-id 
image_name="amzn-ami"
image_version="2011.09"
image_arch="x86_64"
image_file="amzn-ami-2011.09.2.x86_64.ext4"
image_stamp="46f7-fb98"
image_date="20111027225247"
recipe_name="amzn ami"
recipe_id="2aaca5dd-b34f-4a46-8281-d2471ce38631"

저작자 표시
크리에이티브 커먼즈 라이선스
Creative Commons License

apache2 + tomcat7 연동

2012/01/31 19:46 | Posted by 포데브(엄지사랑) 엄지사랑
제목과 같이 구글링을 하여 설치하고 설정했을 때 아래와 같은 에러를 만났다.

ForbiddenYou don't have permission to access / on this server. 

이 문제의 원인을 디렉토리의 퍼미션 문제라고 많은 곳에서 언급하고 있으나 디렉토리 모드가 755로 되어 있으므로 이 문제는 아닌것 같다.
아래 두 사이트로 추정컨데 아마도 proxy_http  모듈이 로드되지 않아서 발생했던 문제인것 같다. 

http://techbug.tistory.com/195 
http://blog.naver.com/PostView.nhn?blogId=zukjimote&logNo=42304917 

그리고 기타 참고할 만한 사이트의 링크를 남긴다.(나중에 내가 참고하기 위해서... ^^)
http://httpd.apache.org/docs/2.2/mod/mod_proxy.html
http://blog.daum.net/donfig/3163746 
http://blog.naver.com/PostView.nhn?blogId=junix&logNo=80130084062 
http://theeye.pe.kr/entry/Apache-integrated-Tomcat-with-ProxyPass-options
http://jo.centis1504.net/?p=102
: 환경

Ubuntu 10.04.3 LTS
apache2 (2.2.14-5ubuntu8.7)
apache-tomcat-7.0.22


: 아파치 설치

$ sudo apt-get install apache2


: 설치된 apache2 버전 확인

$ apache2 -v
Server version: Apache/2.2.14 (Ubuntu)
Server built:   Nov  3 2011 03:31:27


: 연동에 필요한 아파치 모듈

proxy
proxy_ajp
proxy_html
proxy_http

$ sudo apt-get install libapache2-mod-proxy-html
$ sudo a2enmod proxy_ajp
$ sudo a2enmod proxy_html
$ sudo a2enmod proxy_http

Run '/etc/init.d/apache2 restart' to activate new configuration!

$ ll /etc/apache2/mods-enabled/ | grep proxy
proxy.conf -> ../mods-available/proxy.conf
proxy.load -> ../mods-available/proxy.load
proxy_ajp.load -> ../mods-available/proxy_ajp.load
proxy_html.conf -> ../mods-available/proxy_html.conf
proxy_html.load -> ../mods-available/proxy_html.load
proxy_http.load -> ../mods-available/proxy_http.load


: virtual host 설정

$ sudo vi default
 
<VirtualHost *:80>
        ServerAdmin webmaster@localhost
        ServerName cardbook.com
        ServerAlias cardbook.com *.cardbook.com

        ProxyRequests Off
        ProxyPreserveHost On
        <Proxy *>
                Order deny,allow
                Allow from all
        </Proxy>
        ProxyPass / http://localhost:8080/
        ProxyPassReverse / http://localhost:8080/

        ErrorLog /var/log/apache2/error.log

        # Possible values include: debug, info, notice, warn, error, crit,
        # alert, emerg.
        LogLevel warn

        CustomLog /var/log/apache2/access.log combined

</VirtualHost>

$ sudo /etc/init.d/apache2 restart  


끝.
저작자 표시
크리에이티브 커먼즈 라이선스
Creative Commons License
Ref: http://dev.mysql.com/doc/refman/5.0/en/resetting-permissions.html#resetting-permissions-unix 
 
아래와 같이 mysql을 중지 시킨후 권한체크를 하지 않는 모드로 실행한 다음 비밀번호를 재설정하고 재실한한다.

$ sudo service mysql stop (or sudo /etc/init.d/mysql stop)
$ sudo mysqld --skip-grant-tables --skip-networking &
$ mysql
mysql> UPDATE mysql.user SET Password=PASSWORD('NewPassword') WHERE User='root';
mysql> FLUSH PRIVILEGES;
mysql> exit

$ ps -ef | grep mysqld
$ kill -9 pid
$ sudo service mysql start (or sudo /etc/init.d/mysql start)


P.S: 처리하고 난 후 비밀번호 생각하느라 이것저것 해본걸 생각하면 좀 억울한 생각마저 든다. 역시 아는만큼 보이는 것 같다. 
 
저작자 표시
크리에이티브 커먼즈 라이선스
Creative Commons License

Amazon EC2 / S3 mount process

2011/12/21 03:48 | Posted by 포데브(엄지사랑) 엄지사랑
목표: Amazon EC2 (Amazon Linux AMI 64) 와 S3 mount 연동
환경: Amazon Linux AMI 64는 CentOS 기반으로 만들어 졌음을 추정.
http://www.cloudave.com/4872/open-source-and-cloud-computing-the-amazon-linux-ami-is-now-available/

Step 1: 아래 URL을 참조하여 mount 할 수 있는 기술 중 S3FS를 적용하기로 결정
http://code.google.com/p/s3ql/wiki/other_s3_filesystems

※ 처음에 S3QL을 적용하였으나 실제 마운트시 에러가 발생하였고 이를 해결하지 못해서 S3FS로 변경함.

Step 2: S3FS 설치 절차

01. sudo yum remove fuse fuse* fuse-devel
02. sudo yum install gcc libstdc++-devel gcc-c++ curl curl* curl-devel libxml2 libxml2* libxml2-devel openssl-devel mailcap
03. mkdir ~/installed-tools/s3fs-tmp
04. cd ~/installed-tools/s3fs-tmp

05. wget http://sourceforge.net/projects/fuse/files/fuse-2.X/2.8.6/fuse-02.8.6.tar.gz/download
06. tar xf fuse-2.8.6.tar.gz
07. cd fuse-2.8.6
08. ./configure --prefix=/usr
09.  make
10. sudo make install

11. export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/lib64/pkgconfig/
12. sudo ldconfig
13. sudo modprobe fuse
14. pkg-config --modversion fuse (confirm that 2.8.4 is the version displayed)
15. cd ../

16. wget http://s3fs.googlecode.com/files/s3fs-1.61.tar.gz
17. tar xf  s3fs-1.61.tar.gz
18. cd s3fs-1.61
19. ./configure --prefix=/usr
20. make
21. sudo make install

22. sudo vi /etc/passwd-s3fs
23. accessKeyId:secretAccessKey or bucketName:accessKeyId:secretAccessKey 
24. sudo chmod 640 /etc/passwd-s3fs
25. sudo cp  /etc/passwd-s3fs ~/.passwd-s3fs
26. sudo chown ec2-user:ec2-user ~/.passwd-s3fs
27. chmod 600  ~/.passwd-s3fs

28. sudo mkdir /mnt/cardbook-contents
29. sudo chown ec2-user:ec2-user /mnt/cardbook-contents
29. /usr/bin/s3fs cardbook /mnt/cardbook-contents


Ref:
http://code.google.com/p/s3fs/wiki/FuseOverAmazon
http://code.google.com/p/s3fs/issues/detail?id=170
저작자 표시
크리에이티브 커먼즈 라이선스
Creative Commons License

locate and updatedb on Amazon Linux(CentOS)

2011/12/21 00:32 | Posted by 포데브(엄지사랑) 엄지사랑
Ref.: http://www.beguelin.com/2009/05/locate-and-updatedb-on-centos.html

리눅스에서 apt-get, yum, rpm 등을 사용하여 소프트웨어를 설치한 경우 어디에 설치가 되어있는지 대략난감할때가 있다.

보통 명령어의 존재를 확인할 때는 which command 를 사용하지만 이는 어디까지나 명령어의 위치만을 알려줄 뿐 실제 설치된 위치를 알려주는 것은 아니다. 더구나 리눅스는 심볼릭링크 등을 제공하기 때문에 실제 설치 위치는 더욱 알기 어렵다.

그래서 설치위치를 알려주는 유용한 명령어를 소개하고자 한다.

sudo yum install mlocate
sudo /etc/cron.daily/mlocate.cron


첫번째 문장을 실행하면 locate와 updatedb 명령어를 설치하며 하루에 한번 디스크를 리인텍싱하는 크론을 설정한다.
두번째 문장을 실행하면 크론을 실행하여 인덱스를 즉시 생성한다.
저작자 표시
크리에이티브 커먼즈 라이선스
Creative Commons License

MSSQL 서버 2008 - 트랜잭션 로그 줄이기

2011/08/11 11:16 | Posted by 포데브(엄지사랑) 엄지사랑
참고: http://ottoradke.com/2008/11/04/microsoft-sql-server-2008-tip-1-purge-transaction-log/

트랜잭션 로그를 줄이는 방법을 검색해보면 대략 다음의 두가지 방법을 찾을 수 있다.

1. 데이터베이스 복구모델을 simple로 변경하는 방법
2. 트랜잭션 로그를 백업하고 로그파일 크기를 줄이는 방법

2번 방법에 대해서는 다음과 같이 수행한다.

2.1 로그파일 백업
backup log DatabaseName to disk='YourPath\YourFilename.trn'
2.2 로그파일 크기 줄임
dbcc shrinkfile('logfilename', 1024)


※ DatabaseName, logfilename 은 해당 DB 정보를 확인
※ YourPath, YourFilename 은 백업할 위치 및 파일명을 입력

그런데 이 방법을 사용하게 되면 한번에 잘될때도 있지만 사용이 빈번한 DB의 경우 한번에 줄어들지 않고 여러번 반복해야 원하는 크기로 줄일 수 있다.

다른 포스트와 다르게 참고 URL을 상단에 둔 이유가 바로 여기에 있다.
Otto R. Radke  라는 분의 블로그 인데 소개글이 재밌다.
Developer & designer. Americano drinker, tennis player. Goal: be simple, passionate & resourceful.

------------------------------------------------------------------------------

-- Otto R. Radke - http://ottoradke.com

-- Info: T-SQL script to shrink a database's transaction log. Just set the

-- database name below and run the script and it will shrink the

-- transaction log.

------------------------------------------------------------------------------

------------------------------------------------------------------------------

-- Update the line below with the name of the database who's transaction

-- log you want to shrink.

------------------------------------------------------------------------------

USE YourDatabaseName

------------------------------------------------------------------------------

-- Don't change anything below this line.

------------------------------------------------------------------------------

GO

-- Declare variables

DECLARE @SqlStatement as nvarchar(max)

DECLARE @LogFileLogicalName as sysname

-- Alter the database to simple recovery

SET @SqlStatement = 'ALTER DATABASE ' + DB_NAME() + ' SET RECOVERY SIMPLE'

EXEC ( @SqlStatement )

-- Make sure it has been altered

SELECT [name], [recovery_model_desc] FROM sys.databases WHERE [name] = DB_NAME()

-- Set the log file name variable

SELECT @LogFileLogicalName = [Name] FROM sys.database_files WHERE type = 1

-- Shrink the logfile

DBCC Shrinkfile(@LogFileLogicalName, 1)

-- Alter the database back to FULL

SET @SqlStatement = 'ALTER DATABASE ' + DB_NAME() + ' SET RECOVERY FULL'

EXEC ( @SqlStatement )

-- Make sure it has been changed back to full

SET @SqlStatement = 'SELECT [name], [recovery_model_desc] FROM ' + DB_NAME() + '.sys.databases WHERE [name] = ''' + DB_NAME() + ''''

EXEC ( @SqlStatement )

------------------------------------------------------------------------------


실행해 보면 1M의 크기로 로그파일을 줄여준다. 너무 고마워서 해당 블로그에 답글도 쓰고 이렇게 국내에도 소개한다. 물론 검색을 잘하면 걸리기야 하겠지만..  ^^;

저작자 표시
크리에이티브 커먼즈 라이선스
Creative Commons License

프로젝트 추정과 계획의 실패는 누구의 몫인가?

2011/07/14 17:09 | Posted by 포데브(엄지사랑) 엄지사랑
최근 프로젝트(SI, 연구성과제, 회사내부 등)의 대부분을 애자일 방법(스크럼)으로 진행해 왔다. 요구사항에 해당하는 제품백로그를 수집하고 스프린트백로그를 도출하여 스프린트를 계획하고 일일회의와 스프린트 종료회고 등을 통해 좀더 나은 방향으로 프로젝트를 이끌고자 했다.

언젠가 강의 중에 수강생이 이런 질문을 했다.
"애자일을 기간과 비용, 개발범위가 정해져 있는 SI 프로젝트에도 적용할 수 있나요?"
나의 대답은 당당하게 "네! 부분적으로 고객의 이해를 전제하지 않더라도 적용이 가능합니다." 라고 대답을 했다. 그런데 지금은...

지금 진행하고 있는 프로젝트는 "시장적시성(Time to Market)"이 가장 중요하였으며, 그렇기 때문에 구현하고자 하는 기능의 정의와 더불어 추정과 계획이 정말 중요했다. 고객사는 6개월전부터 이 프로젝트를 진행해왔으나 개발업체가 프로젝트 진행도중 중단을 선언하는 등 프로젝트 진행에 어려움이 많이 있었다. 고객사는 그간 투입된 6개월 동안의 실적으로 요구사항 분석 및 UI설계를 완료된 것으로 판단하였고 그 산출물을 받아서 프로젝트를 계속 진행할 수 있는 개발업체를 찾았다. 그래서 우리회사가 이 프로젝트에 참여하게 되었다.

당시까지 진행된 상황과 고객사의 설명을 들으니 처음 접하는 도메인영역임에도 그렇게 어려워 보이지 않았다. 그래서 요구사항 수집이나 요구사항 분석같은 것은 프로젝트를 진행하면서 점차 범위를 넓혀가자고 고객과 합의를 했다. 추출된 기능목록(제품백로그)과 고객사가 생각하고 있는 서비스일정을 고려해보니 대략 3명이 3개월(9 M/M)이라는 추정치가 나오게 되었다. 그런데 최초 업무정의에 대한 내용을 들었을때 단순 DB에 접근하여 데이터를 보여주는 것으로 착각(?)하는 실수를 범한 것이다.

부사장님이 소프트웨어 개발을 비유한 안개고지전투(http://vizend.tistory.com/38)가 불현듯 생각났다. 

소프트웨어 개발은 안개고지전투(Foggy Hills Battle)다. 
    - 고지는 요구사항 그룹이다. 어떤 산이든 작은 산으로 이루어지듯 요구사항도 마찬가지이다.  
    - 안개 자욱하여 앞이 보이지 않듯이 우리의 요구사항은 언제나 안개 속이다.  
    - 하나의 고지를 점령하면 다음 고지가 더 잘 보일 뿐만 아니라, 경험을 활용할 수 있다. 
    - 고지는 언제나 서로  다르며, 지도는 단순한 참조일 뿐이다.
    - 고지로 이르는 길은 고지를 점령하고 의미가 없다. 고지만이 의미가 있다.

프로젝트를 진행하면서 고객의 업무설명을 좀더 깊이있게 이해할수록 "어~~어~~"라는 말이 절로 나왔다. 1주일 걸릴거라고 예상한 것은 2주가 걸렸고 2주가 예상된 것은 4~5주가 걸렸다. 어떤 프로젝트이든 위험요소는 늘 있기마련이다. 그런데 아무리 처음 접하는 도메인이라 하더라도 이렇게 심각하게 과소추정되는 경우는 찾아보기 힘들다.(물론 연구성과제는 한치앞도 보이지 않는 안개속인 경우가 많고 이보다 더 심각한 추정이 나올 수 있으나 연구성과제라는 특징을 모두 인식하므로 크게 문제시 되지 않는듯 하다.) 이제 재추정은 선택이 아닌 필수가 되었다.

먼저 시장적시성을 만족하기 위해서는 시스템오픈을 위한 개발범위와 개발우선순위를 재조정해야 하고 완전한 프로젝트 종료를 위해서는 기간과 비용을 재조정해야만 한다. 이를 위해서 개발팀과 나는 비교적 타당한 추정근거를 제시하기 위하여 스토리점수와 이상적 작업시간 개념을 도입하고 사용자기능에 대해서 고객과 함께 세부적인 추정을 했다. 그 결과는 아주 놀라웠다. 9M/M 이 지난 시점에서 잔여 사용자기능 추정치가 10.5 ~ 14M/M 이 나왔고 관리자 기능도 4 ~ 6M/M이나 나온 것이다. 여기에 관리자 기능은 좀더 추가가 예상되고 있어서 플러스알파의 기간이 더해져야만 한다.[M/M(Man/Month)에 대한 표기는 이해를 돕기위한 것이고 실제로는 스토리점수로 작업규모를 추정하고 이상적작업시간으로 작업기간을 추정했다)

추정과 관련해서 [불확실성과 화해하는 프로젝트 추정과 계획](http://insightbook.springnote.com/pages/1717328) 이라는 책은 다음과 같은 내용을 다루고 있다.

  • 전통적인 규범적 계획법이 실패하는 이유와 애자일 계획법이 먹히는 이유
  • 스토리 점수와 이상적 작업일을 사용해 기능 규모를 추정하는 방법과 그 장단점
  • 재 추정 방법 및 재 추정이 필요한 시기
  • 기능 우선순위를 결정하는 재정적/비 재정적 접근법들
  • 큰 기능을 보다 손쉽게 관리할 수 있게 작은 단위로 분할하는 방법
  • 이터레이션 계획 방법 및 팀의 초기 진도 예측 방법
  • 불확실성이나 일정 관련 위험성이 지나치게 높은 프로젝트의 일정을 계획하는 방법
  • 여러 팀이 함께 작업하는 프로젝트를 추정하는 방법

  • 이제 이 글의 주제인 [프로젝트 추정과 계획의 실패는 누구의 몫인가?]를 생각해 보자.

    "[요구사항 수집이나 요구사항 분석같은 것은 프로젝트를 진행하면서 점차 범위를 넓혀가자고 고객과 합의]를 했다." 

    프로젝트를 진행하는 개발팀 및 개발사의 입장에서 이 말의 의미는 "처음 식별되지 않은 요구사항에 대해서는 추가적인 기간과 비용이 발생할 수 있습니다." 였다. 그런데 고객은 비용에 대한 부분은 고려하지 않고 기간에 대한 부분만을 인식했던것 같다. 즉, 납기를 초과할 수 있다는 것에만 동의했다는 의미이다. 결국 프로젝트 추정과 계획의 실패에 있어 비용은 개발팀 및 개발사가 떠안아야 한다는 결론이다.
    이렇게 개발사에게 프로젝트 추정과 계획의 실패를 떠넘기게 되면 개발사는 프로젝트 초반에 아무리 힘들더라도 요구사항을 모두 확정지어서 가려고 할 것이고 추정에도 상당한 시간을 들여 진행할 것이다. 더욱 문제가 되는 것은 요구사항 변경이나 추가에 대해서 개발사는 상당히 비협조적일 것이다.

    다시 한번 [안개고지전투]가 생각난다. 고객의 입장에서 아주 먼곳에 있는 최종 목적지를 가려고 할때 바로 앞의 안개속 고지를 점령해가면서 점령방향과 점령방법을 판단하면 길을 잃지도 않을 뿐더러 보다 효율적으로  점령해 나갈 수 있지 않을까?

    [고객의 이해를 전제하지 않더라도 애자일 적용가능]이라는 말에 회의가 들었다. 애자일 방법 자체는 적용할 수 있겠으나 추가비용을 고려하지 않는 고객과 과연 요구사항 변경이나 추가를 논의할 수 있을까? 추가비용을 고려하지 않고 해결할 수 있는 방법은 꼭 필요한 요구사항 변경과 추가에 들어가는 기간과 비용만큼 다른 기능(개발범위)을 축소하는 것이다. 그런데 고객은 이마저도 하지 않으려고 한다.

    甲乙(갑을)관계를 들이대며 다음 먹거리(프로젝트)를 무기로 프로젝트 추정과 계획의 실패를 모두 개발사에게 책임을 묻는 것은 분명 옳지 않을 것이다. 물론 개발사에게 전혀 책임이 없고 100% 고객사에게만 책임을 묻는 것도 분명 옳지 않다. 마치 주행중 접촉사고에 대한 과실정도를 판가름하여 책임을 묻는 것과 같이 어느 한쪽에만 책임을 묻기보다는 쌍방과실을 인정하고 합의하여 프로젝트를 성공으로 이끄는 것이 중요할 것이다.

    - 포데브(ForDev)
    저작자 표시
    크리에이티브 커먼즈 라이선스
    Creative Commons License

    Spring + JPA + Hibernate + Multiple DataSource 설정

    2011/05/23 14:40 | Posted by 포데브(엄지사랑) 엄지사랑
    개발환경: 
    - Spring 3.0.5.RELEASE
    - JPA 2.0
    - Hibernate 3.6.2.Final
    - DBMS: MSSQL 2008 R2(10.50.1600.1)

    JPA환경에서 두개이상의 DataSource를 사용하기 위해서 EntityManagerFactory를 두개이상 생성하려고 하면 

    Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [javax.persistence.EntityManagerFactory] is defined: expected single bean but found 2
    ...


    와 같은 에러가 발생한다.

    구글링을 해보면 이 문제에 대한 문의와 토론은 많이 있지만 딱히 해결안이 없어 보인다.
    포기하고 iBATIS로 이 문제를 풀려고 했는데 마지막에 시도해본 방법이 성공을 하여 공유하고자 정리한다.

    방법을 제공해준 사이트는 아래와 같다.
    소스코드: svn checkout http://gwt-spring-jpa-lucene.googlecode.com/svn/com.intre.open.gwtjpa

    단, 이때 트랜잭션은 2PC 상황이므로 다중트랜잭션에 대한 문제는 별도 처리를 해줘야 한다.
    기회가 되면 다중트랜잰션에 대한 내용도 정리할 생각이다. WAS를 사용하지 않는한 JOTM과 같은 트랜잭션 관리자가 필요하다.

    먼저 다음과 같은 Spring applicationContext.xml을 작성한다.

    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance

      xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"

      xmlns:p="http://www.springframework.org/schema/p"

      xsi:schemaLocation="

      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd

      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

      <!-- context:annotation-config도 등록함 -->

      <context:component-scan base-package="kr.hibid.collector" />

      <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

      <bean id="hibidPum" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">

        <property name="persistenceXmlLocations">

          <list>

            <value>/test-coll-persistence.xml</value>

            <value>/test-trans-persistence.xml</value>

          </list>

        </property>

        <property name="dataSources">

          <map>

            <entry key="hibidCollDS" value-ref="hibidCollDS" />

            <entry key="hibidTransDS" value-ref="hibidTransDS" />

          </map>

        </property>

        <property name="defaultDataSource" ref="hibidCollDS" />

      </bean>

      <!-- ===================================================================== -->

      <!-- Collection                                                            -->

      <!-- ===================================================================== -->

      <bean id="hibidCollDS" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

        <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />

        <property name="url" value="jdbc:sqlserver://localhost:1433;DatabaseName=HibidCollDev" />

        <!-- <property name="connectionProperties" value="charset=eucksc;" /> -->

        <property name="username" value="hibid_coll_dev" />

        <property name="password" value="hibid_coll_dev" />

      </bean>

      <bean id="hibidCollEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

        <property name="persistenceUnitManager" ref="hibidPum" />

        <property name="persistenceUnitName" value="hibidCollUnit" />

        <property name="loadTimeWeaver">

          <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />

        </property>

        <property name="jpaDialect">

          <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />

        </property>

      </bean>

      <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">

        <property name="entityManagerFactory" ref="hibidCollEmf" />

        <property name="jpaDialect">

          <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />

        </property>

      </bean>

      <tx:annotation-driven transaction-manager="transactionManager" />

      <!-- ===================================================================== -->

      <!-- Transference                                                          -->

      <!-- ===================================================================== -->

      <bean id="hibidTransDS" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

        <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />

        <property name="url" value="jdbc:sqlserver://localhost:1433;DatabaseName=HibidTest" />

        <!-- <property name="connectionProperties" value="charset=eucksc;" /> -->

        <property name="username" value="hibid_test" />

        <property name="password" value="hibid_test" />

      </bean>

      <bean id="hibidTransEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

        <property name="persistenceUnitManager" ref="hibidPum" />

        <property name="persistenceUnitName" value="hibidTransUnit" />

        <property name="loadTimeWeaver">

          <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />

        </property>

        <property name="jpaDialect">

          <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />

        </property>

      </bean>

      <bean id="hibidTransTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">

        <property name="entityManagerFactory" ref="hibidTransEmf" />

        <property name="jpaDialect">

          <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />

        </property>

      </bean>

      <tx:annotation-driven transaction-manager="hibidTransTxManager" />

    </beans>


    /test-coll-persistence.xml

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

    <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

      xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

      <persistence-unit name="hibidCollUnit" transaction-type="RESOURCE_LOCAL">

        <provider>org.hibernate.ejb.HibernatePersistence</provider>

        <non-jta-data-source>hibidCollDS</non-jta-data-source>

        <!-- Entity Classes Define Start ======================================= -->

        <class>kr.hibid.collector.domain.notify.PpsBiddingBasisAmount</class>

        <!-- Entity Classes Define End ======================================= -->

        <exclude-unlisted-classes />

        <properties>

          <property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect" />

          <property name="hibernate.archive.autodetection" value="false"/>

          <property name="hibernate.transaction.auto_close_session" value="false"/>

          <property name="hibernate.search.default.directory_provider" value="org.hibernate.search.store.FSDirectoryProvider"/>

          <property name="hibernate.search.default.indexBase" value="lucene-first"/>

          <property name="hibernate.search.default.locking_strategy" value="none"/>

          <property name="hibernate.show_sql" value="true" />

          <property name="hibernate.format_sql" value="true" />

          <property name="hibernate.use_sql_comments" value="false" />

          <property name="hibernate.hbm2ddl.auto" value="update" />

        </properties>

      </persistence-unit>

    </persistence>


      /test-trans-persistence.xml

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

    <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

      xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

      <persistence-unit name="hibidTransUnit" transaction-type="RESOURCE_LOCAL">

        <provider>org.hibernate.ejb.HibernatePersistence</provider>

        <non-jta-data-source>hibidTransDS</non-jta-data-source>

        <!-- Entity Classes Define Start ======================================= -->

        <class>kr.hibid.common.domain.BizType</class>

        <!-- Entity Classes Define End ======================================= -->

        <exclude-unlisted-classes />

        <properties>

          <property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect" />

          <property name="hibernate.archive.autodetection" value="false"/>

          <property name="hibernate.transaction.auto_close_session" value="false"/>

          <property name="hibernate.search.default.directory_provider" value="org.hibernate.search.store.FSDirectoryProvider"/>

          <property name="hibernate.search.default.indexBase" value="lucene-first"/>

          <property name="hibernate.search.default.locking_strategy" value="none"/>

          <property name="hibernate.show_sql" value="true" />

          <property name="hibernate.format_sql" value="true" />

          <property name="hibernate.use_sql_comments" value="false" />

          <property name="hibernate.hbm2ddl.auto" value="update" />

        </properties>

      </persistence-unit>

    </persistence>

      

    PpsTransServiceTest.java

    @RunWith(SpringJUnit4ClassRunner.class)

    @ContextConfiguration(locations = { "/test-collector-context.xml" })

    @Transactional(propagation = Propagation.SUPPORTS)

    @TransactionConfiguration(defaultRollback = true)

    public class PpsTransServiceTest {

        @Autowired

        private PpsCollectionEao collectionEao;

        @Autowired

        private PpsTransEao transEao;

        @Test

        public void testDI() {

            assertNotNull(this.collectionEao);

            assertNotNull(this.transEao);

            List<BizType> bizTypes = this.transEao.findBizTypes();

            assertNotNull(bizTypes);

            List<PpsFacilityBiddingNotify> facilityBiddingNotifies = this.collectionEao.findFacilityBiddingNotifies("20110522120000");

            assertNotNull(facilityBiddingNotifies);

            List<PpsBiddingBasisAmount> biddingBasisAmounts = this.collectionEao.findBiddingBasisAmounts("20110522120000");

            assertNotNull(biddingBasisAmounts);

        }

    }

     

    PpsCollectionEao.java

    @Repository

    @Transactional(propagation = Propagation.SUPPORTS)

    public class PpsCollectionEao {

        @PersistenceContext(unitName = "hibidCollUnit")

        private EntityManager em;

        public List<PpsFacilityBiddingNotify> findFacilityBiddingNotifies(String baseDateTime) {

            // QL 작성

            StringBuilder qlString = new StringBuilder(200);

            qlString.append("SELECT pfbn FROM PpsFacilityBiddingNotify pfbn ");

            qlString.append("WHERE pfbn.collectDateTime is null ");

            qlString.append("  AND pfbn.transDateTime <= :baseDateTime ");

            // Query 실행 및 결과 반환

            TypedQuery<PpsFacilityBiddingNotify> query = this.em.createQuery(qlString.toString(), PpsFacilityBiddingNotify.class);

            query.setParameter("baseDateTime", baseDateTime);

            List<PpsFacilityBiddingNotify> results = query.getResultList();

            return results;

        }

        public List<PpsBiddingBasisAmount> findBiddingBasisAmounts(String baseDateTime) {

            // QL 작성

            StringBuilder qlString = new StringBuilder(200);

            qlString.append("select pbba from PpsBiddingBasisAmount pbba ");

            qlString.append("WHERE pbba.collectDateTime is null ");

            qlString.append("  AND pbba.transDateTime <= :baseDateTime ");

            // Query 실행 및 결과 반환

            TypedQuery<PpsBiddingBasisAmount> query = this.em.createQuery(qlString.toString(), PpsBiddingBasisAmount.class);

            query.setParameter("baseDateTime", baseDateTime);

            List<PpsBiddingBasisAmount> results = query.getResultList();

            return results;

        }

    }

     

    PpsTransEao.java

    @Repository

    @Transactional(value = "hibidTransTxManager", propagation = Propagation.SUPPORTS)

    public class PpsTransEao {

        @PersistenceContext(unitName = "hibidTransUnit")

        private EntityManager em;

        public List<BizType> findBizTypes() {

            List<BizType> results = this.em.createQuery("select bt from BizType bt", BizType.class).setMaxResults(10).getResultList();

            return results;

        }

    }  

    저작자 표시
    크리에이티브 커먼즈 라이선스
    Creative Commons License

    Eclipse + GAE class hot deploy

    2011/05/20 17:23 | Posted by 포데브(엄지사랑) 엄지사랑
    개발환경: JDK1.6.0_24 + Eclipse Helios SR2 + appengine-java-sdk-1.5.0 + Googel App Engine for Eclipe 

    서블릿 클래스를 아무리 변경해도 반영이 안되고 서버를 재시작해야 반영이 되었다.
    분명 클래스의 변경은 서버 재시작과 상관이 없다고 했거늘....

    그런데 서버를 시작하는 방법이 이상했다. 많은 곳에서 서버를 시작할 때 [Debug As]로 실행하는 것이 아닌가? 나는 Debug는 안할 거니 [Run As]로 실행했다. 

    이런! 바로 이 차이가 서블릿 클래스의 변화를 감지하지 못하는 원인이 었다.
    즉, Debug As로 서버를 시작해야 서블릿 클래스 등 클래스의 변화를 감지하여 서버 재시작과 무관하게 되는 것이 었다. 

    결론: GAE는 Debug As로 실행시켜라!

    저작자 표시
    크리에이티브 커먼즈 라이선스
    Creative Commons License
    이전 1 2 3 4 5 ... 7 다음