Overview
This wiki outlines the various steps required to use a Stateless Session Bean over HTTP. This is a common configuration where a server is behind a firewall or in a DMZ, with external clients. The server does not have the correct hostname or Ip address available locally, because it is only available externally on the other side of the firewall. This configuration will allow clients to use http to communicate with that server behind the firewall.
The code, build script and configuration files are all attached at the bottom of this page
Setup
Grab a copy of a JBossAS 4.0.5 and up release and create a new server configuration (based on /default).
Server Side
Step 1. Add stateless-http-invoker proxy binding to your jboss.xml file(usually called standardjboss.xml in /server/default/conf directory.
<!-- A custom invoker for RMI/HTTP --> <invoker-proxy-binding> <name>stateless-http-invoker</name> <invoker-mbean>jboss:service=invoker,type=http</invoker-mbean> <proxy-factory>org.jboss.proxy.ejb.ProxyFactory</proxy-factory> <proxy-factory-config> <client-interceptors> <home> <interceptor>org.jboss.proxy.ejb.HomeInterceptor</interceptor> <interceptor>org.jboss.proxy.SecurityInterceptor</interceptor> <interceptor>org.jboss.proxy.TransactionInterceptor</interceptor> <interceptor>org.jboss.invocation.InvokerInterceptor</interceptor> </home> <bean> <interceptor>org.jboss.proxy.ejb.StatelessSessionInterceptor</interceptor> <interceptor>org.jboss.proxy.SecurityInterceptor</interceptor> <interceptor>org.jboss.proxy.TransactionInterceptor</interceptor> <interceptor>org.jboss.invocation.InvokerInterceptor</interceptor> </bean> </client-interceptors> </proxy-factory-config> </invoker-proxy-binding>
Step 2. Change the invoker binding on the Standard Stateless SessionBean.
In the same file(standardjboss-xml), search for and edit the entry "Standard Stateless SessionBean".
Inside of the container-configuration that has that name, change the invoker proxy binding(using the binding you created above)
from
<invoker-proxy-binding-name>stateless-rmi-invoker</invoker-proxy-binding-name>
to
<invoker-proxy-binding-name>stateless-http-invoker</invoker-proxy-binding-name>
This will allow you to use http as an invoker for all of your Session beans.
Keep in mind that if you change your configuration-name for your bean, you will need to also change the invoker-proxy-binding-name for that element in the jboss.xml file. This connects the invoker to your session bean. You can also specify the invoker per session bean in the session bean descriptor, instead of globally in the container-configuration.
<!-- Example bean to show the use of the configuration-name element link with the invoker--> <enterprise-beans> <session> <ejb-name>MySession</ejb-name> <jndi-name>my.Session</jndi-name> <configuration-name>Standard Stateless SessionBean</configuration-name> </session> </enterprise-beans>
Step 3. Configure JNDI to use HTTP(with an external address) in the server/$configuration/deploy/http-invoker.sar/META-INF/jboss-service.xml file
JNDI should already be exposed in the default configuration, but if you plan to access your application through a firewall/NAT or some other device, then you must configure your JNDI with an InvokerURL. If you don't go through a firewall or some other network address translation, you can skip this step.
By default, the name and address of the local box is used. So If my local box is named foo at 192.168.0.125(local address), but external clients know my host as foo2.bogus.com with an ip address of 215.x.x.x(traffic forwarded to 192.168.0.125), then there is a problem. Because once contact is made, the url of of the jndi server is sent to the client and that is what the client uses. So if we left our configuration the same, clients on the outside of my firewall would try to hit either 192.168.0.125 or foo, niether of which will work.
The important thing is her is that the external server name and port are used when setting the InvokerURL.
<!-- Expose the Naming service interface via HTTP --> <mbean code="org.jboss.invocation.http.server.HttpProxyFactory" name="jboss:service=invoker,type=http,target=Naming"> <!-- The Naming service we are proxying --> <attribute name="InvokerName">jboss:service=Naming</attribute> <attribute name="InvokerURL">http://[externalServer]:[externalPort]/invoker/JMXInvokerServlet</attribute> <attribute name="ExportedInterface">org.jnp.interfaces.Naming</attribute> <attribute name="JndiName"></attribute> </mbean> <!-- Expose the Naming service interface via clustered HTTP. This maps to the ReadOnlyJNDIFactory servlet URL--> <mbean code="org.jboss.invocation.http.server.HttpProxyFactory" name="jboss:service=invoker,type=http,target=Naming,readonly=true"> <attribute name="InvokerName">jboss:service=Naming</attribute> <attribute name="InvokerURL">http://[externalServer]:[externalPort]/invoker/JMXInvokerServlet</attribute> <attribute name="ExportedInterface">org.jnp.interfaces.Naming</attribute> <attribute name="JndiName"></attribute> </mbean>
Step 4. Change your HttpInvoker if you go through a firewall, or some network translation in the server/$configuration/deploy/http-invoker.sar/META-INF/jboss-service.xml file
By default the server uses a prefix and a postfix and your hostname/ip to create the url that gets sent to the clients. You should override the default if your local hostname/ipaddress on the server is different from what your external clients know about. This is the case for any Network translation.
<!-- The HTTP invoker service configration--> <mbean code="org.jboss.invocation.http.server.HttpInvoker" name="jboss:service=invoker,type=http"> <attribute name="InvokerURL">http://[external server]:[portE]/invoker/EJBInvokerServlet</attribute> </mbean> <!-- The HTTP invoker service configration for Cluster--> <mbean code="org.jboss.invocation.http.server.HttpInvokerHA" name="jboss:service=invoker,type=httpHA"> <attribute name="InvokerURL">http://[external server]:[portE]/invoker/EJBInvokerHAServlet</attribute> </mbean>
Client
The client must have the following set to determine the initial context
Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.HttpNamingContextFactory Context.PROVIDER_URL, "https://[externalHost]:[externalPort]/invoker/JNDIFactory"
Making the Call from an external client
package jmslab.client; import java.util.Properties; import javax.naming.Context; import javax.naming.InitialContext; import javax.jms.*; public class EJBClient { public static void main(String args[]) throws Exception { Properties env = new Properties(); //notice that we are using the HttpNamingContextFactory env.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.HttpNamingContextFactory"); env.setProperty(Context.PROVIDER_URL, "https://[externalHost]:[externalPort]/invoker/JNDIFactory"); try { //create the initial context given the properties above Context ctx = new InitialContext(env); System.out.println("Created InitialContext, env= " + env); //look up a bean. Used the trailblazer example http://trailblazer.demo.jboss.com/EJB3Trail/serviceobjects/slsb/index.html Calculator cal = (Calculator) ctx.lookup("EJB3Trail/StatefulCalculator/remote"); //call the method double result = cal.calculate (25, 65, .08, 300); System.out.println("result is "+result); } catch(Exception e) { e.printStackTrace(); } } }
Step 5
HttpInvoker security
remove unsecure servlet into web.xml default/deploy/http-invoker.sar/invoker.war/WEB-INF/web.xml
Don't forget to use jaas configuration file. You could find an example in jboss/client/auth.conf
String authConf = this.getClass().getResource("auth.conf").toString(); System.setProperty("java.security.auth.login.config", authConf); //user:admin password:admin AppCallbackHandler handler = new AppCallbackHandler("admin", "admin".toCharArray()); Properties env=null; InitialContext ctx=null; try { LoginContext lc = new LoginContext("testSecureHttpInvoker", handler); lc.login(); env = new Properties(); env.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.HttpNamingContextFactory"); env.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces"); env.setProperty(Context.PROVIDER_URL, "http://localhost:8080/invoker/restricted/JNDIFactory"); ctx = new InitialContext(env); } catch (LoginException e) { logger.error(e,e); } catch (NamingException e) { logger.error(e,e); }
Referenced by:
Comments