Showing posts with label 톰켓. Show all posts
Showing posts with label 톰켓. Show all posts

2009-09-14

톰켓 6.0 + JPA

톰켓 6.0에서 JPA를 사용하는 방법이다.

우선 톰켓 6.0 깨끗하게 하기를 참고하여 톰켓을 설치하고, 톰켓 6.0 데이터소스 설정를 참고하여 아파치 더비를 데이터소스로 설정한다.


JPA 구현부로는 오라클이 제공하는 TopLink Essentials을 다운로드한다.

다운로드한 glassfish-persistence-installer-v2-b41.jar 파일이 있는 디렉토리로 이동하여 다음 명령어를 실행한다. 자바가 사용할 수 있어야 한다.

java -jar glassfish-persistence-installer-v2-b41.jar


아래 화면에서 스크롤을 맨 아래까지 내린 다음에 Accept 버튼을 누른다.



압축이 풀리면서 디렉토리가 생기는데 다음 2 파일을 webapps/ROOT/WEB-INF/lib 디렉토리에 복사한다.

  • toplink-essentials.jar
  • toplink-essentials-agent.jar

그리고 webapps/ROOT/WEB-INF/classes/META-INF/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="ExampleUnit" transaction-type="JTA">
    <provider>oracle.toplink.essentials.PersistenceProvider</provider>
    <jta-data-source>java:comp/env/jdbc/Base</jta-data-source>
    <class>com.dimdol.example.model.Document</class>
    <class>com.dimdol.example.model.User</class>
    <class>com.dimdol.example.model.Element</class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <properties>
      <!--
      <property name="toplink.ddl-generation" value="create-tables" />
      -->
      <property name="toplink.session.customizer" value="com.dimdol.example.util.ToplinkSessionCustomizer" />
    </properties>
  </persistence-unit>
</persistence>

  • persistence-unit 태그 name 속성에 모델 그룹을 지칭하는 적절한 이름(ExampleUnit)을 설정한다.
  • provider 태그에 JPA 구현 클래스 이름(oracle.toplink.essentials.PersistenceProvider)을 설정한다.
  • jta-data-source 태그에 데이터소스 JNDI 이름(java:comp/env/jdbc/Base)을 설정한다.
  • class 태그로 모델 클래스들을 설정한다.

property 태그로 toplink.session.customizer 이름에 다음 클래스를 설정한다. 이렇게 해야 하는 정확한 이유는 알수 없지만 이렇게 하지 않으면 정상적으로 동작하지 않는다.

package com.dimdol.example.util;

import oracle.toplink.essentials.jndi.JNDIConnector;
import oracle.toplink.essentials.sessions.Session;
import oracle.toplink.essentials.tools.sessionconfiguration.SessionCustomizer;

public class ToplinkSessionCustomizer implements SessionCustomizer {

    public void customize(Session session) throws Exception {
        JNDIConnector connector = (JNDIConnector) session.getLogin().getConnector();
        connector.setLookupType(JNDIConnector.STRING_LOOKUP);
    }

}


톰켓 6.0 JSESSIONID 충돌

동일 도메인이나 IP에 포트 번호를 달리해서 여러 개의 자바 애플리케이션 서버를 운영하면 세션 유지를 위해 사용하는 JSESSIONID 쿠키가 충돌한다.

예를 들어 한 사용자가 동일 브라우저에서 이 애플리케이션들을 동시에 사용하면 계속해서 세션이 상실되는 현상이 발생한다.


웹로직같은 경우에는 JSESSIONID 대신에 다른 이름을 사용하도록 변경할 수 있지만 톰켓 5.5에서는 이를 지원하지 않았다. 문제 해결을 위해서 구글링하다가 톰켓 개발자가 쓴 글을 보고 톰켓 코드를 수정해서 사용한 적이 있다.


그런데 톰켓 6.0.20 버전에서는 그 내용이 그대로 반영되어서 코드 수정없이 JSESSIONID 대신에 다른 이름을 사용할 수 있다. (정확하게 톰켓 6.0 어떤 버전부터인지는 모르겠다)


다음 자바 시스템 환경 변수로 JSESSIONID를 수정한다.

-Dorg.apache.catalina.SESSION_COOKIE_NAME=JSESSIONID
-Dorg.apache.catalina.SESSION_PARAMETER_NAME=jsessionid
-Dorg.apache.catalina.authenticator.Constants.SSO_SESSION_COOKIE_NAME=JSESSIONIDSSO

  • 첫번째는 JSESSIONID 쿠키 이름이다.
  • 두번째는 URL Rewriting을 할 때 사용하는 파라미터 이름으로 톰켓에서는 JSESSIONID에 대한 소문자를 사용한다.
  • 세번째는 SSO 처리와 관련된 것으로 보이는데 사용해본 적이 없어서 자세히는 모르겠다.

따라서 위의 값에 대해서 JSESSIONID 대신에 다른 걸 사용하면 세션 충돌을 방지할 수 있다.



동일 도메인에서 서로 다른 자바 애플리케이션 서버가 같은 HTTP 포트 번호를 사용할 수는 없다. 그래서 JSESSIONID + HTTP 포트 번호 조합을 사용하는 것이 가장 바람직하다.


먼저 conf/server.xml 파일을 다음과 같이 수정한다.

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

<Server port="${http.shutdown.port}" shutdown="SHUTDOWN">

  <Service name="Catalina">

    <Connector port="${http.startup.port}" protocol="HTTP/1.1" connectionTimeout="20000"
      redirectPort="8443" URIEncoding="UTF-8"/>

    <!-- 나머지 부분 생략 -->

톰켓 설정 파일에 ${}로 설정하면 자동으로 자바 환경 변수로 치환된다.

  • http.startup.port - HTTP 시작 포트
  • http.shutdown.port - HTTP 종료를 위한 포트

윈도우에서는 bin/catalina.bat 파일 앞부분에 다음 내용을 추가한다.

if "%STARTUP_PORT%" == "" SET STARTUP_PORT=8080
if "%SHUTDOWN_PORT%" == "" SET SHUTDOWN_PORT=8005

set JAVA_OPTS=%JAVA_OPTS%
-Xmx512m
set JAVA_OPTS=%JAVA_OPTS% -Dhttp.startup.port=%STARTUP_PORT%
set JAVA_OPTS=%JAVA_OPTS% -Dhttp.shutdown.port=%SHUTDOWN_PORT%
set JAVA_OPTS=%JAVA_OPTS% -Dorg.apache.catalina.SESSION_COOKIE_NAME=JSESSIONID%STARTUP_PORT%
set JAVA_OPTS=%JAVA_OPTS% -Dorg.apache.catalina.SESSION_PARAMETER_NAME=jsessionid%STARTUP_PORT%
set JAVA_OPTS=%JAVA_OPTS% -Dorg.apache.catalina.authenticator.Constants.SSO_SESSION_COOKIE_NAME=JSESSIONIDSSO%STARTUP_PORT%


* 띄어쓰기 문제로 맨 마지막 줄이 잘려보인다.  =JSESSIONIDSSO%STARTUP_PORT%이 잘려 보이는 것이다.


유닉스/리눅스에서는 bin/catalina.sh 파일 앞부분에 다음 내용을 추가한다.

STARTUP_PORT="${STARTUP_PORT}"
SHUTDOWN_PORT="${SHUTDOWN_PORT}"

if [ "${STARTUP_PORT}" = "" ]
then
    STARTUP_PORT="7900"
fi

if [ "${SHUTDOWN_PORT}" = "" ]
then
    SHUTDOWN_PORT="7999"
fi

JAVA_OPTS="$JAVA_OPTS -Xmx512m"
JAVA_OPTS="$JAVA_OPTS -Dhttp.startup.port=${STARTUP_PORT}"
JAVA_OPTS="$JAVA_OPTS -Dhttp.shutdown.port=${SHUTDOWN_PORT}"
JAVA_OPTS="$JAVA_OPTS -Dorg.apache.catalina.SESSION_COOKIE_NAME=JSESSIONID${STARTUP_PORT}"
JAVA_OPTS="$JAVA_OPTS -Dorg.apache.catalina.SESSION_PARAMETER_NAME=jsessionid${STARTUP_PORT}"
JAVA_OPTS="$JAVA_OPTS -Dorg.apache.catalina.authenticator.Constants.SSO_SESSION_COOKIE_NAME=JSESSIONIDSSO${STARTUP_PORT}"

* 띄어쓰기가 없어서 맨 마지막 줄이 잘려보인다.  =JSESSIONIDSSO%STARTUP_PORT%이 잘려 보이는 것이다.


이렇게 설정하면 HTTP 포트 번호가 8080일때 JSESSIONID8080이 세션 유지를 위한 쿠키 이름으로 사용된다.

톰켓 6.0 데이터소스 설정

톰켓 6.0에 데이터소스를 설정하는 방법이다. 우선 톰켓 6.0 깨끗하게 하기를 참조한다.


conf/server.xml 파일에 데이터소스를 설정하고 JDBC 드라이버는 lib 디렉토리에 복사해야 한다.


아파치 더비

<Context ...>
  <Resource name="jdbc/Base" auth="Container"
    type="javax.sql.DataSource" maxActive="100" maxIdle="30"
    maxWait="10000" username="username" password="password"
    driverClassName="org.apache.derby.jdbc.EmbeddedDriver"
    url="jdbc:derby:db;create=true" />
</Context>

JDBC 드라이버 : derby.jar

* 외부에서도 아파치 더비에 접근하려면 derbynet.jar 파일도 복사해야 한다.


오라클


<Context ...>
  <Resource name="jdbc/Base" auth="Container"
    type="javax.sql.DataSource" maxActive="100" maxIdle="30"
    maxWait="10000" username="username" password="password"
    driverClassName="oracle.jdbc.driver.OracleDriver"
    url="jdbc:oracle:thin:@127.0.0.1:1521:db" />
</Context>

JDBC 드라이버 : ojdbc14.jar


DB2

<Context ...>
  <Resource name="jdbc/Base" auth="Container"
   type="javax.sql.DataSource" maxActive="100" maxIdle="30"
   maxWait="10000" username="username" password="password"
   driverClassName="com.ibm.db2.jcc.DB2Driver"
   url="jdbc:db2://127.0.0.1:50000/db" />
</Context>

JDBC 드라이버 : db2jcc.jar, db2jcc_license_cu.jar


MS SQL 2005


<Context ...>
  <Resource name="jdbc/Base" auth="Container"
   type="javax.sql.DataSource" maxActive="100" maxIdle="30"
   maxWait="10000" username="username" password="password"
   driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"
   url="jdbc:sqlserver://127.0.0.1:1433;databaseName=db" />
</Context>

JDBC 드라이버 : 자바 1.6을 사용하면 sqljdbc4.jar, 자바 1.5를 사용하면 sqljdbc.jar


web.xml 파일 수정

이 설정이 끝나면 web.xml 파일에 다음 내용을 추가한다.

<resource-ref>
  <description>Base DB Connection</description>
  <res-ref-name>jdbc/Base</res-ref-name>
  <res-type>javax.sql.DataSource</res-type>
  <res-auth>Container</res-auth>
  <res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>

* jsp-config 태그 다음에 추가한다. 일반적으로 거의 마지막에^^

톰켓 6.0 깨끗하게 하기

톰켓 6.0을 자주 사용하는데 불필요한 파일과 설정들이 많이 있다. 그래서 항상 다음과 같이 정리한다.


1. 다운로드

exe 파일이 아닌 zip이나 tar.gz 파일을 다운받은 후에 톰켓을 운영할 디렉토리에 압축을 푼다.


2. 지우기

다음 파일 혹은 디렉토리를 지운다.
  • conf/context.xml
  • conf/tomcat-users.xml
  • webapps/docs 디렉토리
  • webapps/examples 디렉토리
  • webapps/host-manager 디렉토리
  • webapps/manager 디렉토리
  • webapps/ROOT 디렉토리에서 WEB-INF/web.xml 파일을 제외한 나머지 파일을 모두 삭제한다.


3. conf/server.xml 파일 정리

취향이겠지만 난 주석을 좋아하지 않고, 애플리케이션 개발에 당장 필요하지 않은 설정은 일단 지우고 필요하면 다시 설정하는 쪽이다.

conf/server.xml 파일은 다음과 같이 정리한다.

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

<Server port="8005" shutdown="SHUTDOWN">

  <Service name="Catalina">

    <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000"
      redirectPort="8443" URIEncoding="UTF-8" />

    <Engine name="Catalina" defaultHost="localhost">
      <Host name="localhost" appBase="webapps"
            unpackWARs="true" autoDeploy="false"
            xmlValidation="false" xmlNamespaceAware="false">
        <Context docBase="${catalina.base}/webapps/ROOT" path="" reloadable="true" backgroundProcessorDelay="3">
        </Context>
      </Host>
    </Engine>

  </Service>
</Server>

  • 한글 인코딩 처리를 위해서 Connector 태그에 URIEncoding 속성을 지정했다
  • 톰켓 문서에서는 Context 설정은 별도 파일에 하는 것을 권장하지만 한두개의 애플리케이션을 배치할 때는 굳이 그럴 필요성을 느끼지 못한다. ROOT 디렉토리를 명시적으로 설정하였다. 개발시에는 Context 태그의 reloadable 속성을 true로 설정한다.


4. 로그 설정 정리

전에는 log4j를 선호했지만 지금은 자바 라이브러리를 줄이는 쪽에 가중치를 준다. 그래서 자바 표준 라이브러리에 있는 로그 모듈을 사용한다. 톰켓도 기본 설정이 그렇다.

conf/logging.properties 파일로 로그 설정을 하는데 관리 애플리케이션을 지웠기 때문에 이 파일에 그 내용을 반영해야 한다. 그렇지 않으면 무의미한 로그 파일이 만들어진다.

handlers = 1catalina.org.apache.juli.FileHandler, 2localhost.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler

.handlers = 1catalina.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler

1catalina.org.apache.juli.FileHandler.level = FINE
1catalina.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
1catalina.org.apache.juli.FileHandler.prefix = catalina.

2localhost.org.apache.juli.FileHandler.level = FINE
2localhost.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
2localhost.org.apache.juli.FileHandler.prefix = localhost.

java.util.logging.ConsoleHandler.level = FINE
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].handlers = 2localhost.org.apache.juli.FileHandler

2009-01-20

아파치 톰켓 6.0 한글 처리

아파치 톰켓 6.0에서 한글 처리 설정을 하는 방법은 다음과 같다. UTF-8을 사용하는 것으로 가정한다.


1. GET

${catalina.base}/conf/server.xml 파일에서 Connector 태그에 URIEncoding 속성을 설정한다.

<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000"
redirectPort="8443" URIEncoding="UTF-8"/>

${catalina.base}는 톰켓을 설치한 디렉토리를 의미한다.


2. POST

서블릿 필터를 이용해서 javax.servlet.ServletRequest 객체의 setCharacterEncoding 메소드를 호출한다.

request.setCharacterEncoding("UTF-8");

* ServletRequest 객체의 getParameter 메소드를 호출하기 전에 setCharacterEncoding 메소드를 호출해야 한다.


3. Content Type


javax.servlet.ServletResponse 객체의 setContentType 메소드로 content-type을 지정한다.

response.setContentType("text/html; charset=UTF-8");

JSP에서 page 선언부로 설정할 수도 있다.

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>

JSP 컴파일과 관련된 pageEncoding 속성도 설정한다.


4. HTML

<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>

meta 태그는 title 태그 앞에 있어야 한다.


5. 참고 자료