7 Replies Latest reply on Dec 15, 2003 3:44 AM by macamat

    <ejb-link> problem - multiple deployments getting wires cros

    macamat

      Hi forum,
      This is my first post - I am relatively new to JBoss and have managed to solve all previous problems by searching through these forums, but I have finally hit a snag. This will take some explaining though -

      I have developed a Struts/EJB application which acts as a blog/photo journal site, running on JBoss 3.2.1 on WinXP (for now). It works nicely. However, I am trying to deploy this application multiple times onto the same JBoss instance; the idea is to give my friends their own independent blog sites etc on my server.

      So to make this work I have configured my ant build so that the following properties of the application are modified before deployment:
      a) application.xml is modified so that the context root is different
      b) The datasource property of the element within the ant task is modified to point to a different datasource.
      c) All references to EJBs (only local references are used) have their context modified to include the application name, to avoid JNDI context clashes. This is done using an ant task.
      So for example:
      ejb/SequenceGenerator
      becomes
      ejb/foo/SequenceGenerator
      in one deployment and
      ejb/bar/SequenceGenerator
      in another.
      d) The ear and jar files are named uniquely
      e) The web.xml file of my .war deployment has its <ejb-local-ref> elements modified so that the <ejb-ref-name> properties match c) and the <ejb-link> properties match d).

      If I run my script twice with different application names I end up with two .ear files which look something like this:

      foo.ear
      + foo.jar (EJB module)
      + photoserver.war (Struts module)

      bar.ear
      + bar.jar (EJB module)
      + photoserver.war (Struts module)

      All communication between the struts module and the ejb module is through local references to stateless session beans.

      Either of these applications work fine when deployed by themselves. However when I deploy them both, the second deployed application will fail as its struts module will retrieve a session bean from the first deployment's EJB module instead of its own - this will eventually end in the following exception (in the case of deploying foo first, then deploying bar, and attempting to access bar):

      Note that the message in the exception - 'foo not bound' - is the name of the first deployed application, when it is the second (bar) that I am trying to access.

      12:01:35,450 ERROR [STDERR] Caught a NamingException
      12:01:35,450 ERROR [STDERR] javax.naming.NameNotFoundException: foo not bound
      12:01:35,460 ERROR [STDERR] at org.jnp.server.NamingServer.getBinding(NamingServer.java:495)
      12:01:35,460 ERROR [STDERR] at org.jnp.server.NamingServer.getBinding(NamingServer.java:503)
      12:01:35,460 ERROR [STDERR] at org.jnp.server.NamingServer.getObject(NamingServer.java:509)
      12:01:35,460 ERROR [STDERR] at org.jnp.server.NamingServer.lookup(NamingServer.java:253)
      12:01:35,460 ERROR [STDERR] at org.jnp.server.NamingServer.lookup(NamingServer.java:256)
      12:01:35,460 ERROR [STDERR] at org.jnp.server.NamingServer.lookup(NamingServer.java:256)
      12:01:35,460 ERROR [STDERR] at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:492)
      12:01:35,460 ERROR [STDERR] at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:606)
      12:01:35,460 ERROR [STDERR] at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:471)
      12:01:35,460 ERROR [STDERR] at javax.naming.InitialContext.lookup(InitialContext.java:347)
      12:01:35,460 ERROR [STDERR] at com.macamat.photoserver.ejb.AdminFacadeBean.getPersistentPropertyHome(AdminFacadeBean.java:589)
      12:01:35,460 ERROR [STDERR] at com.macamat.photoserver.ejb.AdminFacadeBean.incrementNumericProperty(AdminFacadeBean.java:269)
      12:01:35,460 ERROR [STDERR] at com.macamat.photoserver.ejb.AdminFacadeBean.incrementHomepageRequests(AdminFacadeBean.java:216)
      12:01:35,460 ERROR [STDERR] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      ...etc...

      I have tested and shown that this exception occurs in the Session Bean in the FOO deployment, so the communication between the struts and EJB modules is definitely getting its wires crossed.

      Below are two copies of my web.xml file, one for foo and one for bar - as far as I can tell there should be no misunderstanding. I didn't think that I would need to use jboss-web.xml - anyone have any ideas?

      Thanks in advance,

      - Mat

      ps. Tried to attach these files but the attach+post button wasn't working...

      --------------------
      web.xml for FOO
      --------------------

      <?xml version="1.0" encoding="ISO-8859-1"?>

      <!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>
      <display-name>foo application</display-name>

      <!-- Action Servlet Configuration -->

      <servlet-name>action</servlet-name>
      <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
      <init-param>
      <param-name>config</param-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
      </init-param>
      <load-on-startup>1</load-on-startup>



      <!-- Action Servlet Mapping -->
      <servlet-mapping>
      <servlet-name>action</servlet-name>
      <url-pattern>*.do</url-pattern>
      </servlet-mapping>


      <!-- The Welcome File List -->
      <welcome-file-list>
      <welcome-file>home.jsp</welcome-file>
      </welcome-file-list>

      <!-- Struts Tag Library Descriptors -->

      <taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
      <taglib-location>/WEB-INF/struts-bean.tld</taglib-location>



      <taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
      <taglib-location>/WEB-INF/struts-html.tld</taglib-location>



      <taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
      <taglib-location>/WEB-INF/struts-logic.tld</taglib-location>


      <ejb-local-ref>
      <ejb-ref-name>ejb/foo/DirectoryFacade</ejb-ref-name>
      <ejb-ref-type>Session</ejb-ref-type>
      <local-home>com.macamat.photoserver.ejb.DirectoryFacadeHome</local-home>
      com.macamat.photoserver.ejb.DirectoryFacade
      <ejb-link>foo.jar#DirectoryFacade</ejb-link>
      </ejb-local-ref>

      <ejb-local-ref>
      <ejb-ref-name>ejb/foo/AdminFacade</ejb-ref-name>
      <ejb-ref-type>Session</ejb-ref-type>
      <local-home>com.macamat.photoserver.ejb.AdminFacadeHome</local-home>
      com.macamat.photoserver.ejb.AdminFacade
      <ejb-link>foo.jar#AdminFacade</ejb-link>
      </ejb-local-ref>

      <ejb-local-ref>
      <ejb-ref-name>ejb/foo/ImageFacade</ejb-ref-name>
      <ejb-ref-type>Session</ejb-ref-type>
      <local-home>com.macamat.photoserver.ejb.ImageFacadeHome</local-home>
      com.macamat.photoserver.ejb.ImageFacade
      <ejb-link>foo.jar#ImageFacade</ejb-link>
      </ejb-local-ref>

      <ejb-local-ref>
      <ejb-ref-name>ejb/foo/JournalFacade</ejb-ref-name>
      <ejb-ref-type>Session</ejb-ref-type>
      <local-home>com.macamat.photoserver.ejb.JournalFacadeHome</local-home>
      com.macamat.photoserver.ejb.JournalFacade
      <ejb-link>foo.jar#JournalFacade</ejb-link>
      </ejb-local-ref>

      <ejb-local-ref>
      <ejb-ref-name>ejb/foo/GuestbookFacade</ejb-ref-name>
      <ejb-ref-type>Session</ejb-ref-type>
      <local-home>com.macamat.photoserver.ejb.GuestbookFacadeHome</local-home>
      com.macamat.photoserver.ejb.GuestbookFacade
      <ejb-link>foo.jar#GuestbookFacade</ejb-link>
      </ejb-local-ref>

      </web-app>


      --------------------
      web.xml for BAR
      --------------------

      <?xml version="1.0" encoding="ISO-8859-1"?>

      <!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>
      <display-name>bar application</display-name>

      <!-- Action Servlet Configuration -->

      <servlet-name>action</servlet-name>
      <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
      <init-param>
      <param-name>config</param-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
      </init-param>
      <load-on-startup>1</load-on-startup>



      <!-- Action Servlet Mapping -->
      <servlet-mapping>
      <servlet-name>action</servlet-name>
      <url-pattern>*.do</url-pattern>
      </servlet-mapping>


      <!-- The Welcome File List -->
      <welcome-file-list>
      <welcome-file>home.jsp</welcome-file>
      </welcome-file-list>

      <!-- Struts Tag Library Descriptors -->

      <taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
      <taglib-location>/WEB-INF/struts-bean.tld</taglib-location>



      <taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
      <taglib-location>/WEB-INF/struts-html.tld</taglib-location>



      <taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
      <taglib-location>/WEB-INF/struts-logic.tld</taglib-location>


      <ejb-local-ref>
      <ejb-ref-name>ejb/bar/DirectoryFacade</ejb-ref-name>
      <ejb-ref-type>Session</ejb-ref-type>
      <local-home>com.macamat.photoserver.ejb.DirectoryFacadeHome</local-home>
      com.macamat.photoserver.ejb.DirectoryFacade
      <ejb-link>bar.jar#DirectoryFacade</ejb-link>
      </ejb-local-ref>

      <ejb-local-ref>
      <ejb-ref-name>ejb/bar/AdminFacade</ejb-ref-name>
      <ejb-ref-type>Session</ejb-ref-type>
      <local-home>com.macamat.photoserver.ejb.AdminFacadeHome</local-home>
      com.macamat.photoserver.ejb.AdminFacade
      <ejb-link>bar.jar#AdminFacade</ejb-link>
      </ejb-local-ref>

      <ejb-local-ref>
      <ejb-ref-name>ejb/bar/ImageFacade</ejb-ref-name>
      <ejb-ref-type>Session</ejb-ref-type>
      <local-home>com.macamat.photoserver.ejb.ImageFacadeHome</local-home>
      com.macamat.photoserver.ejb.ImageFacade
      <ejb-link>bar.jar#ImageFacade</ejb-link>
      </ejb-local-ref>

      <ejb-local-ref>
      <ejb-ref-name>ejb/bar/JournalFacade</ejb-ref-name>
      <ejb-ref-type>Session</ejb-ref-type>
      <local-home>com.macamat.photoserver.ejb.JournalFacadeHome</local-home>
      com.macamat.photoserver.ejb.JournalFacade
      <ejb-link>bar.jar#JournalFacade</ejb-link>
      </ejb-local-ref>

      <ejb-local-ref>
      <ejb-ref-name>ejb/bar/GuestbookFacade</ejb-ref-name>
      <ejb-ref-type>Session</ejb-ref-type>
      <local-home>com.macamat.photoserver.ejb.GuestbookFacadeHome</local-home>
      com.macamat.photoserver.ejb.GuestbookFacade
      <ejb-link>bar.jar#GuestbookFacade</ejb-link>
      </ejb-local-ref>

      </web-app>

        • 1. Re: <ejb-link> problem - multiple deployments getting wires

          how do you do your EJB lookup from within struts?

          -- Juha

          • 2. Re: <ejb-link> problem - multiple deployments getting wires
            macamat

            Hi,

            The code itself is below - this method is within a struts Action class. As I understand it the local reference
            "java:comp/env/ejb/foo/AdminFacade"
            needs to match the
            <ejb-ref-name> element in the equivalent reference in the web.xml file (also below) - and that it will then use the bean from the jar file given in <ejb-link>, but it is not.

            I am still having trouble with this - is it perhaps possible to isolate these two deployments somehow, so that one is not able to see and reference the classes for another? If possible that should work because either deployment works fine by itself.

            - Mat

            <ejb-local-ref>
            <ejb-ref-name>ejb/foo/AdminFacade</ejb-ref-name>
            <ejb-ref-type>Session</ejb-ref-type>
            <local-home>com.macamat.photoserver.ejb.AdminFacadeHome</local-home>
            com.macamat.photoserver.ejb.AdminFacade
            <ejb-link>foo.jar#AdminFacade</ejb-link>
            </ejb-local-ref>

            protected AdminFacade getAdminFacade()
            {
            if (adminFacade != null)
            return adminFacade;
            try {
            // use jndi.properties in same directory of source
            InitialContext context = new InitialContext();
            AdminFacadeHome adminHome = (AdminFacadeHome) context.lookup( "java:comp/env/ejb/foo/AdminFacade" );
            return adminHome.create();
            }
            catch(Exception e)
            {
            e.printStackTrace();
            return null;
            }
            }

            • 3. Re: <ejb-link> problem - multiple deployments getting wires
              jamesstrachan

              Mat,

              The code example isn't quite complete, but it looks as though you are trying to implement a singleton pattern - declaring adminFacade as a static variable.

              There is only one instance of a static variable within a JVM, so the two deployments within the same JVM will both try to access the same instance. That will cause the confusion between deployments that you describe when the JNDI names accessed from the context don't match the hard coded JNDI name.

              So break the singleton pattern by making adminFacade local and doing a fresh lookup each time.

              Another possible error in this context is any attributes declared directly or through Struts as having application scope. They would also be vulnerable to similar errors.

              James

              • 4. Re: <ejb-link> problem - multiple deployments getting wires
                macamat

                Yeah I saw that boneheaded mistake in my code just after I posted it... unfortunately adminFacade is already not static, it is an instance variable so the problem is not there.

                I have done some further investigation and have proven that the local home interface retrieved is the correct one - its COMP_NAME and JNDI_NAME properties are as I am expecting. However, when I call create to get a local interface, it creates an instance from the incorrect deployment.

                I thought I had the answer when I looked at the generated local home interface - as you can see the COMP_NAME does not contain the name of the application in its path, but the JNDI_NAME does:

                /*
                * Generated by XDoclet - Do not edit!
                */
                package com.macamat.photoserver.ejb;

                /**
                * Local home interface for AdminFacade.
                */
                public interface AdminFacadeHome
                extends com.macamat.photoserver.ejb.PhotoserverFacadeLocalHome
                {
                public static final String COMP_NAME="java:comp/env/ejb/AdminFacadeLocal";
                public static final String JNDI_NAME="ejb/foo/AdminFacade";

                public com.macamat.photoserver.ejb.AdminFacade create()
                throws javax.ejb.CreateException;

                }

                So in my Ant build, directly after generating these classes in the task, I did a across these generated classes and changed them so that the COMP_NAME contained the name of the application in the path. I have verified that that version of the file was compiled and deployed, but it did not solve the problem.

                My logs in JBoss are quite bizarre:

                INFO [STDOUT] (PhotoserverAction) Attempting to retrieve local home at java:comp/env/ejb/foo/AdminFacade
                INFO [STDOUT] (PhotoserverAction) local home COMP_NAME: java:comp/env/ejb/foo/AdminFacadeLocal
                INFO [STDOUT] (PhotoserverAction) local home JNDI_NAME: ejb/foo/AdminFacade
                INFO [STDOUT] (AdminFacadeBean.create()) Creating admin facade session bean at java:comp/env/ejb/bar/AdminFacade
                INFO [STDOUT] (AdminFacadeBean.getPersistentProperty()) Attempting to lookup java:comp/env/ejb/bar/PersistentProperty
                ERROR [STDERR] Caught a NamingException
                ERROR [STDERR] javax.naming.NameNotFoundException: bar not bound

                So the local home interface is retrieved correctly, but then it creates the incorrect local interface. The JNDI_NAME and COMP_NAME properties are static - could that be causing the problem? I could try modifying the generated classes to set those properties as no longer static, but it's a bit of a stretch - does anyone have any better ideas?

                Thanks for the attention so far,

                - Mat

                • 5. Re: <ejb-link> problem - multiple deployments getting wires
                  jamesstrachan

                  Mat,

                  You could well be right about COMP_NAME and JNDI_NAME, as any static variable only appears once within the JVM.

                  There is also a potential problem as your foo and bar deployments have the same class names with different source code. I don't know how the class loader will treat this, but it is a problem that I would rather avoid.

                  This has been going on for some time, so it's worth refactoring to get a different design solution that works.

                  I would suggest that you refactor to use a name factory class that creates a JNDI name from a logical name - I attach an example that does this, and a class that calls the name factory in method getRemote().

                  The name factory can then read a properties file that contains a line :-

                  jndi.deployment.branch=foo

                  or

                  jndi.deployment.branch=bar

                  so as to build the required full jndi name.

                  By this means, you can get a single set of compiled classes for all deployments - although you still need to tweak the deployment descriptor and the properties file for each deployment.

                  This may seem like a lot of work, but nothing is more work than staring at the screen when you can't see your way into the problem.

                  Hope this helps.

                  James

                  • 6. Re: <ejb-link> problem - multiple deployments getting wires
                    jamesstrachan

                    Mat,

                    Further to my last reply, using a properties file may have the same ambiguity that the class loader may pick a properties file from any of the deployed jar files.

                    You could add an env-entry element to the web deployment descriptor that has a value set to foo or bar, etc. That would be unambiguous.

                    At all events, you can only make progress by removing all static variables, having a single set of source code and compiled classes, and then building jndi names dynamically using a resource unique to the particular deployment.

                    James

                    • 7. Re: <ejb-link> problem - multiple deployments getting wires
                      macamat

                      Some closure...

                      I really didn't trust that JNDI would be able to distinguish two classes from the same package (but in different jar files) even with dynamically built JNDI names - the testing I have done showed that despite the JNDI names I was using not being dynamically generated, they were certainly different in different deployments and the problem persisted.

                      I figured that progress could be made by removing the problem altogether somehow. Two solutions presented themselves:

                      1. Have only one deployed application that can maquerade as several. An extra level of cardinality so to speak, there would be some central entity bean called 'application' or something and all of the properties of my application would belong to an instance of that bean. I didn't feel like working out how to get multiple context names pointed at the same deployment so I didn't go for this option.

                      2. Change the class names for each deployment. I went for this option. To do this my build script copies all source files to a temporary directory (build/src), renames the directory com/macamat/photoserver to com/macamat/$(application.name} and then does a replace within the source files to change all of the package names. This solution also required some dynamic modification to web.xml, struts-config.xml and the JSPs.

                      However, when you deploy three applications which are identical (except for their class names, jndi names and contexts)... it works! ;)

                      Thanks all and good night