Java RMI, EJB JNDI 사용법 정리
업데이트:
Java RMI, EJB JNDI 사용법 정리
JAVA RMI
JAVA 에서 제공하는 RMI 사용방식으로 매우 간단하고, WAS(Middleware) 에 종속적이지 않다.
무슨말인가 하면, WAS 에서는 WAS 내부에 REMOTE 설정이 가능하고,
weblogic은 t3://IP:PORT, jboss는 remote://IP:PORT 를 사용한다. http:// 가 아니라는말.
JAVA RMI 도 다른데 rmi://IP:PORT 이다.
JAVA RMI 의 특징으로는 rmiregistry 를 기동시켜서 설정한 IP, PORT 로 서버를 기동해야 하는 불편함이 있다.
아래는 JAVA RMI SAMPLE.
- Interface
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.Date;
public interface Test extends Remote
{
String testLog(String keyword, String term, String logPath) throws RemoteException;
}
- Implement
main 함수가 rmiregistry 를 실행하는 것과 같은 역할을 수행한다.
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class TestImpl implements Test{
public TestImpl() throws RemoteException {
super();
// TODO Auto-generated constructor stub
}
public String testLog(String keyword, String term, String logPath)
{
int iTerm = 0;
try
{
System.out.println("REMOTE METHOD INVOKED");
} catch (Exception e)
{
// do nothing...
}
return "GOOD";
}
//rmi://127.0.0.1:1099/TESTAPP 으로 호출 하면 TestImpl 객체를 return 해줘, 해당 객체의 method를 사용할 수 있게 해줌.
public static void main(String[] args){
try {
System.setProperty("java.rmi.server.hostname", "127.0.0.1");
System.out.println("java.rmi.server.hostname = "+System.getProperty("java.rmi.server.hostname"));
Test app = new TestImpl();
Test appStub = (Test)UnicastRemoteObject.exportObject(app, 0);
Registry registry = LocateRegistry.createRegistry( 1099 );
registry = LocateRegistry.getRegistry();
registry.rebind("TESTAPP", appStub);
System.out.println("RMI Server Ready. URL = //127.0.0.1:1099/TESTAPP");
} catch (Exception e) {
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
}
}
- Client 쪽에서 사용시
import java.rmi.Naming;
Test test = (TESTEAPP) Naming.lookup("rmi://127.0.0.1:1099/TESTAPP");
System.out.println(test.testLog("asdfas","asdfasd","asdf"));
JBoss EJB JNDI(RMI) 사용법
내가 테스트 하면서 가장 애를 먹고 시간을 낭비한 부분이 바로 standalone.bat -b 111.222.33.444 로 서버를 기동안시킨 부분이였다.
-b 옵션은 binding 옵션인데, ip 를 지정해서 서버를 기동 시키는데에 사용되어 보인다.
자 이슈가 무엇이였길래 이렇게 시작부터 언급하느냐.... 이유는 이렇다.
jboss 는 remote://127.0.0.1:4447 이 기본으로 기동이 된다. 그래서 테스트시,
remote://127.0.0.1:4447 로 테스트를 시도하는게 정상적인 방법이다. 근데 일반적으로
127.0.0.1 의 static 주소인 기타 172.333.222.111 등등의 IP 로도 당연히 될것으로 착각하게 된다.
내 로컬 IP 가 1.2.3.4 였다면 remote://127.0.0.1:4447 도 되고 , remote://1.2.3.4:4447 도 될것으로 당연히 생각하게 된다.
하지만!!!! 테스트하면 127.0.0.1 만 된다..
더 힘든 부분은, 실제로 remote 서버를 만들어서 테스트를 진행 할때, 서버로그에서 어떤 IP 로 remote 를 설정해서 올라오는지 확인 안하게 된다면,
희안한 에러로 인해 세월을 보내게 될 수 있다. 그 에러는 바로 아래의 에러들.
javax.naming.AuthenticationException: Failed to connect to any server. Servers tried: [remote://1.2.3.4:4447 (Authentication failed: the server presented no authentication mechanisms)] [Root exception is javax.security.sasl.SaslException: Authentication failed: the server presented no authentication mechanisms]
javax.naming.CommunicationException: Failed to connect to any server. Servers tried: [remote://1.2.3.4:4447 (java.net.ConnectException: Connection refused: no further information)]
Jboss RMI (EJB JNDI 가 여기에서 사용한 방식)를 테스트 할 때, 가장 중요한 부분 3가지를 언급한다.
1. 서버 기동시에 뜨는 remote IP/PORT 를 확인한다.
2. application user 를 등록하여 해당 계정으로 사용한다.
3. standalone.bat or .sh 로 기동시 -b 옵션으로 로컬 서버의 IP 를 박아서 뜨운다.
기본적으로 standalone.bat 를 실행 시키면, remote 서버가 내부적으로 기동되는 것 같다. 특별한 설정은 만지지 않았다.
Jboss 6.4 사용
RMI 를 기록하는데 왜 갑자기 EJB JNDI 가 나오나 싶을것 같다.
EJB 내부에 RMI 기능이 내장되어 있는것으로 알고 있다.
EJB 를 만들고, 위의 JAVA RMI 처럼 객체를 받기 위해서는, JNDI Context lookup 을 사용하여 해당 객체를 서버에서 받는방식으로 사용했다.
나도 잘 아는 부분이 아니라서 그냥 경험을 바탕으로 기록해 둔다.
.ear 파일 안에
XXXEJB.jar 파일인 EJB 및 관련 class를 묶은 jar 파일과
XXXWEB.war 파일인 Web Application 관련 파일들을 묶은 war 파일이 들어간다.
.ear 파일 안에 META-INF 폴더안에 application.xml 을 작성하는데 아래는 샘플
<?xml version = '1.0' encoding = 'UTF-8'?>
<!DOCTYPE application PUBLIC "-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN" "http://java.sun.com/dtd/application_1_3.dtd">
<application>
<display-name>TESTAPP</display-name>
<module>
<ejb>TESTEJB.jar</ejb>
</module>
<module>
<web>
<web-uri>TESTWEB.war</web-uri>
<context-root>/TEST</context-root>
</web>
</module>
<module>
<ejb>TESTEJB2.jar</ejb>
</module>
<module>
<web>
<web-uri>TESTWEB2.war</web-uri>
<context-root>/TEST2</context-root>
</web>
</module>
</application>
폴더 구조
TESTAPP.ear
- TESTEJB.jar
- TESTWEB.war
- TESTEJB2.jar
- TESTWEB2.war
- META-INF(폴더)
- application.xml
- MANIFEST.MF (여기엔 내용이 별게 없었다. 아래 두줄이 전부)
Manifest-Version: 1.0
Created-By: 1.6.0_35 (Sun Microsystems Inc.)
이제부터는 TESTEJB.jar 와 TESTWEB.war 의 자원에 EJB-JNDI 를 설정해서 사용하는 법을 설명한다.
Middleware 인 Weblogic과 Jboss 는 Bender 사 별로 INITIAL_CONTEXT_FACTORY 를 지정하여 사용한다.
- JBoss Client 에서 EJB 호출시 사용하는 소스
Context ctx = null;
Hashtable env = new Hashtable();
env.put(Context.PROVIDER_URL, rmiInfo.get(PosAnalyzerConstants.URL));
env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory"); //jboss 사용시
env.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
env.put("jboss.naming.client.ejb.context", "true");
env.put("jboss.naming.client.connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT", "false");
env.put(Context.SECURITY_PRINCIPAL, "user");
env.put(Context.SECURITY_CREDENTIALS, "password");
ctx = new InitialContext(env);
TestHome home = (TestHome) ctx.lookup("TESTAPP/TESTEJB/Test!com.test.TestHome");
Test test = (Test)home.create();
test.runMethod();
정신건강을 위해 JBoss6.4 를 로컬에 설치해 놓고 테스트 하기 바란다.
JAVA_HOME 환경변수가 잡혀있어야 한다.
standalone.bat 로 실행시켜서 테스트 성공한 케이스이다.
zip 으로 받은 Jboss6.4 에서 bin/standalone.bat 를 실행했고
standalone/configuration/standalone.xml 에서 아래 내용이 remote 부분에 대한 설정으로 알고 있다.
상세 설정이 있는것 같지만, 대충 짐작으로는 <remote> 가 remote 연결을 한다는 의미의 설정으로,
<socket-binding> 이 어떤 PORT 로 remote 를 받을건지 설정하는 것으로 이해했다.
<remote connector-ref="remoting-connector" thread-pool-name="default"/>
<socket-binding name="remoting" port="4647"/>
remote://127.0.0.1:4647 이 JBoss 의 remote URL 이 된다.
Client 에서 EJB JNDI 를 사용하기 위해서는 library 가 필요한데, JBoss6.4 에 bin/client 폴더가 있다
거기에 jboss-cli-client.jar, jboss-client.jar 를 classpath 에 등록해주면 된다. 적어도 내가 만든 소스에서는 되었다..
EJB Session Bean 을 ejb-jar.xml 파일에 정의 해두고,
TESTEJB.jar 의 META-INF 폴더에 넣는다.
```xml
<?xml version = '1.0' encoding = 'EUC-KR'?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd">
<ejb-jar>
<enterprise-beans>
<session>
<description>Session Bean ( Stateless )</description>
<display-name>Test</display-name>
<ejb-name>Test</ejb-name>
<home>com.test.TestHome</home>
<remote>com.test.Test</remote>
<ejb-class>com.test.TestBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
</ejb-jar>
같은 META-INF 폴더 안에 jboss-ejb-client.xml 파일을 만들고
아래의 내용을 기술한다.
<jboss-ejb-client xmlns="urn:jboss:ejb-client:1.0">
<client-context>
<ejb-receivers>
<remoting-ejb-receiver outbound-connection-ref="remote-ejb-connection"/>
</ejb-receivers>
</client-context>
</jboss-ejb-client>
TESTWEB.war 안에 META-INF 폴더 안에 web.xml 을 만들고 설정을 한다.
Servlet 3.0 이상부터는 보통 Annotation 기반의 sample 소스가 많은듯 하다. 이거는 옜날 동작방식에서
못벗어 나는 상황에서 수정이 발생할 때, 이해를 하기 위해 적어 놓았다.
<?xml version = '1.0' encoding = 'EUC-KR'?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<description>Empty web.xml file for Web Application</description>
<servlet>
<servlet-name>TestController</servlet-name>
<servlet-class>com.test.TestController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestController</servlet-name>
<url-pattern>/testurl</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<mime-mapping>
<extension>html</extension>
<mime-type>text/html</mime-type>
</mime-mapping>
<mime-mapping>
<extension>txt</extension>
<mime-type>text/plain</mime-type>
</mime-mapping>
<mime-mapping>
<extension>jnlp</extension>
<mime-type>application/x-java-jnlp-file</mime-type>
</mime-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
해당 자원들의 설정 정보가 정상적으로 들어가 있는 상태에서
TESTAPP.ear 를 JBoss6.4 의 standalone/deployments 안에 복사해 두고
bin/standalone.bat 를 실행하면 서버 기동이 되는데, 정상적으로 떳다면,
아래와 같은 로그가 보여야 한다.
10:39:25,780 INFO [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-8) JNDI bindings for session bean named Test in deployment unit su
bdeployment "TESTEJB.jar" of deployment "TESTEAPP.ear" are as follows:
java:global/TESTEAPP/TESTEJB/Test!com.test.TestHome
java:app/TESTEJB/Test!com.test.TestHome
java:module/Test!com.test.TestHome
java:jboss/exported/TESTEAPP/TESTEJB/Test!com.test.TestHome
java:global/TESTEAPP/TESTEJB/Test!com.test.Test
java:app/TESTEJB/Test!com.test.Test
java:module/Test!com.test.Test
java:jboss/exported/TESTEAPP/TESTEJB/Test!com.test.Test
이런 로그가 나온다는 것을 JNDI Binding 이 되었다는 의미로, ejb-jar.xml 에 기술한 내용이 등록되었다는 의미이다.
// 위에 아래의 샘플 소스가 있었는데, lookup 부분을 보면
// java:jboss/exported/TESTEAPP/TESTEJB/Test!com.test.TestHome 와 비슷함을 알 수 있다.
// 잘은 모르겠지만, 지금 내가 설정한 Context 의 상태에서는 TESTEAPP/TESTEJB/Test!com.test.TestHome 를 넣으면
// 정상적으로 해당 객체를 리턴해 준다.
// 테스트 서버에서 테스트시, ID, PWD 관련 에러가 나는데, 설정의 문제이지, RMI 에러와 관련된 부분은 아닌듯하다.
// Remote 로 접속할때 사용하는 인증 방식 설정이 없거나.. 잘못 되어 있는것이 아닐가.. 생각해본다..
// 로컬에서 잘 되는 이유는 Local auth 동작방식으로 동작하기 때문에, 사용자 계정정보와 관계없이 bypass 되는듯해서 인듯 하다.
// 참고로 사용자를 admin 과 application user 2명 이 필요한데, bin/add-user.bat 로 a 는 admin b 는 application user 를 등록할 수 있다.
// 만드려고 하다보면 어려운점은 없는데, group 할당을 할 거냐고 물어 본다. 안하면 default 그룹을 만들고 이 이름이 괜찮냐고 물어 본다. yes 하면 됩.
TestHome home = (TestHome) ctx.lookup("TESTAPP/TESTEJB/Test!com.test.TestHome");
Weblogic EJB JNDI(RMI) 사용법
JBoss에 설명한 내용의 대부분이 그대로 사용된다.
다른 부분은 사용할때 필요로 하는 library 와 Context.INITIAL_CONTEXT_FACTORY 와 같은 Context 설정 정보 내용이
weblogic 용으로 마춰져야 한다. 사실 기존에 이미 되는 소스가 있어서, 정확히 어떤 설정들이 되어야 하는지
로컬 테스트를 못하고 그냥 일단 기록해 둔다.
다행히, weblogic EJB JNDI 는 인터넷에 좀 샘플이 괜찮게 있는편이라고 생각한다.
아래의 library 들이 기존에 있었다. 사실 어디까지가 EJB JNDI 의 범주에 해당하는지를 몰라 일단 다 적어놓는다.
wlthint3client.jar
jndi.jar
javax.ejb_3.0.1.jar
javax.servlet_1.0.0.0_2-5.jar
jmxri.jar
- Welogic용 Client가 EJB JNDI 사용시 사용하는 소스
// 왜 TESTAPP 으로 이름이 따지는가 생각해 보면, 기본적으로 .ear 파일의 이름을 따서 만드는 듯 하다.
// 이 부분은 변경이 가능할 것 같고, 정확하지 않으니 믿지 말것. 그냥 추측일 뿐. 로그에 정보가 나오는 지도 확인
// 안해본 상태의 정보임.
Context ctx = null;
Hashtable env = new Hashtable();
env.put(Context.PROVIDER_URL, rmiInfo.get(PosAnalyzerConstants.URL));
env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory"); //weblogic 사용시
env.put(Context.SECURITY_PRINCIPAL, "user");
env.put(Context.SECURITY_CREDENTIALS, "password");
ctx = new InitialContext(env);
TestHome home = (TestHome) PortableRemoteObject.narrow(ctx.lookup("TESTAPP"), "com.test.TestHome");
Test test = (Test)home.create();
test.runMethod();
마지막으로 JBoss 의 기본 REMOTE PORT 는 4447, Weblogic 은 9403 인듯하다.