JBoss / WildFly (全部俺) Advent Calendar 2013の11日目です。
EJBのリモート呼び出しは通常はコンテナが備えるプロプライエタリなプロトコル(native protocol)で呼び出されますが、RMI-IIOPだったりJAX-WS, JAX-RSなどからも呼び出せます。というわけで全部から呼び出せる呼び出しインタフェース豊富なEJBを作ってみようと思います。
最終的に以下の6クラスできました。
- EJB本体
- EJB Remote Interface
- EJB Local Interface (with JAX-RS, JAX-WS annotations)
- JAX-RS Application
- EJB2 Home Interface
- EJB2 Remote Interface
EJBのリモート呼び出しにはリモートインタフェースが必要です。また、JAX-RSやJAX-WSはEJBのリモートインタフェースから呼び出す必要はなく、EJBローカルインタフェースを使うことになりますから、ローカルインタフェースも必要になります。@LocalBeanを利用したNo-interface viewはローカル限定でJAX-RSなどとは組み合わせ可能ですが、EJBリモートインタフェースとの組み合わせができなくなってしまうため今回は登場しません。
RMI-IIOPはEJB2のビューを要する機能なのでejb-jarじゃないと認識されません。そのため、クラスは全てejb-jarにパッケージし、JAX-RSを有効化するために空のWARと共にEARにしなければなりません。RMI-IIOPなEJBをWARにパッケージングして、META-INF/jboss-ejb3.xmlやWEB-INF/jboss-ejb3.xmlも含めるテストもやってみたのですが当然のごとくIIOP側にバインドされませんでした。普通はRMI-IIOPなんていう超レガシーは使わないのでWAR一個で大丈夫なはずです。
あとWARにした場合にちょっともにょったのですが、JRubyクライアントから参照しようと思った時にWARファイルはクラスパスに含めることができないので、target/classesを参照するようにたり、ライブラリjarを作ったりしないとダメなのがまた微妙です。
ソースのツリーはhttps://github.com/nekop/java-examples/tree/master/ee6-ejb-interfacesに、ファイル一覧はhttps://github.com/nekop/java-examples/tree/master/ee6-ejb-interfaces/src/main/java/com/github/nekop/examplesにあります。すごい適当に書いてます。
クライアントはパラメータにnative, iiop, ws, rsがあり、呼び出しインタフェースを切り換えるようにしました。JAX-RSのクライアントはいかようにも実装できるのですが、とりあえずRESTEasyのふつうのクライアントAPIで実装しています。
require 'java' JBOSS_HOME="/home/nekop/eap6" require "./target/ee6-ejb-interfaces.jar" require "#{JBOSS_HOME}/bin/client/jboss-client.jar" require "#{JBOSS_HOME}/modules/system/layers/base/org/jboss/resteasy/resteasy-jaxrs/main/resteasy-jaxrs-2.3.7.Final-redhat-2.jar" require "#{JBOSS_HOME}/modules/system/layers/base/javax/ws/rs/api/main/jboss-jaxrs-api_1.1_spec-1.0.1.Final-redhat-2.jar" require "#{JBOSS_HOME}/modules/system/layers/base/org/apache/httpcomponents/main/httpclient-4.2.1-redhat-1.jar" require "#{JBOSS_HOME}/modules/system/layers/base/org/apache/httpcomponents/main/httpcore-4.2.1-redhat-1.jar" require "#{JBOSS_HOME}/modules/system/layers/base/org/slf4j/jcl-over-slf4j/main/jcl-over-slf4j-1.7.2.redhat-2.jar" require "#{JBOSS_HOME}/modules/system/layers/base/org/slf4j/main/slf4j-api-1.7.2.redhat-2.jar" java_import "java.util.Properties" java_import "javax.naming.Context" java_import "javax.naming.InitialContext" def initial_context p = Properties.new() p.put("remote.connections", "default") p.put("remote.connection.default.port", "4447") p.put("remote.connection.default.host", "localhost") p.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false") p.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming") p.put("org.jboss.ejb.client.scoped.context", true) InitialContext.new(p) end def corba_initial_context p = Properties.new() p.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.cosnaming.CNCtxFactory") p.put(Context.PROVIDER_URL, "corbaloc:iiop:localhost:3528/JBoss/Naming/root") InitialContext.new(p) end def hello_slsb(ejb_context) ear_name = "ee6-ejb-interfaces" ejbjar_name = "ee6-ejb-interfaces" ejb_name = "Hello" interface_name = "com.github.nekop.examples.HelloRemote" ejb_context.lookup("#{ear_name}/#{ejbjar_name}/#{ejb_name}!#{interface_name}") end type = ARGV.shift case type when "native" ejb_context = initial_context.lookup("ejb:") begin bean = hello_slsb(ejb_context) bean.hello("world") ensure begin ejb_context.close rescue # no-op end end when "iiop" java_import "com.github.nekop.examples.HelloEJB2Home" java.lang.System::setProperty("com.sun.CORBA.ORBUseDynamicStub", "true") o = corba_initial_context.lookup("Hello") home = javax.rmi.PortableRemoteObject.narrow(o, HelloEJB2Home.java_class) bean = home.create() bean.hello("world") when "ws" java_import "java.net.URL" java_import "javax.xml.namespace.QName" java_import "javax.xml.ws.Service" java_import "com.github.nekop.examples.HelloLocal" ejbjar_name = "ee6-ejb-interfaces" ejb_name = "Hello" wsdlLocation = URL.new("http://127.0.0.1:8080/#{ejbjar_name}/#{ejb_name}?wsdl") serviceName = QName.new("http://examples.nekop.github.com/", "#{ejb_name}Service") portName = QName.new("http://examples.nekop.github.com/", "#{ejb_name}Port") service = Service.create(wsdlLocation, serviceName) bean = service.getPort(portName, HelloLocal.java_class) bean.hello("world") when "rs" java_import "org.jboss.resteasy.client.ClientRequest" war_name = "ee6-ejb-interfaces-web" url = "http://localhost:8080/#{war_name}/rest/hello/world" request = ClientRequest.new(url) request.get(java.lang.String.java_class); else puts "unko" end