질문/답변 페이지 많이 이용해 주세요.

Archive for 6월 2010

Apache와 OC4J 연동하기.

Oracle Application Server는 크게 Oracle HTTP Server(Apache MOD)와 OC4J(Oracle Container for J2EE)로 구성되어 있습니다.

그런데, OC4J의 경우 단독으로도 사용할 수 있도록 Standalone 버전으로 Oracle에서 제공하고 있습니다.

만약, 서버를 저와 같이 PHP나 다른 Apache의 기능을 사용하고, OAS를 사용할 수 없는 환경에서 Apache와 OC4J를 연동하여 사용하는 방법을 알려드립니다.(물론 이미 알고 있는 분도 계시겠지만.)

먼저, Apache와 OC4J의 연동은 OAS 에서는 mod_oc4j 라는 모듈을 통해 처리합니다.
그러나, mod_oc4j는 OAS에 맞도록 작성되어 단독 Apache에 쓰기에는 무리가 있습니다.

다음으로, AJP를 통해 OC4J와 연동하는 것이 가능합니다.
그러나, 톰캣에서 제공하는 AJP 모듈과 OC4J모듈의 연동에서는 알 수 없는 문제가 발생했었습니다.

다음은, proxy 모듈을 사용하는 방법입니다.(이 방법을 소개합니다.)

저의 경우 Apache 2.2 버전에서 제공되는 proxy 모듈로 사용하고 있습니다.

LoadModule proxy_module /usr/lib/apache2/modules/mod_proxy.so
LoadModule proxy_http_module /usr/lib/apache2/modules/mod_proxy_http.so

위와 같이 모듈이 활성화 된 상태에서 아래와 같이 설정하여 OC4J와 연동이 가능합니다.

ProxyPass /ical2bi http://localhost:8888/ical2bi

위 설정은 Apache 웹서버에 /ical2bi 형태로 접근할 경우, OC4J의 /ical2bi 로 요청을 넘겨줍니다.

이 방법의 경우, 일반적으로 잘 돌아가지만, redirect 등과 같이 서버 주소를 참조하는 경우에는 정상 동작하지 않는 경우도 발생할 수 있습니다.

proxy 모듈을 통해서 저는 OC4J의 예제만 보여드렸습니다만, OC4J 뿐 아닌 다른 WAS나 심지어 다른 웹서버까지 요청을 전달할 수 있습니다.

저는 하나의 서버에 여러개의 애플리케이션을 돌리고, 각각 접근하는 URL에 따라 애플리케이션을 구분하여 서비스하고 있습니다.

ps. 제 서버에는 개발에 사용된 언어가 PHP, Python, Java, CGI(Perl) 입니다. 이중 Java부분을 OC4J로 처리하고 있습니다.

Tomboy + ( Snowy | Ubuntu One ) 메모짱

오늘은 Tomboy라는 메모장 프로그램을 소개합니다.

Tomboy Icon Tomboy Logo

http://projects.gnome.org/tomboy/

리눅스 GNOME 프로젝트로 만들어진 프로그램입니다.

저는 atnote, 한컴노트 등의 메모용 프로그램을 사용해 봤습니다.

그 프로그램들은 윈도우 창 모양 등을 최소화하고, 바탕화면 등에 배치해서 사용하기에 좋게 만들어진 것으로 보입니다.

그러나, Tomboy는 그와는 달리 창 모양도 갖추고 있으며, 심지어 툴바까지 있어서 바탕화면에 띄워둘 만한 프로그램은 아닙니다.

강점으로는 위키와 같이 페이지간 링크을 줄 수 있다는 점입니다.

또한, 백링크도 되며, 검색도 쓸만하게 만들어져 있어서 좋습니다.

아래는 윈도우용 설치에 대한 링크입니다.

http://live.gnome.org/Tomboy/Win32

추가로 소개해 드릴 것은, Snowy라는 프로젝트와 Ubuntu One서비스입니다.

Snowy는 Tomboy노트를 웹에 싱크해주는 프로그램입니다. 아직은 웹에서 조회만 가능하며 작성은 불가능합니다.

http://live.gnome.org/Snowy

설치형 버전을 사용을 원하시면, Snowy를 자체 서버에 설치하고 사용하시면되고,

Ubuntu One이란 서비스를 사용하셔도 되겠습니다. ( Ubuntu One의 경우 Ubuntu를 기준으로 Tomboy 와 추가 동기화를 제공합니다. )

https://one.ubuntu.com/

ps. 예전에는 Freemind를 통해서 구조화를 했었고, notepad로 디렉토리에 txt파일 형태로도 저장해 봤습니다. 이런 방식의 경우 검색이 가장 문제가 되었습니다. Microsoft의 Onenote의 경우 자체 저장 포맷을 사용하여 사용하지 않는 사람입니다.

OpenVPN으로 가상사설망 구축하기

OpenVPN으로 가상 사설망 구축이 가능합니다.

http://openvpn.net/

1. OpenVPN 서버 설치

$ sudo apt-get install openvpn

openvpn용 dh파일 만들기

$ openssl dhparam -out dh1024.pem 1024

openvpn 서버용 설정파일 만들기

port 1194
proto udp
dev tun0

ca ca.crt
cert server.crt
key server.key

dh dh1024.pem

server 10.8.0.0 255.255.255.0

ifconfig-pool-persist ipp.txt

push "route 168.126.63.1 255.255.255.255"
push "route 168.126.63.2 255.255.255.255"

client-to-client

keepalive 10 120

comp-lzo

persist-key
persist-tun

status openvpn-status.log

verb 3

위 구성은 아래 내용으로 요약합니다.

  • 클라이언트가 접속할 포트는 udp 1194 입니다.
  • 10.8.0.0/24 를 클라이언트에게 할당할 IP 풀로 사용합니다.
  • 클라이언트가 168.126.63.1, 168.126.63.2 로 접근할 때, VPN 접속을 사용하도록 합니다.
  • 클라이언트간 통신이 가능합니다.

서버에서는 매스커레이딩 혹은 라우팅 설정이 있어야 정상적으로 사용할 수 있습니다. ( 10.8.0.0/24의 사설 IP풀을 사용하므로 )

2. 클라이언트 사용하기

아래 URL에서 클라이언트 프로그램을 받을 수 있습니다. 윈도우에서 사용하는 GUI인터페이스를 제공하는 클라이언트입니다.

http://openvpn.se/

설치후, 클라이언트용 설정을 아래와 같이 생성합니다.

client
dev tun
proto udp

remote vpn-server.dgkim.net 1194

nobind

persist-key
persist-tun

ca cacert.pem
cert dgkimcert.pem
key dgkimkey.pem

verb 3
comp-lzo

Openssl로 사설인증기관 만들기

Openssl을 사용하여 사설인증기관 및 사설인증 서비스를 구축할 수 있습니다.

인증기관 준비하기.

1. 인증기관용 디렉토리 생성.

아래와 같은 구조로 생성합니다.

./dgkim.net # 인증기관 디렉토리
./dgkim.net/certs # 인증서 저장 디렉토리
./dgkim.net/crl # CRL 저장 디렉토리
./dgkim.net/crl.pem # CRL 파일 - 빈파일
./dgkim.net/crlnumber # CRL 넘버 파일 - 초기 00 입력
./dgkim.net/index.txt # 인증서 데이터베이스 인덱스 파일 - 빈파일
./dgkim.net/index.txt.attr # 빈파일
./dgkim.net/newcerts # 신규 발급된 인증서 저장 디렉토리
./dgkim.net/private # 개인키 디렉토리
./dgkim.net/serial # 인증서 시리얼넘버 파일 - 초기 00 입력

2. openssl.cnf 생성

아래는 제가 사용하는 설정파일입니다.

HOME                    = .
RANDFILE                = $ENV::HOME/.rnd

[ ca ]
default_ca      = CA_default            # The default ca section

[ CA_default ]

dir             = ./dgkim.net           # Where everything is kept
certs           = $dir/certs            # Where the issued certs are kept
crl_dir         = $dir/crl              # Where the issued crl are kept
database        = $dir/index.txt        # database index file.
#unique_subject = no                    # Set to 'no' to allow creation of
                                        # several ctificates with same subject.
new_certs_dir   = $dir/newcerts         # default place for new certs.

certificate     = $dir/cacert.pem       # The CA certificate
serial          = $dir/serial           # The current serial number
crlnumber       = $dir/crlnumber        # the current crl number
                                        # must be commented out to leave a V1 CRL
crl             = $dir/crl.pem          # The current CRL
private_key     = $dir/private/cakey.pem# The private key
RANDFILE        = $dir/private/.rand    # private random number file

x509_extensions = usr_cert              # The extentions to add to the cert

# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt        = ca_default            # Subject Name options
cert_opt        = ca_default            # Certificate field options

default_days    = 365                   # how long to certify for
default_crl_days= 30                    # how long before next CRL
default_md      = sha1                  # which md to use.
preserve        = no                    # keep passed DN ordering

policy          = policy_match

[ policy_match ]
countryName             = match
stateOrProvinceName     = match
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ policy_anything ]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ req ]
default_bits            = 2048
default_keyfile         = privkey.pem
distinguished_name      = req_distinguished_name
attributes              = req_attributes
x509_extensions = v3_ca # The extentions to add to the self signed cert

string_mask = default

[ req_distinguished_name ]
countryName                     = Country Name (2 letter code)
countryName_default             = KR
countryName_min                 = 2
countryName_max                 = 2

stateOrProvinceName             = State or Province Name (full name)
stateOrProvinceName_default     = Daegu

localityName                    = Locality Name (eg, city)

0.organizationName              = Organization Name (eg, company)
0.organizationName_default      = dgkim.net

# we can do this but it is not needed normally
#1.organizationName             = Second Organization Name (eg, company)
#1.organizationName_default     = World Wide Web Pty Ltd

organizationalUnitName          = Organizational Unit Name (eg, section)
#organizationalUnitName_default =

commonName                      = Common Name (eg, YOUR name)
commonName_max                  = 64

emailAddress                    = Email Address
emailAddress_max                = 64

[ req_attributes ]
challengePassword               = A challenge password
challengePassword_min           = 4
challengePassword_max           = 20

unstructuredName                = An optional company name

[ usr_cert ]

basicConstraints=CA:FALSE

nsComment                       = "OpenSSL Generated Certificate"

subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer

crlDistributionPoints=@cdp_section

nsCaRevocationUrl               = ldap://home1.dgkim.net:389/cn=arldp1,ou=CA,o=dgkim.net,st=Daegu,c=KR
nsRevocationUrl                 = ldap://home1.dgkim.net:389/cn=crldp1,ou=CA,o=dgkim.net,st=Daegu,c=KR

[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment

[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
basicConstraints = CA:true

[ crl_ext ]
authorityKeyIdentifier=keyid:always,issuer:always

[ proxy_cert_ext ]
basicConstraints=CA:FALSE
nsComment                       = "OpenSSL Generated Certificate"
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer:always

[crldp1_section]

[cdp_section]
URI=ldap://home1.dgkim.net:389/cn=crldp1,ou=CA,o=dgkim.net,st=Daegu,c=KR

DN 값에 대한 설정과, CRL DP에 대한 설정을 조정하여 사용합니다.

3. 인증기관 인증서 생성

아래 스크립트와 같이 생성합니다.

$ openssl req -x509 -out dgkim.net/cacert.pem -keyout dgkim.net/private/cakey.pem -config openssl.cnf -new -days 3650
Generating a 2048 bit RSA private key
...+++
.....+++
writing new private key to 'dgkim.net/private/cakey.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [KR]:
State or Province Name (full name) [Daegu]:
Locality Name (eg, city) []:
Organization Name (eg, company) [dgkim.net]:
Organizational Unit Name (eg, section) []:
Common Name (eg, YOUR name) []:dgkim.net CA
Email Address []:

이 과정에서 인증기관 인증서와 인증기관 개인키가 생성됩니다.

4. 개인 인증서(혹은 서버인증서) 발급 신청

아래 명령으로 발급 신청을 생성할 수 있습니다.

$ openssl req -new -keyout dgkim/key.pem -out dgkim/req.pem -config openssl.cnf
Generating a 2048 bit RSA private key
................+++
.................................................................+++
writing new private key to 'dgkim\key.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [KR]:
State or Province Name (full name) [Daegu]:
Locality Name (eg, city) []:
Organization Name (eg, company) [dgkim.net]:
Organizational Unit Name (eg, section) []:
Common Name (eg, YOUR name) []:Deoggon Kim
Email Address []:dgkim@dgkim.net

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

5. 인증기관에서 인증서 발급하기.

4번 단계에서 생성한 인증서발급 요청은 아래 스크립트를 통해 인증서 발급이 이뤄 집니다.

$ openssl ca -config openssl.cnf -in dgkim/req.pem -out dgkim/cert.pem
Using configuration from openssl.cnf
Enter pass phrase for ./dgkim.net/private/cakey.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 0 (0x0)
        Validity
            Not Before: Jun 18 06:37:25 2010 GMT
            Not After : Jun 18 06:37:25 2011 GMT
        Subject:
            countryName               = KR
            stateOrProvinceName       = Daegu
            organizationName          = dgkim.net
            commonName                = Deoggon Kim
            emailAddress              = dgkim@dgkim.net
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                06:BE:86:57:69:43:30:3D:15:CA:C2:B9:85:CB:5C:34:0D:CD:D2:D5
            X509v3 Authority Key Identifier:
                keyid:72:B5:1E:14:A7:E2:CE:D1:D9:79:0D:01:1E:3D:2D:82:26:48:2B:07

            X509v3 CRL Distribution Points:
                URI:ldap://home1.dgkim.net:389/cn=crldp1,ou=CA,o=dgkim.net,st=Daegu,c=KR

            Netscape CA Revocation Url:
                ldap://home1.dgkim.net:389/cn=arldp1,ou=CA,o=dgkim.net,st=Daegu,c=KR
            Netscape Revocation Url:
                ldap://home1.dgkim.net:389/cn=crldp1,ou=CA,o=dgkim.net,st=Daegu,c=KR
Certificate is to be certified until Jun 18 06:37:25 2011 GMT (365 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

이상 간단하게 인증기관 생성 및 인증서 발급을 하여 보았습니다.

Java properties 에서 문자 치환하기.

저는 아래와 같은 properties 파일을 만들고,

UNKNOWNERROR=알 수 없는 오류입니다.(ERRORCODE)

아래와 같은 코드로 ERRORCODE를 대체해 왔습니다.

package com.idatabank.sso.exception;

import java.util.ResourceBundle;

public class SSOException extends Exception {
    public SSOException() {
        super();
    }
    
    public SSOException(String errorType) {
        super(errorType);
        
        userMessage = rb.getString(errorType);
        
        if ( userMessage == null ) {
            userMessage = rb.getString("UNKNOWNERROR").replaceAll("ERRORCODE", errorType);
        }
    }
    
    /**
     * 사용자 에러 메시지를 세팅한다.
     * @param userMessage
     */
    public void setUserMessage(String userMessage) {
        this.userMessage = userMessage;
    }
    
    /**
     * 사용자 에러 메시지를 반환한다.
     * @return
     */
    public String getUserMessage() {
        return userMessage;
    }
    
    private String userMessage = null;
    private static final ResourceBundle rb = ResourceBundle.getBundle("com.idatabank.sso.exception.Messages");
}

그런데, 오늘 문자열 치환의 새로운 방법을 찾았습니다.

http.method_not_implemented=Method {0} is not defined in RFC 2068 and is not supported by the Servlet API 
      String errMsg = rb.getString("http.method_not_implemented");
      Object[] errArgs = new Object[1];
      errArgs[0] = method;
      errMsg = java.text.MessageFormat.format(errMsg, errArgs);

이 방법이 좀더 우아해 보입니다.

Java 서블릿 필터 활용하기

Java 웹애플리케이션에서는 servlet filter라는 기능이 있습니다.

저는 주로, servlet filter를 통해 권한 체크를 수행합니다.

servlet filter의 경우 servlet 이나 jsp 등이 수행되기 전에 사전 처리를 하거나, 처리후 사후 처리를 하는데 활용할 수 있습니다.

servlet filter는 web.xml에서 정의합니다.

   <filter>
        <filter-name>AuthFilter</filter-name>
        <filter-class>test.AuthFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AuthFilter</filter-name>
        <url-pattern>/*.do</url-pattern>
    </filter-mapping>

위 필터의 경우 test.AuthFilter 클래스를 필터로 등록했으며,
/*.do URL에 대한 접근에 대해서 필터 처리를 수행합니다.

서블릿 필터의 구현은 아래와 같이 정의합니다.

package test;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class AuthFilter implements Filter {
    private FilterConfig _filterConfig = null;

    public void init(FilterConfig filterConfig) throws ServletException {
        _filterConfig = filterConfig;
    }

    public void destroy() {
        _filterConfig = null;
    }

    public void doFilter(ServletRequest request, ServletResponse response, 
                         FilterChain chain) throws IOException, ServletException {
        // 사전권한 체크 수행
        chain.doFilter(request, response);
        // 사후 처리 수행
    }
}

위와 같이 javax.servlet.Filter 인터페이스를 구현하여 정의합니다.

doFilter 에서 사전권한 체크 수행 부분에서 서블릿 수행전에 수행할 작업들을 정의할 수 있습니다.

그리고, 만약 여러개의 필터가 사용되고 있다면(filter는 여러개를 사용할 수 있습니다.) chain.doFilter에서 다음으로 정의된 filter의 doFilter를 수행합니다.
그리고, 마지막으로 서블릿 컨테이너가 다른 filter가 없을 경우 서블릿으로 처리를 넘기게 됩니다.

그리고, chain.doFilter 이후에, 추가적으로 처리할 작업이 있으면 사후 처리 수행 부분에서 처리할 수 있습니다.

그리고, 현재 가장 많이 사용되는 것이 characterset filter입니다.

java에서는 encoding의 변환이 발생하는 경우가 많습니다. ( ex. os charset과 웹 charset이 다른 경우 주로 getBytes를 통한 문자 변환을 사용 )

아래는 스프링프레임워크에서 자체 제공하는 servlet filter의 사용 예입니다.

    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>EUC-KR</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

필터도 역시 목적에 따라 잘 활용할 경우 유용하게 활용할 수 있습니다.

편리한 LDAP 프로그램

LDAP 관리에 사용하는 프로그램 소개

1. Softerra LDAP Browser
http://www.ldapbrowser.com/
가장 먼저 사용한 툴입니다.
단, LDAP Browser 만 다운로드 받아 사용할 수 있습니다.
물론, LDAP Browser는 검색만 가능하고 입력 수정이 불가능합니다.
검색에서는 아래 나오는 툴보다 훨씬 빠르게 작업할 수 있습니다.

2. LDAP Browser Editor
Jarek Gawor분이 만든 Java버전 LDAP Editor입니다.
현재는 업데이트가 되지 않는 것으로 보입니다.
(Apache Directory Studio를 만나기 전에는 이 툴을 사용했었습니다.)

3. phpLDAPadmin
http://phpldapadmin.sourceforge.net/wiki/index.php/Main_Page
php로 만들어졌고, 웹기반으로 사용할 수 있습니다.
최근에는 Ajax로 보강되었으나, Frame을 사용하므로 Ajax가 완전하지는 않아 보입니다.
웹에서 할 수 있다는 것은 장점이나, 일부 속성을 다루지 못하는 문제가 있습니다.

4. Apache Directory Studio
http://directory.apache.org/studio/
현재 제가 사용하고 있는 툴입니다.(최근에 발견했기 때문에 마지막 순번으로 등록하였습니다.)
Eclipse RCP로 만들어졌으며(?), 사용하기 편리합니다.
단, 편의성에 비하여 퍼포먼스는 만족할 만한 수준은 아닙니다.
( ex. excel 시트형으로 펴두고, 일괄 수정 작업시 체감속도가 느림. )

Trac + SVN 을 사용하여 협업하기

개발업무를 하면, 당연히 소스코드를 관리할 필요가 있습니다.
그래서 일반적으로 CVS SVN GIT … 등의 소스 버전 관리툴을 사용합니다.
하지만 잘못 사용하고 계신 사용자 분도 계십니다. ( ex. 커밋시점 등. 테스트가 완료되지 않은 코드를 커밋하여 테스트. )

저는 trac 이란 툴과 SVN을 사용하여 협업을 하고 있습니다.

SVN에서 소스코드를 관리해 주고, Trac을 통해 타임라인 확인(히스토리), wiki 기능을 통한 문서 및 지침사항 정리, ticket을 통한 이슈 사항 관리를 하고 있습니다.

저는 현재 trac의 레포지토리 관리를 제 업무특성상, 고객사별로 레포지토리를 만들어 사용중입니다. 차후 버전에서는 멀티레포지토리를 지원한다고 하는데, 정확하게 어떻게 되는지는 모릅니다.

리눅스 서버를 하나가지고 계시면, 이번에 Trac + SVN 의 협업 개발환경을 사용해 보시는 것은 어떨까 권해드립니다.

trac 홈페이지 : http://trac.edgewall.org/

저는 현재, 소스코드 뿐 아닌 노출하지 말아야할 정보도 trac으로 관리하다 보니 인터넷에는 공개를 못하고, 사내에서만 사용합니다.

Java jarcheck.jsp로 클래스 파일 찾기

Java 웹애플리케이션에서 클래스 파일의 실제 위치를 찾을 때 사용하는 유용한 jsp
jarcheck.jsp

    String reqName = null;
    java.net.URL classUrl = null;
    reqName = request.getParameter("reqName");
    if (reqName == null || reqName.trim().length() == 0) {
        reqName = "javax.servlet.http.HttpServlet";
    }
    if (reqName.trim().length() != 0) {
 	reqName = reqName.replace('.', '/').trim();
	reqName = "/" + reqName + ".class";
        classUrl = this.getClass().getResource(reqName);
        if (classUrl == null) {
            out.println(reqName + " not found");
        } else {
            out.println("" + reqName + ": [" + classUrl.getFile() + "]\n" );
        }
        out.println("
"); }

소스의 출처/원본은 javaservice.net 입니다.

JSP 에러 페이지

JSP 페이지에서 errorPage 로 사용할 수 있는 페이지입니다.

<%@ page contentType="text/html;charset=EUC-KR" isErrorPage="true"
         import="java.io.CharArrayWriter, java.io.PrintWriter"%>
    
    <%
      if (exception != null) 
      { 
        out.println(exception.getMessage());
        CharArrayWriter charArrayWriter = new CharArrayWriter(); 
        PrintWriter printWriter = new PrintWriter(charArrayWriter, true); 
        exception.printStackTrace(printWriter); 
        out.println(charArrayWriter.toString()); 
      } 
    %>
    

Oracle JDeveloper 10g 에서 기본으로 제공하는 페이지 샘플을 가져옴.