2013년 9월 6일 금요일

Register SSH Pub Key to Github

Github SSH Pub Key 등록


Generate SSH Key

$ ssh-keygen -t rsa -C "your@email.com"

Copy generated RSA Key to Clipboard

$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC3JEUABSWccJt7FhwrtwU5cdWr50Q+Cm80NDEY8CaxF3Vc0sQeqzHYPJLpWowTTRgebeoUgQrluROSWC342WxsSps6cNIlUM0ssl7498xpOZPBVx
... 중략 ...
JX0ZZRxwtSJnBdiuXX2zi1prw2AVsO/X8jIg59jeGJZbIqD3R+SvK5kWe3acPKuj2mdryhIEngpvMvJkAX5d your@email.com

github Site Sign-in

  • Account Setting > SSH Keys Menu
  • Add SSH Key
  • Title: your@email.com
  • Key: Paste your copied rsa pub key

Check Registered SSH Key on Github

$ git -T git@github.com
Warning: Permanently added the RSA host key for IP address '192.30.252.129' to the list of known hosts.
PTY allocation request failed on channel 0
Hi {yourName}! You've successfully authenticated, but GitHub does not provide shell access.
Connection to github.com closed.
* If you see this sentence then you get successful to register SSH Key.

Clone Your Repository

$ git clone git@github.com:/yourID/repositoryPath.git

* NOW You can just pulling without authentication ! :)




2013년 7월 11일 목요일

GCM의 주요 개발자 기능


About GCM

용어 정의


  • GCM
    • Google Cloud Messaging for Android

GCM Overview

Google Cloud Messaging for Android(GCM)는(은) 개발자가 서버에서 Android Device에 있는
Android Application으로 Data를 전송하거나, 사용자의 Device에서 Cloud로 Upstream Message를 전송할 수 있도록 돕는 무료 서비스 이다.

Key Developer Features

Send data from your server to users' Android-powered devices

Send-to-Sync Message

e-mail이나 매번 Update되는 마지막 점수를 Sync하는 Application 처럼 Sync를 위한 Message가 수신될 때
이들은 서로 연관된 종류의 Message끼리 묶여 Collapse되어 관리된다.
그로인해 새로운 메일을 동시에 여러 개 받거나 점수가 연속적으로 Update되었을 때
마지막에 최종적으로 수신된 Message로 인한 Noti.가 발생한다는 것이다.

이는 임의로 발급된 "collepse_key"라는 Parameter를 사용해 관리되며,
Device가 꺼져있다든가하는 등의 경우 Message Collepsing을 위한 Group을 구성하는데 사용된다.

GCM은 최대 4개의 서로 다른 collepse_key를 사용할 수 있도록 허용한다.
즉 동시에 4개의 서로 다른 Send-to-Sync Message만 허용한다는 뜻이며,
4개를 초과할 경우 GCM은 어떤 메세지가 될지 보장할 수 없지만 딱 4개만 유지한다는 것이다.

Send message with payload

Send-to_Sync와 달리 매 "message with payload"(non-collapsible message) 마다 전달되며
Message의 최대 크기는 4KB이다.
아래 예는 경기관중들이 어떤 스포츠 경기에 대해 대화 중인 IM Application에서 사용하는
JSON-formatted 메세지이다.


{
  "registration_id" : "APA91bHun4MxP5egoKMwt2KZFBaFUH-1RYqx...",
  "data" : {
    "Nick" : "Mario",
    "Text" : "great match!",
    "Room" : "PortugalVSDenmark",
  },
}

이 "message with payload"는 Mobile Application이 Server의 상태를 알기위해 접속하는 단순한 "ping"이 아니다.

앞서 소개한 IM Application의 경우, 매번 보내는 대화 내용이 다르기 때문에 모든 Message가 전달되어야 할 것이다.
non-collapsible message를 명시할 때는 그냥 "collapse_key"를 생략하면된다.
그러면 GCM은 매번 Message를 개별적으로 전달할 것이다.(단, 전달 순서는 보장할 수 없다.)

GCM는 100개의 non-collapsible message를 저장할 수 있어 이 후 전달되는 모든 Message는 버려지게 된다.
그래서 100개 이후 몇 개의 Message가 버려졌는지를 Client에게 보내게 되며,
다음을 포함하는 정규 com.google.android.c2dm.intent.RECEIVE intent를 통해 전달된다.

  • message_type
    • String Type의 "deleted_message"라는 값만 갖는다.
  • total_deleted
    • String Type의 삭제된 Message의 수
따라서 Application은 Server와의 Syncing을 통해 삭제된 Message에 대한 대응을 해야한다.

Which should I use?



non-collapsible message가 요구되지 않는다면, Performance 관점에서
Device의 베터리 부담을 적게 주는 collapsible message를 사용하는 쪽이 좀더 효과적이다.


2013년 4월 4일 목요일

What is Architecture...


우리는 보통 개발을 하는 과정에서 여러가지 일을 한다.
Project를 관리 작업과 구현을 위한 개발 작업, 그리고 Issue와 Risk 관리, 비용산정 등
크게 나누어도 적지않은 할일들이 있고 또 지금 하려는 얘기의 대상이되는 개발작업을 좀더 나워보면
요구사항 분석, 설계, 설계를 바탕으로 한 구체적인 구현계획 및 설계 등과 같이 역시 적지않은 작업들로 나뉜다.
서론이 길었는데, 하고자하는 얘기를 하자면,
개발 작업에 있어 Architecture는 결코 관가할 수 없는 중요한 작업이라는 것이다.
많은 개발자들이 설계작업을 그리 중요시 하지않거나 불필요한 작업정도로 치부하는 경우가 많다.
이유로는 대부분, "그럴 시간이 없다", "개발하는데 시간이 너무 올래 걸려", "꼭 해야 해?", "그건 규모가 있는 곳 에서나.."등 들어봄직한 말 들일 것이다.
하지만 절대 그렇지 않다. 절대!

Architecture라는 것은 이런 것 이이다..라고 생각하는 몇 가지 예를 들고자 한다.
하나, 우리가 인생을 살아가는 데 있어 크게 두 가지로 많으들 얘기한다. "짧고 굵게"와 "길고 가늘게".
이것의 선택은 각자의 인생관에 따라 결정될 것이고 그에 따라 인생을 그렇게 살아가거나 그렇게 살고자 의지를 가질 것이다.
하나, 길을 찾아갈 때 출발지에서 목적지로 가는 길은 하나만 있지는 않을 것이다. 하지만 최단거리 경로 또는 최단시간 경로는 있을 것이고
이 또한 그 길을 찾아가는 사람의 목적이나 상황에 따라 결정될 것이다.
하나, 도로에는 신호등을 포함해 도로의 상태나 방향 등을 알리기위한 목적의 표지판이 있다는 것을 잘 알고 있을 것이다.
이 신호등이나 표지판은 전 세계가 함께쓰는 하나의 Protocol이다. 그 덕에 우리는 어디를 가든 운전을 하는데 별 어려움이 없다.
그렇다, 이 처럼 Architecture는 Project가 갖는 성격이나 특성에 따라 방향이 결정되고,
Project의 목적에 맞게 다듬어지며, Project 참여자들이 함께 공유하며 함께 작업할 수 있도록 해주는
최소한의 일정한 규칙이 있는 것이다.
그 것이 Architecture인 것이다.
적어도 나는 그것이 Architecture이고 이의 역할이며 목적이라고 생각한다.

위에서 예로 들지는 않았지만 많이들 건물과 같은 실체를 만들기위한 설계에 많이 비유를 하는데
그것만으로 Software에 있어서의 Architecture를 설명하기에는 너무나 식상하고 뻔한 소리같아 조금 다르게 접근해 보았다.
하지만 이 비유는 매우 잘된 비유에 속하며 절대 식상하지 않은 것이다.
개발 작업을 하다보면 여러차례 요구사항이 바뀌고 그에 따라 Code를 수정하는 작업들이 빈번하게 이루어지고
지금도 그 작업을 하기위해 밤을 세우는 개발자들이 많을 것이다.
건물 얘기를 했으니 건물에 비유해 한 번 생각해 보자.
처음의 요구사항은 1층 건물을 짖는 것이었는데 갑자기 2층짜리 건물로 요구사항이 바뀌었다고 생각해보자.
그럼 건물의 높이가 높아지는 만큼 땅도 더 깊이 파야하고, 2층으로 올리기위해 보의 강도도 높여야하고,
층간 소음, 등 고려해야할 것들이 생기고 그에 따른 추가적인 작업이 요구되는 것 처럼,
Software 개발에서도 같은 일이 마찬가지로 발생한다.
"이럴 때 Architecture가 이런 문제들을 쉽게해결해 주기 때문에 Architecture가 반드시 필요하다."라는 말을 하려고 이런 얘길하는 것은 절대 아니다. 의외로 적지않은 사람들이 오해를 하거나 잘 못된 기대를 하는 부분이기도 하다.
하지만 여기서 Architecture의 역할과 목적은 저런 문제를 쉽게 해결하기위한 것이 아니라
올바르게, 정확히는 Project의 특성과 목적에 맞게 진행될 수 있도록 기준을 잡아준다는 것이다.
어떠한 개발 작업이든 개발자가 단 한 번의 Code 작성으로 완성되는 Code는 없다.
이는 오류로 인한 것이든, 요구사항의 변경으로 인한 것이든 여러 외적 요인으로 인해 변경되는데,
그 때마다 정해진 규칙없이 Code가 작성된다면, 그리고 한 사람이 아닌 여러 사람이 그렇게 작성한 Code는
Code의 owner만 알 수 있는 구조와 Logic을 갖게되며 그나마도 얼마간의 시간이 지나면 Owner 역시 당시 자신이 왜 그렇게 Code를 작성했는지 모른다고한다.
부끄럽지만 이것이 현재의 우리네 개발환경의 현실이다.
다음이 없다. 고도화라는 이름을 붙여 Project를 진행하지만 속을 들여다보면 처음부터 다시 같은 동작을 하는 Code를 다른 개발자가 작성하고있는 것 뿐이다.
슬프게도 말이다.

Architecture에는 목적과 방향성 그리고 철학이 있다.
그에 따라 Code를 작성하고, 수정하고, 생각하고 고민해야 한다.
Architecture에서 정한 규칙을 준수해야 한다.
그럼으로 인해 Code에 일관성이 생기고, 사상이 투영된 Logic을 가지며,
Architecture가 말하는 Protocol 대로 해석이 가능하게 함으로써 그 다음을 바라 볼 수 있게 해준다.

2013년 3월 20일 수요일

iBATIS net.sf.cglib.beans.BulkBeanException

iBATIS에서 "net.sf.cglib.beans.BulkBeanException"와 같은 오류가 발생했다면

그것은 Integer Type의 Field가 null인 Record가 ResultMap에 Set되었다는 것을 의미한다.

즉, ResultMap의 Property 중 type이 Integer인 것을 찾아 아래와 같이 수정해 주면된다.



<resultMap id="resultSample" class="sample">

<result property="stringProp" column="STRING_PROP" />

<result property="integerProp" column="INTEGER_PROP" />

</resultMap>


<select id="selectSample" resultMap="resultSample">

SELECT STRING_PROP, COALESCE( INTEGER_PROP, 0 ) AS INTEGER_PROP

FROM TB_SAMPLE

</select>

"COALESCE"를 사용해 null인 경우 '0'으로 대치한다.



Hostname 'some.site.com' is blocked in your defense plan.

 

 

Hostname 'some.site.com' is blocked in your defense plan.

 

위와 같은 오류가 발생했다면 DynDNS를 사용하고 있을 것이다.

이를 해결하기 위해서는 다음 절차를 따르면 된다.

 

  1. DynDNS Updater를 열어 'Advanced' Menu를 선택한다.
  2. Advanced Menu의 'Enable DynDNS.com Internet Guide on this PC'를 uncheck한다.
  3. '네트워크 연결'의 '로컬 영역 연결'을 선택, 오른쪽 Mouse Click 후 '사용 안함' 선택 후
    연결이 끊어지면 다시 오른쪽 Mouse Button을 Click해 '사용함'을 선택한다.

DynDNS를 사용하다 이와 같은 오류를 보고 놀란 가슴 부여잡으며 다급히 Uninstall하는 사용자가 많을텐데 그러지 않아도 되며 안심하고 사용해도 될 것 같다.

 

 

 

Tomcat Error: Error filterStart



Tomcat과 Struts를 연동하던 중 Catalina log에 다음과 같이 "Error filterStart"라는 오류가 난다면

stdout.log의 Error Message를 먼저 확인해 보아야할 것이다.

분명 Load할 Class를 정상적으로 Load하지 못했기 때문이다.

Googling을 해 보면 Tomcat과 Struts의 Version 문제라거나 필수 Library의 문제라고도 하지만 그리 명확한 답변은 못 된다.

오류 Message가 아래와 같다면 적어도 그렇지 않다.

이 오류의 주요 원인은 'stdout.log'에서 보듯이 weblogic Library가 Load 되었기 때문이다.

weblogic Library가 Load되면서 XML 관련 Class의 참조 Path가 바뀐 것이 주요 원인이다.


이 문제의 해결 방법은 두 가지가 있다.

만약 Project에서 weblogic을 사용하지 않는다면 이를(Load된 weblogic.jar File을) 삭제하면 될 것이고,

사용한다면 www.apache.org에서(http://www.apache.org/dyn/closer.cgi/xml/xalan-j) 'xalan'을 Download해

'xalan.jar'과 'serializer.jar'를 Project Library path에 복사해 주면 된다.



아마 이 글을 보고 있는 개발자라면 문제의 원인을 찾거나 해결할 방법을 찾느라 많이 고심했으리라 미루어 짐작된다. 이 글을 쓰고 있는 필자 또한 그러했기 때문에..

모쪼록 이 글을 통해 기쁨이 함께하길 바라며 이 글을 올린다.



  • Canalina log.

2008. 12. 29 오후 11:50:32 org.apache.catalina.core.StandardHost start

정보: XML validation disabled

2008. 12. 29 오후 11:50:32 org.apache.catalina.core.StandardContext start

심각: Error filterStart

  • stdout log

심각: Exception starting filter struts2
java.lang.NoClassDefFoundError: org/apache/xalan/processor/TransformerFactoryImpl
at weblogic.xml.jaxp.RegistryTransformerFactory.<init>(RegistryTransformerFactory.java:62)
at weblogic.xml.jaxp.RegistrySAXTransformerFactory.<init>(RegistrySAXTransformerFactory.java:12)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at java.lang.Class.newInstance0(Unknown Source)
at java.lang.Class.newInstance(Unknown Source)
at javax.xml.transform.FactoryFinder.newInstance(Unknown Source)
at javax.xml.transform.FactoryFinder.findJarServiceProvider(Unknown Source)
at javax.xml.transform.FactoryFinder.find(Unknown Source)
at javax.xml.transform.TransformerFactory.newInstance(Unknown Source)
at com.opensymphony.xwork2.util.DomHelper$DOMBuilder.<clinit>(DomHelper.java:167)
at com.opensymphony.xwork2.util.DomHelper.parse(DomHelper.java:115)
at com.opensymphony.xwork2.config.providers.XmlConfigurationProvider.loadConfigurationFiles(XmlConfigurationProvider.java:830)
at com.opensymphony.xwork2.config.providers.XmlConfigurationProvider.loadDocuments(XmlConfigurationProvider.java:131)
at com.opensymphony.xwork2.config.providers.XmlConfigurationProvider.init(XmlConfigurationProvider.java:100)
at com.opensymphony.xwork2.config.impl.DefaultConfiguration.reload(DefaultConfiguration.java:130)
at com.opensymphony.xwork2.config.ConfigurationManager.getConfiguration(ConfigurationManager.java:52)
at org.apache.struts2.dispatcher.Dispatcher.init_PreloadConfiguration(Dispatcher.java:395)
at org.apache.struts2.dispatcher.Dispatcher.init(Dispatcher.java:452)
at org.apache.struts2.dispatcher.FilterDispatcher.init(FilterDispatcher.java:205)
at org.apache.catalina.core.ApplicationFilterConfig.getFilter(ApplicationFilterConfig.java:221)
at org.apache.catalina.core.ApplicationFilterConfig.setFilterDef(ApplicationFilterConfig.java:302)
at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:78)
at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:3635)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4222)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1014)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:736)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1014)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)
at org.apache.catalina.core.StandardService.start(StandardService.java:448)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:700)
at org.apache.catalina.startup.Catalina.start(Catalina.java:552)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:295)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:433)


Subversive update URL

 

http://www.polarion.org/projects/subversive/download/1.1/update-site/

 

 

* TortoiseSVN - http://tortoisesvn.tigris.org/

우편번호 DB

정보통신부에서 제공되는 최신 우편번호 DB 정보를 KSSoft에서 가공해 제공하고 있다.

URL은 다음과 같다.

 

http://kssoft.wowdns.com:8090/postnum/index.html

 

http://www.kssoft.com/postnum/index.html

 

 

Struts2의 핵심 Library

* Struts2 핵심 Library

activation.jar
antlr-2.7.2.jar
commons-beanutils-1.6.jar
commons-chain-1.1.jar
commons-logging-1.0.4.jar
commons-validator-1.3.0.jar
freemarker-2.3.8.jar
ognl-2.6.11.jar
oro-2.0.8.jar
struts2-core-2.0.11.1.jar
xwork-2.0.4.jar

 

* File Upload를 위한 Library

commons-fileupload-1.2.1.jar
commons-io-1.4.jar

하나의 Apache와 Tomcat Instance 2개 띄우기

이 방법은 하나의 Machine에 하나의 Apache HTTP Server와 두 개의 Tomcat을 설치해 연동 설정하는 방법이다.

 

 {HTTP_HOME}/conf/httpd.conf

<VirtualHost *:80>
        ServerAdmin abc@abc.com
        DocumentRoot "/home/www/www.abc.com"
        ServerName www.abc.com
        ServerAlias www.abc.co.kr www.abc.net
        ErrorLog "/var/log/apache2/www.abc.com-error.log"
        CustomLog "/var/log/apache2/www.abc.com-access.log" common
        JkMount /*.jsp worker1
        JkMount /servlet/* worker1
        JkMount /*.action worker1
        JkMount /*/*.action worker1
</VirtualHost>

 

<VirtualHost *:80>
        ServerAdmin abc@abc.com
        DocumentRoot "/home/www/www.abc.com"
        ServerName www.abc.com
        ServerAlias www.abc.co.kr www.abc.net
        ErrorLog "/var/log/apache2/www.abc.com-error.log"
        CustomLog "/var/log/apache2/www.abc.com-access.log" common
        JkMount /*.jsp worker2
        JkMount /servlet/* worker2
        JkMount /*.action worker2
        JkMount /*/*.action worker2
</VirtualHost>

{HTTP_HOME}/conf/worker.properties

: worker의 port를 서로 다르게 설정하고 뒤에 설정할 Tomcat의 설정 File인 server.xml의 Connector port가 된다.

workers.tomcat_home=/usr/local/tomcat5523, /usr/local/tomcat5523-web
workers.java_home=/usr/local/java_1.6
ps=/
worker.list=worker1, worker2

# mod_jk for 1st web
worker.worker1.port=18009
worker.worker1.host=localhost
worker.worker1.type=ajp13
worker.worker1.lbfactor=1

 

# mod_jk for 2nd web
worker.worker2.port=28009
worker.worker2.host=localhost
worker.worker2.type=ajp13
worker.worker2.lbfactor=1

2개의 Tomcat 설정 File인 server.xml의 port를 각각 다르게 설정한다.

{CATALINA_HOME}/conf/server.xml - 1st Tomcat

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

... ...

<!-- Define a SSL HTTP/1.1 Connector on port 8443 -->

 <Connector port="18443" maxHttpHeaderSize="8192"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" disableUploadTimeout="true"
               acceptCount="100" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS"  keystorePass="changeit" keystoreFile="/home/jira_confluence_source/plugin/ssl/keystore"/>

 

<!-- Define an AJP 1.3 Connector on port 8009 -->
    <Connector port="18009"
               enableLookups="false" redirectPort="18443" protocol="AJP/1.3"  useBodyEncodingForURI="true" URIEncoding="UTF-8" />

... ...

{CATALINA_HOME}/conf/server.xml - 2nd Tomcat

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

... ...

<!-- Define a SSL HTTP/1.1 Connector on port 8443 -->

 <Connector port="28443" maxHttpHeaderSize="8192"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" disableUploadTimeout="true"
               acceptCount="100" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS"  keystorePass="changeit" keystoreFile="/home/jira_confluence_source/plugin/ssl/keystore"/>

 

<!-- Define an AJP 1.3 Connector on port 8009 -->
    <Connector port="28009"
               enableLookups="false" redirectPort="28443" protocol="AJP/1.3"  useBodyEncodingForURI="true" URIEncoding="UTF-8" />

... ...

Tomcat URL 직접 접근 막기

o web.xml 설정


 <!-- Define a Security Constraint on this Application -->
 <security-constraint>
  <web-resource-collection>
   <web-resource-name>Resumes</web-resource-name>
   <url-pattern>/data/*</url-pattern>

   <url-pattern>/data2/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
   <!-- NOTE:  This role is not present in the default users file -->
   <role-name>admin</role-name>
  </auth-constraint>
 </security-constraint>

 <!-- Define the Login Configuration for this Application -->
 <login-config>
  <auth-method>BASIC</auth-method>
  <realm-name>인증창에 설명할 내용</realm-name>
<!--  <auth-method>FORM</auth-method>
  <form-login-config>
   <form-login-page>/admin/login.jsp</form-login-page>
   <form-error-page>/admin/fail.jsp</form-error-page>
  </form-login-config> -->

 </login-config>

 <!-- Security roles referenced by this web application -->
 <security-role>
  <description>
  The role that is required to log in to the Manager Application
  </description>
  <role-name>admin</role-name>
 </security-role>

 

Tomcat 404 Error 처리

 

o 설정 내용

 1. {Context}/WEB-INF/web.xml 수정

 2. {docBase}/{errorHandlingDirectory}/{errorPage} 작성

    - Error Page의 기준 Path는 Context에 설정한 docBase가 되며,

       {errorHandlingDirectory}는 임의로 지정하면 된다.

 

1. 해당 Context의 WEB-INF에 있는 web.xml에 다음 추가

<web-app>

... ...

... ...

<error-page>
    <error-code>404</error-code>

    <location>/error/error404.jsp</location>

    <exception-type>java.lang.NullPointerException</exception-type>
    <location>/error/errorNullPointer.jsp</location>

</error-page>

<error-page>
    <error-code>500</error-code>
    <location>/error/500.jsp</location>
</error-page>
<error-page>
    <exception-type>java.lang.Throwable</exception-type>
    <location>/error/exception.jsp</location>
</error-page>

<error-page>
    <exception-type>java.lang.NullPointerException</exception-type>
    <location>/servlet/ErrorServlet2</location>
</error-page>

... ...

... ...
</web-app>

2.해당 Context의 docBase에 Error Page 작성

 

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

<%

        Status를 200(OK)로 Response하지 않으면

        브라우져의 Default 404 Error Page가 뜨게 된다.
          response.setStatus( HttpServletResponse.SC_OK );
%>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>404 Error</title>
</head>

<body>
 <center><B>요청하신 Page를 찾을 수 없습니다.</B></center>
</body>
</html>

 

* 참고 사항

web.xml 의 <error-code>, <exception-type> 엘리먼트에 설정된 오류의 <location> 지정 서블릿/JSP 가 수행될 때 발생된 오류에 대한 정보가 HttpServletRequest 객체 통해 전달됨.

 

  - javax.servlet.error.status_code : 오류 코드

  - javax.servlet.error.exception_type : 예외 타입 정보 (Class 형 객체)

  - javax.servlet.error.message : 예외 메시지

  - javax.servlet.error.exception : 발생된 예외 객체

  - javax.servlet.error.request_uri : 오류 발생 파일의 URI

 

  ==> 사용법

        Object exObj = req.getAttribute("javax.servlet.error.exception");

        String uri = "";

        if(uriObj != null)   uri= uriObj.toString();

==========================================================================
out.print(request.getAttribute("javax.servlet.error.status_code"));
out.print("<br/>");
out.print(request.getAttribute("javax.servlet.error.exception_type"));
out.print("<br/>");
out.print(request.getAttribute("javax.servlet.error.message"));
out.print("<br/>");
out.print(request.getAttribute("javax.servlet.error.exception"));
out.print("<br/>");
out.print(request.getAttribute("javax.servlet.error.request_uri"));
 

CVS 설치 및 설정

CVS(Concurrent Version System)

 

  1. 설치 확인

    RPM으로 설치된 package가 있는지 확인한다.

    $ rpm -qa | grep cvs

    Package가 있다면 다음과 같이 설치한다.

    $ rpm -Uvh cvs-version.rpm

          또는

    $ rpm -ivh cvs-version.rpm

 

  2. Download & 설치

    Download here: http://www.nongnu.org/cvs/

    $ ./configure

    $ make

    $ make install

 

  3. 설정(초기화)

     '/home/cvsroot'에 원하는 cvs의 repositary path를 입력한다.

    $ cvs -d /home/cvsroot init

             또는

    환경변수 CVSROOT=/home/cvsroot를 profile파일에 설정하고

    $ cvs init

Apache HTTP Server 2.2.4 + Apache Tomcat 5.5.23 + Tomcat Connector 1.2.25(mod_jk) 설치 및 연동

mod_jk를 사용해서 아파치 2 와 톰캣 5.5를 연동하기 

1: 아파치 2를 설치한다.

httpd-2.0.55의 소스를 받아 설치한다.
./configure --enable-so --enable-mods-shared=most --prefix=/opt/httpd-2.2.4
make
make install

2: 자바(JDK 1.5이상)를 설치한다.

/usr/local/java 등의 위치에 설치하고, /etc/profile 제일 아랫줄에
export JAVA_HOME=/usr/java/jdk1.5.0_05
라고 추가하고,
source /etc/profile 명령을 실행해서 변경을 적용시킨다.


3: 톰캣 5.5 설치

apache-tomcat-5.5.12.tar.gz 를 다운받아 /usr/local 에 놓고 root 권한으로 아래와 같이 실행한다.

cd /usr/local
tar xvfz apache-tomcat-5.5.12
ln -s /opt/apache-tomcat-5.5.23 /opt/tomcat
groupadd tomcat
useradd tomcat -g tomcat -d /opt/tomcat tomcat
chown -R tomcat.tomcat /opt/apache-tomcat-5.5.12 /opt/tomcat

/etc/profile에 아래의 내용을 추가하고 적용한다.

export PATH=$PATH:/usr/local/bin:/opt/tomcat/bin
export JAVA_HOME=/usr/java/jdk1.5.0_05
export CATALINA_HOME=/opt/tomcat

위의 내용을 추가했으면,

source /usr/profile을 실행


톰캣 서버 실행 테스트

톰캣 디렉토리의 bin디렉토리가 PATH에 포함된 것을 확인하고,
startup.sh
을 실행하면 톰캣이 실행된다.

ps -def | grep tomcat
이라고 실행할 때
tomcat 18591 1 88 06:40 pts/0
와 비슷한 내용이 보이면 톰캣 서버가 실행된 것이다.
 
shutdown.sh
을 실행하여 톰캣 서버를 멈추고서, 다시
ps -def | grep tomcat

을 실행하면 위의 내용이 보이지 않게 된다.


4: mod_jk 설치하기
 jakarta-tomcat-connectors-1.2.14.1-src.tar.gz 를 다운받고 아래처럼 실행한다.

cd tomcat-connectors-1.2.25-src/native
./buildconf.sh
./configure --with-apxs=/opt/httpd-2.2.4/bin/apxs
make

make install

mod_jk.so 가 /opt/httpd-2.2.4/modules 디렉토리 안에 있고, 권한은 755로 설정되어있는지 확인한다.

 

5: 아파치를 톰캣에 연결하기

/usr/local/apache2/conf 디렉토리에 workers.properties 라는 이름의 파일을 만들고 아래의 내용을 입력한 후 저장한다.

workers.tomcat_home=/opt/apache-tomcat-5.5.23
workers.java_home=/opt/java
ps=/
worker.list=ajp13

worker.ajp13.port=8009
worker.ajp13.host=[ localhost | IP ]
worker.ajp13.type=ajp13

 

httpd.conf 에 아래의 내용을 추가한다.
# Mod_jk settings
# Load mod_jk module
LoadModule jk_module modules/mod_jk.so
# Where to find workers.properties
JkWorkersFile conf/workers.properties
# Where to put jk logs
JkLogFile logs/mod_jk.log
# Set the jk log level [debug/error/info]
JkLogLevel debug
# Select the log format
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
# JkOptions indicate to send SSL KEY SIZE,
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories
# JkRequestLogFormat set the request format
JkRequestLogFormat "%w %V %T"

iBATIS 오류 Cause: java.lang.IllegalArgumentException: Cannot subclass final class int



iBATIS Error Cause: java.lang.IllegalArgumentException: Cannot subclass final class int

Cause: java.lang.IllegalArgumentException: Cannot subclass final class int


Solution>

Add attribute at 'settings' tag in your SQL map config file, like below...
enhancementEnabled="false"
...
/>

iBATIS Insert 시 자동으로 생성된 Key의 참조


MySQL 사용 시 예제

<insert ...>

<!--

insert query here

-->

<selectKey keyProperty="createdKey" resultClass="int">

SELECT LAST_INSERT_ID()

</selectKey>

</insert>

DBMS 별 참조 Key

oracle: nextval(#sequence#)

mssql: @@IDENTITY

mysql: LAST_INSERT_ID()


30 Auto Completion Scripts for Better User Experience

http://www.1stwebdesigner.com/resources/auto-completion-scripts/

NoSuchProviderException: provider Cryptix is not available

 

Cryptix를 Library를 이용한 암/복호화 시 Tomcat과 연동할 때가 있을 수 있는데

이때 설정을 하지 않고 사용할 경우 발생하는 오류 되시겠다.

NoSuchProviderException: provider Cryptix is not available

 설정을 확인해야할 부분은 다음과 같다.

 

1. "$JAVA_HOME/lib/"에 cryptix32.jar Library를 복사한다.

2. 다음 명령을 실행해 Install한다.

   $ java -classpath cryptix32.jar cryptix.provider.Install

3. "$CATALINA_HOME/bin/"에 있는 catalina.sh을 수정한다.

   CLASSPATH=$JAVA_HOME/lib/cryptix32.jar:$CLASSPATH

4. Tomcat Restart

 

 

 

 

암호화/복호화


import java.security.*;
import javax.crypto.*;
public class SimpleExample {
    public static void main(String [] args) throws Exception {
        if( args.length != 1) {
            System.out.println("Usage : java SimpleExample text ");
            System.exit(1);
        }
        String text = args[0];
        System.out.println("Generating a DESded (TripleDES) key...");
        // Triple DES 생성
        KeyGenerator keyGenerator = KeyGenerator.getInstance("DESede");
        keyGenerator.init(168); // 키의 크기를 168비트로 초기화
        Key key = keyGenerator.generateKey();
        System.out.println("키생성이 완료되었음");
        System.out.println("key=" + key);
        // Cipher를 생성, 사용할 키로 초기화
        Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte [] plainText = text.getBytes("UTF8");
        System.out.println("Plain Text : ");
        for (int i = 0; i < plainText.length ; i++) {
            System.out.print(plainText[i] + " ");
        }
        // 암호화 시작
        byte [] cipherText = cipher.doFinal(plainText);
        // 암호문서 출력
        System.out.println("\nCipher Text : ");
        for (int i = 0; i < cipherText.length ; i++) {
            System.out.print(cipherText[i] + " ");
        }
        //복호화 모드로서 다시 초기화
        cipher.init(Cipher.DECRYPT_MODE, key);
        //복호화 수행
        byte [] decryptedText = cipher.doFinal(cipherText);
        String output =  new String(decryptedText, "UTF8");
        System.out.println("\nDecrypted Text : " + output);
    }
}

MD5 암호화



import java.security.*;


public class MD5Password
  {
  public static String getEncodedPassword(String clearTextPassword)
               throws NoSuchAlgorithmException
    {
    MessageDigest md = MessageDigest.getInstance("MD5");

    md.update(clearTextPassword.getBytes());

    return HexString.bufferToHex(md.digest());
    }

  public static boolean testPassword(String clearTextTestPassword,
                     String encodedActualPassword)
                     throws NoSuchAlgorithmException
    {
    String encodedTestPassword = MD5Password.getEncodedPassword(
                      clearTextTestPassword);

    return (encodedTestPassword.equals(encodedActualPassword));
    }
  }


Download Page - JSP



<%
/**   ================================================================
      Mobicator
      ----------------------------------------------------------------
      @description {file} Parameter 지정한 Id 해당하는 file Download
                        * CAUTION: JSP File File Download하는 File이므로
                         임의로  '% >' '< %'사이를 띄우거나 CR/LF 줘서는 안된다.
      @param
            file Download File Id
      @include
            N/A
      @usage
            /download.jsp?file={file_id}
      @author pluto
      @date 2008. 02. 20
      ================================================================
*/
%><%@ page language="java" contentType="application/unknown; charset=UTF-8" pageEncoding="UTF-8"
%><%-- All of Import here
--%><%@page import="java.io.File"
%><%@page import="java.io.InputStream"
%><%@page import="java.io.OutputStream"
%><%@page import="java.io.FileInputStream"
%><%@page import="java.io.BufferedOutputStream"
%><%@page import="java.io.BufferedInputStream"
%><%@page import="java.io.UnsupportedEncodingException"
%><%!
      private int DEFAULT_BUFFER_SIZE                 = 2048;

      private String HEAD_CONTENT_TYPE                = "Content-Type";
      private String HEAD_CONTENT_DISPOSITION         = "Content-Disposition";
      private String HEAD_CONTENT_TRANSFER_ENCODE     = "Content-Transfer-Encoding";
      private String HEAD_PRAGMA                            = "Pragma";
      private String HEAD_EXPIRES                           = "Expires";
      private String HEAD_CONTENT_LENGTH              = "Content-Length";

      private String CONTENT_TYPE_MATTER              = "doesn/matter;";
      private String CONTENT_TYPE_OCTET               = "application/octet-stream;";
      private String CONTENT_TRANSFER_ENCODE          = "binary;";
      private String PRAGMA                                 = "no-cache;";
      private String EXPIRES                                = "-1";

      private String USER_AGENT                             = "USER-AGENT";
      private String ENCODING                               = "UTF-8";
      private String CHAR_SET                               = "charset=" + ENCODING + ";";
      private String FILE_NAME                              = "filename=";
      private String ATTACHMENT                             = "attachment;";
      private String LOCAL_PATH                             = "/localPath/";

      private String MSIE_4                                 = "MSIE 4";
      private String MSIE_5_5                               = "MSIE 5.5";
      private String FIRE_FOX                               = "Firefox";

      public String getFileName( String fileName, String ua ) throws UnsupportedEncodingException {
            if( ua.indexOf( FIRE_FOX ) >= 0 ) {
                  return fileName;
            } else {
                 return java.net.URLEncoder.encode( fileName, "UTF-8" ).replaceAll( "\\+", "%20" );
            }
      }
%><%
      InputStream                   inStream = null;
      OutputStream                  outStream = null;
      byte[]                              buffer = null;

      String                              docBase = getServletContext().getRealPath( "/" );
      String                              ua = request.getHeader( USER_AGENT );
      String                              id = request.getParameter( "file" );
      String                              url = request.getRequestURL().toString();
      File                          downloadFile = new File( LOCAL_PATH + id );
      String                              fileName = getFileName( downloadFile.getName(), ua );

      if( ua.indexOf( MSIE_4 ) >= 0 || ua.indexOf( MSIE_5_5 ) >= 0 ) {
            response.setHeader( HEAD_CONTENT_TYPE, CONTENT_TYPE_MATTER + CHAR_SET );
            response.setHeader( HEAD_CONTENT_DISPOSITION, FILE_NAME + fileName + ";" );
      } else {
            response.setHeader( HEAD_CONTENT_TYPE, CONTENT_TYPE_OCTET + CHAR_SET );
            response.setHeader( HEAD_CONTENT_DISPOSITION, ATTACHMENT + FILE_NAME + fileName + ";" );
      }
      response.setHeader( HEAD_CONTENT_TRANSFER_ENCODE, CONTENT_TRANSFER_ENCODE );
      response.setHeader( HEAD_PRAGMA, PRAGMA );
      response.setHeader( HEAD_EXPIRES, EXPIRES );
      response.setHeader( HEAD_CONTENT_LENGTH, String.valueOf( downloadFile.length() ) );

      if( downloadFile.isFile() ) {
            try {
                  inStream = new BufferedInputStream( new FileInputStream( downloadFile ) );
                  outStream = new BufferedOutputStream( response.getOutputStream() );
                  buffer = new byte[ DEFAULT_BUFFER_SIZE ];
                  int                           bufferSize = 0;
                  while( (bufferSize = inStream.read( buffer )) != -1 ) {
                        outStream.write( buffer, 0, bufferSize );
                        outStream.flush();
                  }
            } catch( Exception e ) {
                  e.printStackTrace();
            } finally {
                  if( inStream != null ) {
                        inStream.close();
                  }
            }
      }
%>