해당 Servlet 3.0을 지원하기 전의 서블릿 컨테이너들은 각기 자체적인 비동기 방식의 IO 구현을 통해 Comet (참조글:http://www.opentalker.com/212)방식을 처리하고 있었습니다. 대표적인 것이 Tomcat 6.0부터의 CometProtocol 지원이었습니다. 이는 다음의 블로그에서 확인하세요.
어느날 Tomcat의 Comet 프로세서를 이용한 애플리케이션을 JBoss에서 구동해야 한다는 문제가 생겼습니다. JBoss Web의 경우 Tomcat 코드에 기반을 두고 있기는 하나 방식도 약간 다르고 성능 확장 팩 등이 포함되어 있는 태생은 같으나 생김새가 조금은 다른 형태로 진화되었습니다.
JBoss Web 2.1.0에서는 Tomcat의 Http11NioProtocol을 같이 내장하고 있었지만 현재 나오는 상용버전인 JBoss EAP 5.1(Community 5.1)의 내장 컨테이너인 JBoss Web 2.1.10에서는 해당 클래스가 삭제되어 있는 것을 Change Log를 통해 알았습니다.
또 다른 중요한 한 가지가 존재하는 데 Tomcat의 org.apache.catalina.CometProcessor, CometEvent가 JBoss에서 Servlet 3.0을 준비하기 위한 org.jboss.servlet.http.HttpEventServlet, HttpEvent로 변경되었습니다.
주의 : Servlet 3.0 스펙을 지원하는 JBoss 6의 경우 이미 표준화되어 있기 때문에 특정 클래스를 사용하지 않아도 기능이 구현되었습니다.
2. org.apache.catalina.CometProcessor -> org.jboss.servlet.http.HttpEventServlet org.apache.catalina.CometEvent -> org.jboss.servlet.http.HttpEvent로 변경 API를 몇 개를 빼고 거의 똑같으므로 큰 변경은 없음.
위의 JBossWeb 관련 라이브러리는 $JBOSS_HOME/server/default/deploy/jbossweb.sar/jbossweb.jar 파일안에 포함되어 있음. 이제부터 상세 절차를 알아보겠습니다.
public class MessageSender implements Runnable {
private volatile boolean running = true;
private final BlockingQueue<String> messages = new LinkedBlockingQueue<String>();
private final Map<String, HttpEvent> sessions = new ConcurrentHashMap<String, HttpEvent>();
private final ExecutorService executor = Executors.newFixedThreadPool(5);
JBoss AS의 web에는 tomcat이 빌드되어 있습니다. Servlet을 호출할 경우 보통 org.apache.catalina.servlets.InvokerServlet을 사용하여 다음과 같은 URL을 사용하여 직접 서블릿 클래스를 호출하는 것이 가능합니다.
As you may know, JBoss Appliation Server includes tomcat runtime for processing web application. If you don't want to use specific url-pattern in web descriptor file(web.xml), you can use Tomcat invoker servlet instead of it.
이를 실제 URL로 위의 예를 변환하면 http://localhost:8080/test/servlet/info.javapattern.servlet.TestServlet
놀새~에게 나타났던 문제는 "고객사에서 /servlet/info.javapattern.servlet.TestServlet 처럼 호출이 아닌 WebLogic, Jeus(초기에 웹로직을 본떴으니 같은 기능을 가지고 있겠죠)와 같은 /servlet/info/javapattern/servlet/TestServlet 호출시 해당 서블릿이 구동되게 해주십시오"였습니다.
이를 해결하기 위해 catalina 소스를 빌드하였지만 컨텍스트 클래스 로더 내의 클래스로 한정짓는 제약조건과 signing된 jar로 인하여 소스를 패치하는 것에 문제가 있었습니다.
결국 이를 해결하기 위해 InvokerServlet을 사용하는 중간 단계의 서블릿을 하나 만들어 Catalina InvokerServlet으로 전달하는 방식으로 Slash 형태의 서블릿을 호출하는 코드를 만들었습니다.
SlashInvoker의 소스는 다음과 같습니다.
I've got a customer who is asking JBoss web container can be invoked using slash style url pattern like WebLogic. To resolve this issue, I made a patch by changing the catalina source(InvokerServlet), but it failed because there was a problems with ServletDispatcher.
Finally, I made a temporary servlet to redirect request to catalina InvokerServlet. It is called SlashInvoker and its code is here :
이 Invoker를 사용하는 web.xml에서는 invoker를 /servlet/*로 변경하고 해당 URL pattern으로 들어오는 서블릿의 경로를 /에서 .으로 변경한 후 실제 catalina의 invoker로 redirect시키는 방법으로 문제를 해결하였습니다.
I fixed the problem by changing servlet path information from slash(/) to dot(.) and redirecting to catalina servlet. The descriptor I explained is below:
config 디렉토리를 보면 stand-alone항목에 non-clustered와 clustered 항목이 존재하는 데 이를 구동하는 방법에는 두 가지가 있습니다.
1. run.sh 또는 run.bat 파일의 CLUSTER_PROPS를 통하여 필요한 host, port 및 클러스터 정보를 넘겨주는 방법 2. run.sh 또는 run.bat 파일의 CONFIG 디렉토리를 변경하고, clustered에 해당하는 config를 하나 더 생성하는 방법
놀새~의 경우 1번 방법을 사용하였습니다. 이 때 주의해야 할 사항은 Acceptor와 Connector의 포트를 변경하도록 하는 것입니다. 클러스터이기 때문에 브로드캐스트 및 discovery-group에 대한 부분은 동일한 네트워크에 있도록 그대로 두도록 합니다.
그리고 한 가지 중요한 사항은 기존의 JBoss messaging에서 ServerPeerID에 대한 항목을 사용자가 직접 수정해주었어야 했는데, HornetQ부터는 UUID를 자동으로 생성하여 클러스터 내의 노드ID를 만들어 줍니다.
마지막으로 바인딩과 저널에 대한 디렉토리의 위치를 변경해주어야 합니다. 앞서 clustered에 대한 내용을 복사했으므로 아래와 같이 동일한 내용의 data디렉토리를 보게 됩니다.
위와 같이 config가 동일할 경우 data디렉토리로 인하여 아래와 같은 에러가 발생하게 됩니다.
[hornetq-discovery-group-thread-dg-group1] 11:15:52,596 WARNING [org.hornetq.core.cluster.impl.DiscoveryGroupImpl] There are more than one servers on the network broadcasting the same node id. You will see this message exactly once (per node) if a node is restarted, in which case it can be safely ignored. But if it is logged continuously it means you really do have more than one node on the same network active concurrently with the same node id. This could occur if you have a backup node active at the same time as its live node.
이를 해결하기 위하여 각 클러스터 노드별로 data디렉토리를 다르게 지정하면 정상적으로 서버가 구동됩니다.
[Thread-0 (group:HornetQ-server-threads664112322-1407844613)] 11:30:52,465 INFO [org.hornetq.core.server.cluster.impl.BridgeImpl] Connecting bridge sf.my-cluster.ecda32db-3ef4-11e0-ac99-001aa0bb36f0 to its destination [Thread-0 (group:HornetQ-server-threads664112322-1407844613)] 11:30:53,281 INFO [org.hornetq.core.server.cluster.impl.BridgeImpl] Bridge sf.my-cluster.ecda32db-3ef4-11e0-ac99-001aa0bb36f0 is connected to its destination
클러스터 설정 파일(Cluster Configuration File)을 config디렉토리 아래로 복사하십시오.
If you're trying to set up HornetQ cluster using default configuration on the same box, you'll probably get error message like these :
[hornetq-discovery-group-thread-dg-group1] 11:15:52,596 WARNING [org.hornetq.core.cluster.impl.DiscoveryGroupImpl] There are more than one servers on the network broadcasting the same node id. You will see this message exactly once (per node) if a node is restarted, in which case it can be safely ignored. But if it is logged continuously it means you really do have more than one node on the same network active concurrently with the same node id. This could occur if you have a backup node active at the same time as its live node.
To fix this problem, you should cleanup the data files and change the journal/binding/paging directories in hornetq-configuration.xml. After that, when node1 in cluster is started, you can see "cluster.name + UUID" for that cluster node.
If you want to get config and script directories in hornetq, please download above links(config.zip, bin.zip)
@REM set JAVA_HOME=C:\Program Files\Java\jdk1.6.0_10
set JAVA_HOME=C:\Java\jdk1.6.0_20
@REM set JAVA_HOME=OTHER_FOLDER
set JBOSS_HOME=C:\java\jboss-eap-5.1.0.GA\jboss-as
set SERVER_HOME=C:\java\domains
set SERVER_NAME=demo
set PARTITION_NAME=cluster1
set MULTICAST_ADDR=228.1.2.3
echo "================================================"
echo "JBOSS_HOME=%JBOSS_HOME%"
echo "SERVER_HOME=%SERVER_HOME%"
echo "SERVER_NAME=%SERVER_NAME%"
echo "================================================"
run.cmd
call env.cmd
set JAVA_OPTS=-Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 -Dsun.lang.ClassLoader.allowArraySyntax=true
set JAVA_OPTS=-server -Xms512m -Xmx512m -XX:MaxPermSize=128m %JAVA_OPTS%
@REM set JAVA_OPTS= %JAVA_OPTS% -verbose:gc
%JBOSS_HOME%\bin\run.bat -b 0.0.0.0 -Dserver=%SERVER_NAME% -Djboss.server.base.dir=%SERVER_HOME% -Djboss.server.base.url=file://%SERVER_HOME% -c %SERVER_NAME% -g %PARTITION_NAME% -u %MULTICAST_ADDR%