spring과 hornetq를 사용하는 EJB 애플리케이션을 등록하다가 ear 애플리케이션 디플로이시 바인딩되지도 않은 JMS Connection Factory를 접근하다가 에러가 발생을 하는데 아래와 같이 설정을 바꿈으로써 가능합니다.

좋은 정보를 알려주신 송민우 책임, 감사합니다.

Many people talked about getting error when they deploy ear application including MDB, EJB like this :
http://stackoverflow.com/questions/4616078/how-to-order-deployment-of-ejbs-and-jms-queue-config-in-jboss-5 

http://community.jboss.org/thread/93539 http://community.jboss.org/thread/93539


In earlier version of JBoss, there was a method you can change deployment order in configuration file.

In JBoss EAP5 or AS5, you can configure deployment order like this :

$SERVER_HOME/conf/bootstrap/deployers.xml

 <!-- use legacy ordering -->
 <bean name="topContextComparator">
      <constructor factoryClass="org.jboss.system.deployers.LegacyDeploymentContextComparator" factoryMethod="getInstance"/>
         <property name="suffixOrder" class="java.util.Map">
             <map keyClass="java.lang.String" valueClass="java.lang.Integer">
              <entry>
               <key>-beans.xml</key>
               <value>200</value>
              </entry>
              <entry>
               <key>.ear</key>
               <value>1000</value>
              </entry>
              <entry>
               <key>.jar</key>
               <value>1000</value>
              </entry>
              <entry>
               <key>.war</key>
               <value>1000</value>
              </entry>
          </map>
       </property>
 </bean>


크리에이티브 커먼즈 라이센스
Creative Commons License
2011/12/20 08:57 2011/12/20 08:57

JBoss의 UnifiedClassloader의 경우 Parent Classloader와의 불화(?)로 인하여 웹 애플리케이션이나 엔터프라이즈 애플리케이션의 클래스가 참조되지 못하는 현상이 가끔 있습니다.

이러한 현상이 다시 발생한 조합은 다음과 같습니다.

WAS : JBoss EAP 5.1
Framework : Spring 2.5.6, iBatis, Apache CXF

에러 현상은 다음과 같습니다.

Mapped-name is required for cxf of deployment xxx.war


이는 @Resource 어노테이션 태그를 사용했을 경우에 상위 JBoss 표준 API가 하위 웹 애플리케이션의 클래스에 제대로 Injection을 시켜주지 못하기 때문에 나는 것으로 보입니다.(확정은 아닙니다.)

위의 문제를 해결하기 위해서는 하위 애플리케이션을 상위 클래스로더로부터 분리시키고, @Resource를 처리하는 라이브러리를 설치하여 해결해야만 합니다.

jboss-classloading.xml 파일을 WEB-INF/ 디렉토리 아래에 생성하여 아래와 같이 설정합니다.

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

<classloading xmlns="urn:jboss:classloading:1.0"

    name="push.war"

    domain="push.war.domain"

parent-first="false"

    export-all="NON_EMPTY"

    import-all="true">

</classloading>

그리고 @Resource 어노테이션을 처리하기 위한 annotation-api.jar파일을 다운받아 WEB-INF/lib 디렉토리 밑에 추가하도록 합니다.


URL은 다음과 같습니다.

크리에이티브 커먼즈 라이센스
Creative Commons License
2011/07/04 09:39 2011/07/04 09:39

Asynchronous IO의 여파가 Servlet까지 이어져 Servlet 3.0 명세서에는 이벤트 서블릿에 대한 항목(http://www.javaworld.com/javaworld/jw-02-2009/jw-02-servlet3.html)이 추가되었습니다.

해당 Servlet 3.0을 지원하기 전의 서블릿 컨테이너들은 각기 자체적인 비동기 방식의 IO 구현을 통해 Comet (참조글:http://www.opentalker.com/212)방식을  처리하고 있었습니다.
대표적인 것이 Tomcat 6.0부터의 CometProtocol 지원이었습니다.
이는 다음의 블로그에서 확인하세요.

자바 서블릿 컨테이너의 Comet 지원 2 - Tomcat
http://corund.net/blog/entry/%EC%9E%90%EB%B0%94-%EC%84%9C%EB%B8%94%EB%A6%BF-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%9D%98-Comet-%EC%A7%80%EC%9B%90-2---Tomcat

어느날 Tomcat의 Comet 프로세서를 이용한 애플리케이션을 JBoss에서 구동해야 한다는 문제가 생겼습니다. JBoss Web의 경우 Tomcat 코드에 기반을 두고 있기는 하나 방식도 약간 다르고 성능 확장 팩 등이 포함되어 있는 태생은 같으나 생김새가 조금은 다른 형태로 진화되었습니다.

Tomcat 6.X의 Comet을 JBoss로 변환하기 위해 찾아본 문서(http://community.jboss.org/message/126750)에 다음과 같이 이야기하고 있습니다.

"You should use the APR connector"

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의 경우 이미 표준화되어 있기 때문에 특정 클래스를 사용하지 않아도 기능이 구현되었습니다.

결국 마이그레이션 절차는 다음과 같이 요약될 수 있습니다.

1. server.xml 설정의 Http11NioProtocol을 JBoss의 APR(http://docs.jboss.org/jbossweb/2.1.x/aio.html)로 변경

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 파일안에 포함되어 있음.
이제부터 상세 절차를 알아보겠습니다.

1. 먼저 APR(Apache Portable Runtime) 프로토콜을 사용하기 위해 Native Library를 다음의 사이트에서 다운받아 압축을 풉니다.
http://www.jboss.org/jbossweb/downloads/jboss-native-2-0-9

현재 가장 최신이 2.0.9입니다.

OS에 맞는 버전을 받아 library path로 잡습니다. Windows의 경우 path에 잡힌 곳에 풀어줍니다. 저의 경우는 JDK_HOME/bin 디렉토리에 풀어놓았습니다.

2. JBoss Web의 server.xml을 바꿉니다.
  - JBOSS_HOME/server/$SERVER_NAME/deploy/jbossweb.sar/server.xml을 편집하여 기본 HTTP/1.1을 다음과 같이 바꿉니다.

<!-- A HTTP/1.1 Connector on port 8080 -->
      <!--Connector protocol="HTTP/1.1" port="8080" address="${jboss.bind.address}"
               connectionTimeout="20000"  redirectPort="8443" /-->
               
      <Connector protocol="org.apache.coyote.http11.Http11AprProtocol" port="8000" address="${jboss.bind.address}"
               connectionTimeout="20000" redirectPort="8443" />

위에서 바뀐 것은 protocol이 HTTP/1.1에서 org.apache.coyote.http11.Http11AprProtocol, 포트가 8080에서 8000으로 바뀐 것 밖에 없습니다. 8000번으로 바꾼 것은 놀새~ 마음입니다.

3. 기존의 Comet 애플리케이션을 변경합니다.
테스트에 사용한 Comet 애플리케이션을 미리 ChatServlet을 친철하게 구현한 nemo님의 소스를 활용했습니다.
반드시 읽어보시기 바랍니다.
http://yewon.egloos.com/2335303

변경한 소스 샘플(ChatServlet, Broadcaster)은 다음과 같습니다.

ChatServlet.java

package javapattern;

import java.io.IOException; import java.io.UnsupportedEncodingException;

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

public class ChatServlet extends HttpServlet {  private static final long serialVersionUID = 1L;  private MessageSender messageSender = null;

 @Override  public void init() throws ServletException {   messageSender = (MessageSender) getServletContext().getAttribute(     "messageSender");   super.init();  }

 public void doGet(HttpServletRequest request, HttpServletResponse response) {   try {    request.getRequestDispatcher("/WEB-INF/chat.jsp").forward(request, response);   } catch (ServletException e) {    // TODO Auto-generated catch block    e.printStackTrace();   } catch (IOException e) {    // TODO Auto-generated catch block    e.printStackTrace();   }  }

 public void doPost(HttpServletRequest request, HttpServletResponse response) {   try {    request.setCharacterEncoding("UTF-8");   } catch (UnsupportedEncodingException e1) {    // TODO Auto-generated catch block    e1.printStackTrace();   }   String name = request.getParameter("name");   String text = request.getParameter("text");

  String message = "<b>" + name + "</b>: " + text + "<br>";   messageSender.send(message);

  response.setContentType("text/html");   try {    response.getWriter().println("OK");   } catch (IOException e) {    // TODO Auto-generated catch block    e.printStackTrace();   }  }   }

BroadcasterServlet.java

package javapattern;

import java.io.IOException;

import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;

import org.jboss.servlet.http.HttpEvent; import org.jboss.servlet.http.HttpEventServlet;

public class BroadcasterServlet extends HttpServlet implements HttpEventServlet {  private static final long serialVersionUID = 457435579620762241L;    private MessageSender messageSender = null;  private ServletContext servletContext = null;

 @Override  public void init(ServletConfig config) throws ServletException {

  servletContext = config.getServletContext();   log("Comet test initialized.");   messageSender = (MessageSender) servletContext     .getAttribute("messageSender");   log("Comet test initialized.");  }

 public void doGet(HttpServletRequest request, HttpServletResponse response)    throws IOException {   System.err.println("doGet called.");  }

 public void event(HttpEvent event) throws ServletException, IOException {   HttpServletRequest request = event.getHttpServletRequest();   HttpServletResponse response = event.getHttpServletResponse();   String sessionId = request.getRequestedSessionId();

  switch (event.getType()) {     case BEGIN :     log("Begin for session: " + request.getSession(true).getId());     event.setTimeout(60 * 1000);     response.setContentType("text/html; charset=utf-8");     messageSender.addSession(sessionId, event);     break;       case ERROR :      messageSender.removeSession(sessionId);      event.close();      break;     case END :      event.close();     log("End event");     break;

    case READ :      log("Read event");      break;   }  }

 @Override  public void destroy() {   // TODO Auto-generated method stub

 }

 @Override  public void log(String msg) {   servletContext.log(msg);  } }

MessageSender.java (Utility Class)

package javapattern;

import java.io.IOException; import java.io.PrintWriter; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue;

import javax.servlet.http.HttpServletResponse;

import org.jboss.servlet.http.HttpEvent;

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);

 public void send(String message) {   try {    messages.put(message);   } catch (InterruptedException ignore) {    // ignore   }  }

 public void addSession(String id, HttpEvent event) {   sessions.put(id, event);  }

 public void removeSession(String id) {   sessions.remove(id);  }

 public void stop() {   this.running = false;   this.executor.shutdown();  }

 @Override  public void run() {   while (running) {    String message = null;    try {     message = messages.take();    } catch (InterruptedException ignore) {     // ignore    }    for (String id : sessions.keySet()) {     executor.submit(new Task(id, message));    }   }  }

 private class Task implements Runnable {   private String sessionId;   private String message;

  public Task(String id, String msg) {    sessionId = id;    message = msg;   }

  public void run() {    HttpEvent event = sessions.get(sessionId);    if (null == event) {     return;    }    HttpServletResponse response = event.getHttpServletResponse();    PrintWriter out = null;    try {     out = response.getWriter();     out.println(message);     out.flush();     //response.setStatus(HttpServletResponse.SC_OK);     //response.flushBuffer();    } catch (IOException naive) {     naive.printStackTrace();    } finally {     try {      out.close();     } catch (Exception ignore) {     }     try {      event.close();     } catch (Exception ignore) {     }     sessions.remove(sessionId);    }   }  } }

MessageListener.java (ServletListener로 web.xml 등록)

package javapattern;

import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener;

public class ChatMessageSenderListener implements ServletContextListener {  protected MessageSender messageSender = null;

 @Override  public void contextDestroyed(ServletContextEvent sce) {   messageSender.stop();         messageSender = null;  }

 @Override  public void contextInitialized(ServletContextEvent sce) {   messageSender = new MessageSender();   Thread messageSenderThread = new Thread(messageSender, "MessageSender["     + sce.getServletContext().getContextPath() + "]");   messageSenderThread.setDaemon(true);   messageSenderThread.start();

  sce.getServletContext().setAttribute("messageSender", messageSender);  } }

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>jboss-comet</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
    <listener>
<listener-class>javapattern.ChatMessageSenderListener</listener-class></listener><servlet><servlet-name>ChatServlet</servlet-name><servlet-class>javapattern.ChatServlet</servlet-class></servlet><servlet><servlet-name>BroadcasterServlet</servlet-name><servlet-class>javapattern.BroadcasterServlet</servlet-class></servlet><servlet-mapping><servlet-name>ChatServlet</servlet-name><url-pattern>/chat</url-pattern></servlet-mapping><servlet-mapping><servlet-name>BroadcasterServlet</servlet-name><url-pattern>/broadcaster</url-pattern></servlet-mapping></web-app>

chat.jsp[WEB-INF/chat.jsp로 저장]

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
String ctxPath = getServletContext().getContextPath(); %>
<html>
<head>
<style type="text/css">
form {
margin:0;padding: 0;}
#panel {
height: 400px;width: 500px;background: #E3FFE3;overflow: auto;}
</style>
<script type="text/javascript" src="<%=ctxPath%>/js/jquery-1.3.2.min.js"></script>
<script type="text/javascript">
var dummmy=1;
function pollMessage() {
    $.ajax({
        type:'GET',
                success: function (msg) {
        $('#panel').append(msg);
    },
    complete:function(xhr, status) {
        pollMessage();
        
    }     
    });
}
function sendMessage(){
    $.ajax({
        type:'POST',
        url: '<%=ctxPath%>/chat',
        data: { name: $('#name').val(), text: $('#text').val() }
    });
    $('#text').val('');
    $('#text').focus();
    return false;
}
$(document).ready(function(){
pollMessage();});
</script>
</head>
<body>
<div id="panel"></div>
<form action="" method="POST" onsubmit="return sendMessage()">
    Nick <input type="text" name="name" id="name" size="10">
    <input type="text" name="text" id="text" size="40">
    <input type="submit" value="보내기">
</form>
</body>
</html>


Eclipse Project :


크리에이티브 커먼즈 라이센스
Creative Commons License
2011/06/29 14:41 2011/06/29 14:41

JBoss를 이용하여 sun management 패키지의 jmx를 사용하고자 할 경우 에러가 발생을 하게 됩니다.

$JBOSS_HOME/bin/run.sh를 사용할 경우에는 문제가 발생하지 않았었는데, 도메인 디렉토리를 따로 만들어 관리하는 방법을 사용하고 해당 실행 스크립트에 다음의 내용이 들어가 있으면 에러를 발생시킵니다.

# Enable the jconsole agent remotely on port 8888
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.port=8888"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.authenticate=false"
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.ssl=false"
export JAVA_OPTS

Error :
Error Content :
Error installing to Configured: name=ServiceBindingManager state=Configured: java.lang.Exception: Error calling callback JMXRegistrationAdvice for target context ServiceBindingManager

이에 대한 버그 리포트가 다음의 URL에 보고되었습니다.

이를 해결하기 위해서는 $JBOSS_HOME/bin/run.conf 마지막에 위의 workaround처럼 아래의 내용을 추가하도록 합니다.

JAVA_OPTS="$JAVA_OPTS -Djboss.platform.mbeanserver"
JAVA_OPTS="$JAVA_OPTS -Djavax.management.builder.initial=org.jboss.system.server.jmx.MBeanServerBuilderImpl"
JAVA_OPTS="$JAVA_OPTS -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
JAVA_OPTS="$JAVA_OPTS -Dorg.jboss.logging.Logger.pluginClass=org.jboss.logging.logmanager.LoggerPluginImpl"
jmx 관련 항목때문에 한참을 헤맸네요.
참조 URL :
크리에이티브 커먼즈 라이센스
Creative Commons License
2011/04/08 17:16 2011/04/08 17:16

JBoss AS 7 Beta나 3월 15일에 출시되었습니다.

특징적인 것은 EE6 Web Profile에 대한 여러 가지 기능들의 추가입니다. OSGi 형태의 애플리케이션 서버이며, 특히 주목할 만한 것은 부팅 시간이 놀새 컴퓨터(Intel Core Duo 2.4Ghz, Windows XP)에서 2.4초입니다.

참조 : http://in.relation.to/Bloggers/AS7BetaStreamBegins

사용자 삽입 이미지


원래 사용하던 Fedora였다면 조금 더 빨랐을 겁니다.
크리에이티브 커먼즈 라이센스
Creative Commons License
2011/03/22 09:29 2011/03/22 09:29

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.
Assume that we have a web application like this :
source : info.javapattern.servlet.TestServlet
war context : test

web.xml


<servlet>
    <servlet-name>invoker</servlet-name>
    <servlet-class>
          org.apache.catalina.servlets.InvokerServlet
        </servlet-class>
    <init-param>
      <param-name>debug</param-name>
      <param-value>2</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>invoker</servlet-name>
    <url-pattern>/servlet/*</url-pattern>
  </servlet-mapping>


위와 같은 설정이 되어 있을 경우 해당 컨텍스트의 서블릿 클래스는 다음과 같이 호출됩니다.
It is possible to access an written servlet class by name through the invoker as follow:

http://ip:port/context/invoker-url-pattern/pacakge.ServletClass

이를 실제 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 :


package org.jboss.servlets;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.jboss.logging.Logger;

public class SlashInvoker extends HttpServlet {

	private static final long serialVersionUID = 156894170597729161L;
	private int debug = 0;
	private static Logger log = Logger.getLogger(SlashInvoker.class);

	/** A serialized MarshalledInvocation */

	/**
	 * Initializes the servlet.
	 */
	@Override
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
		try {
			// Set our properties from the initialization parameters
			if (getServletConfig().getInitParameter("debug") != null)
				debug = Integer.parseInt(getServletConfig().getInitParameter("debug"));

		} catch (Exception e) {
			throw new ServletException("Failed to build invokerName", e);
		}

	}

	/**
	 * Destroys the servlet.
	 */
	@Override
	public void destroy() {

	}

	/**
	 * Handles the HTTP <code>GET</code> method.
	 * 
	 * @param request
	 *            servlet request
	 * @param response
	 *            servlet response
	 */
	@Override
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		processRequest(request, response);
	}

	/**
	 * Handles the HTTP <code>POST</code> method.
	 * 
	 * @param request
	 *            servlet request
	 * @param response
	 *            servlet response
	 */
	@Override
	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		processRequest(request, response);
	}

	/**
	 * Returns a short description of the servlet.
	 */
	@Override
	public String getServletInfo() {
		return "An HTTP to JMX invocation servlet";
	}

	/**
	 * Process servlet request using slash url pattern
	 * 
	 * @param request
	 *            servlet request
	 * @param response
	 *            servlet response
	 */
	protected void processRequest(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		try {
			if( debug > 0 ) {
				log.info("************ URI : " + request.getRequestURI());
				log.info("************ PATH INFO : " + request.getPathInfo());
			}
			String servletClass = request.getPathInfo();
			int slash = servletClass.indexOf('/');
			if (slash >= 0) {
				servletClass = servletClass.substring(slash + 1);
			}

			String inServletClass = servletClass.replace("/", ".");

			String redirectUrl = request.getContextPath() + "/catalina/"
					+ inServletClass;
			if( debug > 0 ) 
				log.info("************* Redirect URL : " + redirectUrl);

			// response.sendRedirect(redirectUrl);
			RequestDispatcher dispatcher = getServletContext()
					.getRequestDispatcher("/catalina/" + inServletClass);
			dispatcher.forward(request, response);

		} catch (Exception e) {
			throw new ServletException("Failed to build invokerName", e);
		}
	}
}


이 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:


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>jboss-jms-monitor</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  <servlet>
    <servlet-name>invoker</servlet-name>
    <servlet-class>
          org.jboss.servlets.SlashInvoker
        </servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>invoker</servlet-name>
    <url-pattern>/servlet/*</url-pattern>
  </servlet-mapping>
  <servlet>
    <servlet-name>CatalinaInvoker</servlet-name>
    <servlet-class>
          org.apache.catalina.servlets.InvokerServlet
        </servlet-class>
    <init-param>
      <param-name>debug</param-name>
      <param-value>2</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>CatalinaInvoker</servlet-name>
    <url-pattern>/catalina/*</url-pattern>
  </servlet-mapping>
</web-app>


테스트 웹 애플리케이션을 첨부합니다.
Refer to attached eclipse project file.
Eclipse 3.5 Project :

크리에이티브 커먼즈 라이센스
Creative Commons License
2011/03/09 10:30 2011/03/09 10:30

HornetQ는 기존의 JBoss Messaging 2버전 정도 되겠습니다. 비동기 메시징 통신을 위해 모든 코드들이 거의 재작성되었으며, 빠른 성능을 자랑합니다.

JBoss의 안타까운 점이라면 문서 때문에 국내 사용자들이 쉽게 접근하지 못하는 부분이 아닐까 생각합니다. 잡설은 그만하고 단일 서버에서 HornetQ의 클러스터링 방법에 대해 설명하겠습니다.

테스트하는 HornetQ는 2.1.2 버전입니다. 아래의 사이트에서 다운로드합니다.
http://www.jboss.org/hornetq

 HornetQ의 압축을 풀면 다음과 같은 디렉토리로 구성이 되어 있습니다.
사용자 삽입 이미지
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디렉토리를 보게 됩니다.

   <clustered>true</clustered>
   <paging-directory>${data.dir:../data}/paging</paging-directory>
   <bindings-directory>${data.dir:../data/bindings</bindings-directory>
   <journal-directory>${data.dir:../data}/journal</journal-directory>


위와 같이 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디렉토리 아래로 복사하십시오.
bin 디렉토리의 쉘은 다음과 같습니다.



For the English users :

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)

크리에이티브 커먼즈 라이센스
Creative Commons License
2011/02/23 13:07 2011/02/23 13:07

JBoss에서 JFreeChart를 만들어 JMX를 통한 Queue의 메시시 처리 상태 모니터입니다. 이클립스 프로젝트로 구성되어 있으며, JMS에 대한 전송 예제까지 같이 첨부합니다.

1 - JBoss Monitor.zip
2. JBoss JMS Standalone Send/Recv Example

크리에이티브 커먼즈 라이센스
Creative Commons License
2011/02/16 09:35 2011/02/16 09:35

env.cmd

@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%


크리에이티브 커먼즈 라이센스
Creative Commons License
2010/12/14 14:39 2010/12/14 14:39

Deploy이 되어지는 WAR 파일등에 XML API 관련 클래스가 존재하면 일반적으로 ClassCastException이 발생을 합니다.

jboss-app.xml이나 jboss-web.xml 파일에 classloader repository 속성을 주어도 이 부분은 해결이 되지를 않습니다.

아래의 jboss-classloading.xml을 구성하여 WEB-INF/ 위치시키십시오.




크리에이티브 커먼즈 라이센스
Creative Commons License
2010/11/18 13:38 2010/11/18 13:38