Version 14

    Introduction

     

    This wiki compliments the instructions provided to access EJB2 beans, EJB3 beans, JMS and JNDI to be accessed either via HTTP or HTTPS using JBoss Remoting based invokers with the changes required when clients are accessing these services via a NAT firewall. The instructions below assume that you have implemented the changes in that wiki.

     

    NAT firewalls are designed to receive external invocations via a public IP address and forward them to an internal server configured with a private IP address. The AS/EAP instance where JNDI and/or EJBs are deployed is generally an internal server which is bound to one of its private IP addresses via -b runtime parameter or -Djboss.bind.address system property. This is not enough in NAT environments because the AS/EAP instance needs to return proxies back to the client, which is outside the private network, that can talk to the NAT firewall via its public IP address, hence AS/EAP needs to return proxies that communicate via this public IP address. To achieve this, it's recommended defining a -Dpublic.firewall.address=<external_public_ip_host_address> system property and making sure such system property is used when creating these proxies.

     

    The wiki is split into two main sections, explaining first changes required if calls to EJBs, JMS and/or JNDI over HTTP will only come from outside the private network via the firewall and secondly, changes required if calls will come from both outside and inside the private network.

     

    Note: To use JMS (over JBoss Messaging) with Servlet invoker in Remoting, Remoting 2.2.2.SP12. This remoting version will be included in EAP 4.2.0.GA_CP07 and 4.3.0.GA_CP05.

     

    Calls Only Via Firewall

     

    This section focuses on the required changes to the EJB, JMS and JNDI over HTTP with Unified Invoker assuming that EJBs, JMS and JNDI accessed via HTTP will only be accessed via the NAT firewall. Steps belonging to this section contain a trailing 'a'.

     

    Server Side Setup

     

    Step 1a. Change any references to ${jboss.bind.address} in http-uinvoker.sar/unified-invoker.war/WEB-INF/web.xml to ${public.firewall.address}

     

    Step 2a. Change any references to ${jboss.bind.address} in http-uinvoker.sar/META-INF/jboss-service.xml to ${public.firewall.address}

     

    EJB2 Deployments Setup


    Step 3a. No changes required to EJB2 deployments.

     

    EJB3 Deployments Setup

     

    Step 4a. For EJB3 beans only, change references to ${jboss.bind.address} in the <client-bind-url> element within its META-INF/jboss.xml to ${public.firewall.address}. If you defined client bind url via @RemoteBinding annotation, you'll need to change the source code and recompile it.

     

    Client Code

     

    Step 5a. Make sure the java.naming.provider.url contains the public IP address.

     

    Calls Via Firewall And From Within Private Network

     

    This section focuses on the required to commuicate with EJBs and JNDI via HTTP both from within a private network and via a NAT firewall. The steps below are marked with the letter 'b'.

     

    Server Side Setup

     

    Step 1b. Grab a copy of JBoss AS 4.2.x+ or JBoss EAP 4.2.x/4.3.x+ and create a new server configuration (based on /default)

     

    Step 2b. Go to deploy/http-invoker.sar/invoker.war/WEB-INF/classes/ directory and ZIP up the classes into a JAR called jboss-http-naming.jar. Put this jar in the server's lib directory so that both the legacy and new unified NAT invokers can share these classes.

     

    Step 3b. Take the attached nat-invokers-v3.sar.zip file and unzip it in the deploy/ directory.

     

    Step 4b. Add -Dpublic.firewall.address=<external_public_ip_host_address> to the AS/EAP startup options

     

    EJB2 Deployments Setup

     

    Step 5b. Create two new EJB bean definitions in META-INF/ejb-jar.xml and META-INF/jboss.xml and configure one for private access and one for public access by setting a different <jndi-name> and a different container configuration for each. These configurations should contain  a <invoker-proxy-binding-name> element that points to a new <invoker-proxy-binding> whose invoker-mbean element should point to its corresponding unified invoker MBean definition on the server side (defined in the attached zip file). Example:

     

    META-INF/ejb-jar.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <ejb-jar xmlns="http://java.sun.com/xml/ns/j2ee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
             http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd"
             version="2.1">
      <enterprise-beans>
        <session>
          <ejb-name>TimeTellerEjbPrivateHttp</ejb-name>
          <home>com.acme.ejb2.slsb.TimeTellerHome</home>
          <remote>com.acme.ejb2.slsb.TimeTeller</remote>
          <ejb-class>com.acme.ejb2.slsb.TimeTellerBean</ejb-class>
          <session-type>Stateless</session-type>
          <transaction-type>Container</transaction-type>
        </session>       
    
        <session>
          <ejb-name>TimeTellerEjbPublicHttp</ejb-name>
          <home>com.acme.ejb2.slsb.TimeTellerHome</home>
          <remote>com.acme.ejb2.slsb.TimeTeller</remote>
          <ejb-class>com.acme.ejb2.slsb.TimeTellerBean</ejb-class>
          <session-type>Stateless</session-type>
          <transaction-type>Container</transaction-type>
        <session> 
      </enterprise-beans>
    </ejb-jar>
    

     

    META-INF/jboss.xml

     

    <?xml version="1.0"?>
    <!DOCTYPE jboss PUBLIC "-//JBoss//DTD JBOSS 4.0//EN"
            "http://www.jboss.org/j2ee/dtd/jboss_4_0.dtd">        
    <jboss>
      <enterprise-beans>
       <session>
         <ejb-name>TimeTellerEjbPrivateHttp</ejb-name>
         <jndi-name>ejb/TimeTellerEjbPrivateHttp</jndi-name>          
         <configuration-name>Unified Private Http Stateless SessionBean</configuration-name>
       </session>            
       <session>
         <ejb-name>TimeTellerEjbPublicHttp</ejb-name>
         <jndi-name>ejb/TimeTellerEjbPublicHttp</jndi-name>          
         <configuration-name>Unified Public Http Stateless SessionBean</configuration-name>
         </session>            
      </enterprise-beans>
        
      <invoker-proxy-bindings>    
        <invoker-proxy-binding>
          <name>stateless-unified-private-http-invoker</name>
          <invoker-mbean>jboss:service=invoker,type=unified,transport=servlet,loc=private</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 call-by-value="false">org.jboss.invocation.InvokerInterceptor</interceptor>
              <interceptor call-by-value="true">org.jboss.invocation.MarshallingInvokerInterceptor</interceptor>
            </home>
            <bean>
              <interceptor>org.jboss.proxy.ejb.StatelessSessionInterceptor</interceptor>
              <interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>
              <interceptor>org.jboss.proxy.TransactionInterceptor</interceptor>
              <interceptor call-by-value="false">org.jboss.invocation.InvokerInterceptor</interceptor>
              <interceptor call-by-value="true">org.jboss.invocation.MarshallingInvokerInterceptor</interceptor>
            </bean>
           </client-interceptors>
          </proxy-factory-config>
        </invoker-proxy-binding>
    
        <invoker-proxy-binding>
          <name>stateless-unified-public-http-invoker</name>
          <invoker-mbean>jboss:service=invoker,type=unified,transport=servlet,loc=public</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 call-by-value="false">org.jboss.invocation.InvokerInterceptor</interceptor>
               <interceptor call-by-value="true">org.jboss.invocation.MarshallingInvokerInterceptor</interceptor>
             </home>
             <bean>
               <interceptor>org.jboss.proxy.ejb.StatelessSessionInterceptor</interceptor>
               <interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>
               <interceptor>org.jboss.proxy.TransactionInterceptor</interceptor>
               <interceptor call-by-value="false">org.jboss.invocation.InvokerInterceptor</interceptor>
               <interceptor call-by-value="true">org.jboss.invocation.MarshallingInvokerInterceptor</interceptor>
             </bean>
           </client-interceptors>
          </proxy-factory-config>
        </invoker-proxy-binding>
                  
      </invoker-proxy-bindings>
        
      <container-configurations>
        <container-configuration extends="Standard Stateless SessionBean">
          <container-name>Unified Private Http Stateless SessionBean</container-name>
          <invoker-proxy-binding-name>stateless-unified-private-http-invoker</invoker-proxy-binding-name>
        </container-configuration>
    
        <container-configuration extends="Standard Stateless SessionBean">
          <container-name>Unified Public Http Stateless SessionBean</container-name>
          <invoker-proxy-binding-name>stateless-unified-public-http-invoker</invoker-proxy-binding-name>
        </container-configuration>
      </container-configurations>    
    </jboss>

     

    EJB2 Client Code

     

    Step 6b. The client code to be used varies slightly depending on whether the bean is accessed from within the private network or from the public network via the NAT firewall:

     

    • From private network, assuming AS/EAP is bound to 192.168.100.20:
    Properties p = new Properties();
    p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.HttpNamingContextFactory");
    p.put(Context.PROVIDER_URL, "http://192.168.100.20:8080/unified-invoker/PrivateJNDIFactory/?return-exception=true");
    p.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");      
    ctx = new InitialContext(p);
          
    TimeTellerHome home = (TimeTellerHome)ctx.lookup("ejb/TimeTellerEjbPrivateHttp");
    TimeTeller teller = home.create();
    System.out.println(teller.whatsTheTime());
    

     

    • From public network, assuming that the NAT firewalls public address is 12.35.146.211:
    Properties p = new Properties();
    p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.HttpNamingContextFactory");
    p.put(Context.PROVIDER_URL, "http://12.35.146.211:8080/unified-invoker/PublicJNDIFactory/?return-exception=true");
    p.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");      
    ctx = new InitialContext(p);
          
    TimeTellerHome home = (TimeTellerHome)ctx.lookup("ejb/TimeTellerEjbPublicHttp");
    TimeTeller teller = home.create();
    System.out.println(teller.whatsTheTime());

     

    EJB3 Deployments Setup

     

    Step 7b. Two remote bindings need to be added to the EJB3 bean(s) so that they can be accessed both ways which can be done either via annotations or via XML. It is recommended that the XML option is chosen for a couple of reasons:

    1. Bindings are linked to environmental setups and hence you wanna be decoupling those from the actual EJB source code.

    2. Annotations do not allow ${jboss.bind.address} type of system property substitutions, which forces the user to hardcode the IP/hostname in the source code.

     

    To use XML, create a META-INF/jboss.xml file in the ejb jar deployment and add something like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <jboss
      xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
      http://www.jboss.org/j2ee/schema/jboss_5_0.xsd"
      version="3.0">
    
      <enterprise-beans>
        <session>
          <ejb-name>TimeTellerBean</ejb-name>
          <remote-binding>
            <jndi-name>TimeTellerBean/http/private</jndi-name>
            <client-bind-url>
              http://${jboss.bind.address}:8080/unified-invoker/PrivateEjb3ServerInvokerServlet/?return-exception=true
            </client-bind-url>
          </remote-binding>
    
          <remote-binding>
            <jndi-name>TimeTellerBean/http/public</jndi-name>
            <client-bind-url>
              http://${public.firewall.address}:8080/unified-invoker/PublicEjb3ServerInvokerServlet/?return-exception=true
            </client-bind-url>
          </remote-binding>
        </session>
      </enterprise-beans>
    </jboss>

     

    If you still wanna use annotations in spite of disadvantages mentioned above, add the following annotations to the EJB3 bean, assuming that 192.168.100.20 is the private address where AS/EAP is bound and 12.35.146.211 is the public address exposed by the NAT firewall:

    @RemoteBinding(jndiBinding="TimeTellerBean/http/private",
      clientBindUrl="http://192.168.100.20:8080/unified-invoker/PrivateEjb3ServerInvokerServlet/?return-exception=true")
    @RemoteBinding(jndiBinding="TimeTellerBean/http/public",
      clientBindUrl="https://12.35.146.211:8443/unified-invoker/PublicEjb3ServerInvokerServlet/?return-exception=true")
    

     

    EJB3 Client Code

     

    Step 8b. The client code to be used varies slightly depending on whether the bean is accessed from within the private network or from the public network via the NAT firewall:

     

    • From private network, assuming AS/EAP is bound to 192.168.100.20
    Properties p = new Properties();
    p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.HttpNamingContextFactory");
    p.put(Context.PROVIDER_URL, "http://192.168.100.20:8080/unified-invoker/PrivateJNDIFactory/?return-exception=true");
    p.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");      
    ctx = new InitialContext(p);
          
    TimeTeller teller = (TimeTeller) ctx.lookup("TimeTellerBean/http/private");
    System.out.println(teller.whatsTheTime());
    

     

    • From public network, assuming that the NAT firewalls public address is 12.35.146.211:
    Properties p = new Properties();
    p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.HttpNamingContextFactory");
    p.put(Context.PROVIDER_URL, "http://12.35.146.211:8080/unified-invoker/PublicJNDIFactory/?return-exception=true");
    p.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");      
    ctx = new InitialContext(p);
          
    TimeTeller teller = (TimeTeller) ctx.lookup("TimeTellerBean/http/public");
    System.out.println(teller.whatsTheTime());

     

    JMS Client Code

    Step 9b. The client code to be used varies slightly depending on whether messages to JMS server are sent from within the private network or from the public network via the NAT firewall:

     

    • From private network, assuming AS/EAP is bound to 192.168.100.20
    Properties p = new Properties();
    p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.HttpNamingContextFactory");
    p.put(Context.PROVIDER_URL, "http://192.168.100.20:8080/unified-invoker/PrivateJNDIFactory/?return-exception=true");
    p.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");      
    ctx = new InitialContext(p);
          
    ConnectionFactory cf = (ConnectionFactory)ic.lookup("/PrivateServletConnectionFactory");
    Queue queue = (Queue)ic.lookup("queue/testQueue");
    

     

    • From public network, assuming that the NAT firewalls public address is 12.35.146.211:
    Properties p = new Properties();
    p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.HttpNamingContextFactory");
    p.put(Context.PROVIDER_URL, "http://12.35.146.211:8080/unified-invoker/PublicJNDIFactory/?return-exception=true");
    p.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");      
    ctx = new InitialContext(p);
    
    ConnectionFactory cf = (ConnectionFactory)ic.lookup("/PublicServletConnectionFactory");
    Queue queue = (Queue)ic.lookup("queue/testQueue");
    

     

     

    Testing NAT Locally

     

    Step 9b. Finally, it can sometimes take time and effort to create a full blown NAT environment but if you wanna test things quickly, you can configure a Linux firewall to mock NAT behaivour locally. The simplest set up would be your AS/EAP to be bound to localhost and to modify  iptables so that eth0 is redirected to localhost in such way that localhost mocks your private address and eth0 becomes your public NAT address. Requests send to eth0 would be forwarded to localhosts. Example:

     

    Backup your current firewall rules and execute a script like this (eth0 is assumed to be 12.15.146.211):

    iptables --flush
    iptables --table nat --flush
    iptables --delete-chain
    iptables --table nat --delete-chain
    
    iptables -t nat -A OUTPUT  -d 12.15.146.211 -p tcp --dport 8080 -j REDIRECT --to-ports 8080
    iptables -t nat -A PREROUTING -d 12.15.146.211 -p tcp --dport 8080 -j REDIRECT --to-ports 8080
    

    You should now be able to access 12.15.146.211:8080 and such access be redirected to localhost:8080.