1 2 Previous Next 28 Replies Latest reply on Dec 3, 2008 10:07 AM by dilipramji Go to original post
      • 15. Re: share an EJB3 layer between two ears?
        rhills

        Hi Guillaume,


        the shared-ejb.jar is in server/default, not server/default/lib.  We only moved jboss-seam.jar to server/default/lib.

        • 16. Re: share an EJB3 layer between two ears?
          rhills

          Hi Dean,


          OK, I'll do that as soon as I can.  I'm in the middle of reworking our build.xml file to create the new structure.  As soon as I've done that, I'll run your tests and let you know.  Also bear in mind the time differences, we're on GMT +9 here (recently started daylight saving time).



          • 17. Re: share an EJB3 layer between two ears?
            rhills

            Hi Guillaume,


            Regarding the dependencies between the .EARs, we're well aware of those but it works quite well for the design of our app.  As you suggested, it enables us to functionally isolate different components of our application.

            • 18. Re: share an EJB3 layer between two ears?
              rhills

              While refining this structure and reworking the build script, I've run into a couple of issues that I believe may be bugs (probably in JBoss, but bear with me please).


              I mentioned previously that we have a shared-ejb.jar for our persistence units, which lives in the server/default directory.  Internally, this jar was structured like this:


                /au/../.../entity/{various entity .class files here}
                /META-INF/persistence.xml (defining 3 persistence units).


              For various reasons that I won't bore you with, we wanted to separate our Oracle and SQL Server entity classes (we have legacy tables with the same names but different structures in the two dbs, hence the separate entities).  So, I thought I'd do that with this structure in my shared-ejb.jar:


                /lib/sqlserver-entities.jar
                /lib/oracle-entities.jar
                /lib/shared-usertypes.jar
                /META-INF/persistence.xml (still defining 3 persistence units, 1x sqlserver, 2x oracle).


              However, with this structure, when I start up the server, it fails to start my application, with exceptions like the following:


              javax.management.InstanceAlreadyExistsException: persistence.units:unitName=sqlserver already registered.
              javax.management.InstanceAlreadyExistsException: persistence.units:unitName=oracle1 already registered.


              Working through the log file, it appears that the server is processing the persistence.xml file once for each jar in the lib, including the shared-usertypes.jar that actually has no entity classes in it.  Is that expected behaviour?


              OK, so I thought I'd remove my persistence.xml file from my shared-ejb.jar/META-INF and split it into two, putting the sqlserver persistence unit definition into the relevant jars so my shared-ejb.jar structure ends up looking like this:


                /lib/sqlserver-entities.jar/META-INF/persistence.xml (defines SQLServer persistence unit only
                /lib/oracle-entities.jar/META-INF/persistence.xml (defines Oracle persistence units only
                /lib/shared-usertypes.jar
                /META-INF (now empty)


              Starting the server up with this structure, I now get my persistence units created as expected, without duplication, but my EntityManagerFactories (defined in the persistence.xml) are no longer registered for some reason and my app fails to start with the underlying error:


              Caused by: java.lang.IllegalArgumentException: EntityManagerFactory not found in JNDI : java:/sqlserverEntityManagerFactory


              Again, is this expected behaviour?


              Note, with my original structure, the EntityManagerFactories were all registered and working correctly and I've not changed the part of the persistenceUnit definitions that register those, so I don't understand what's going on here.


              Does anyone know if this kind of structure is possible?  If so, how does one define the persistence units and entity managers properly?

              • 19. Re: share an EJB3 layer between two ears?
                rhills

                Sorry, forgot to format the last post properly...


                While refining this structure and reworking the build script, I've run into a couple of issues that I believe may be bugs (probably in JBoss, but bear with me please). I mentioned previously that we have a shared-ejb.jar for our persistence units, which lives in the server/default directory. Internally, this jar was structured like this:



                /au/../.../entity/{various entity .class files here}
                /META-INF/persistence.xml (defining 3 persistence units).
                



                For various reasons that I won't bore you with, we wanted to separate our Oracle and SQL Server entity classes (we have legacy tables with the same names but different structures in the two dbs, hence the separate entities). So, I thought I'd do that with this structure in my shared-ejb.jar:


                /lib/sqlserver-entities.jar
                /lib/oracle-entities.jar
                /lib/shared-usertypes.jar
                /META-INF/persistence.xml (still defining 3 persistence units, 1x sqlserver, 2x oracle).
                



                However, with this structure, when I start up the server, it fails to start my application, with exceptions like the following:


                javax.management.InstanceAlreadyExistsException: persistence.units:unitName\=sqlserver already registered.
                javax.management.InstanceAlreadyExistsException: persistence.units:unitName\=oracle1 already registered.



                Working through the log file, it appears that the server is processing the persistence.xml file once for each jar in the lib, including the shared-usertypes.jar that actually has no entity classes in it. Is that expected behaviour?


                OK, so I thought I'd remove the persistence.xml file from my shared-ejb.jar/META-INF and split it into two, putting the sqlserver persistence unit definition into the relevant jars so my shared-ejb.jar structure ends up looking like this:



                /lib/sqlserver-entities.jar/META-INF/persistence.xml (defines SQLServer persistence unit only) 
                /lib/oracle-entities.jar/META-INF/persistence.xml (defines Oracle persistence units only) 
                /lib/shared-usertypes.jar 
                /META-INF (now empty)
                



                Starting the server up with this structure, I now get my persistence units created as expected, without duplication, but my EntityManagerFactories (defined in the persistence.xml) are no longer registered for some reason and my app fails to start with the underlying error:


                Caused by: java.lang.IllegalArgumentException: EntityManagerFactory not found in JNDI : java:/sqlserverEntityManagerFactory
                



                Again, is this expected behaviour?


                Note, with my original structure, the EntityManagerFactories were all registered and working correctly and I've not changed the part of the persistenceUnit definitions that register those, so I don't understand what's going on here. Does anyone know if this kind of structure is possible? If so, how does one define the persistence units and entity managers properly?

                • 20. Re: share an EJB3 layer between two ears?
                  rhills

                  Hi Dean,


                  I believe I have good news for you.


                  Our apps use an ApplicationService class, that's the same class for each app, but does a number of application-specific tasks, including loading an app-specific configuration file.


                  I put the following code into the ApplicationService class, in its init() method, after it had completed all initialisations:


                  log.info("**** ApplicationService Classloader in app: #0 is: #1 *******", getConfiguration().getSubSystem().getCode(), this.getClass().getClassLoader());
                  log.info("**** Address entity Classloader in app: #0 is: #1 *******", getConfiguration().getSubSystem().getCode(), au.edu.tisc.entity.Address.class.getClassLoader());
                  



                  Although this is a bit different from the code you suggested, I believe it achieves the same purpose.  Note, the getConfiguration().getSubsystem().getCode() bit returns a string that is unique for each application.


                  After starting up the server with our 3 applications, I found the following in the log:



                  2008-11-07 11:11:16,187 INFO  [ApplicationService] **** ApplicationService Classloader in app: replication is: org.jboss.mx.loading.UnifiedClassLoader3@10fa4b8{ url=file:/C:/app/jboss-eap/jboss-as/server/default/deploy/shared-ejb.jar/ ,addedOrder=37} *******
                  2008-11-07 11:11:16,187 INFO  [ApplicationService] **** Address entity Classloader in app: replication is: org.jboss.mx.loading.UnifiedClassLoader3@10fa4b8{ url=file:/C:/app/jboss-eap/jboss-as/server/default/deploy/shared-ejb.jar/ ,addedOrder=37} *******
                  
                  2008-11-07 11:11:42,468 INFO  [ApplicationService] **** ApplicationService Classloader in app: admin is: org.jboss.mx.loading.UnifiedClassLoader3@10fa4b8{ url=file:/C:/app/jboss-eap/jboss-as/server/default/deploy/shared-ejb.jar/ ,addedOrder=37} *******
                  2008-11-07 11:11:42,468 INFO  [ApplicationService] **** Address entity Classloader in app: admin is: org.jboss.mx.loading.UnifiedClassLoader3@10fa4b8{ url=file:/C:/app/jboss-eap/jboss-as/server/default/deploy/shared-ejb.jar/ ,addedOrder=37} *******
                  
                  2008-11-07 11:12:05,249 INFO  [ApplicationService] **** ApplicationService Classloader in app: course is: org.jboss.mx.loading.UnifiedClassLoader3@10fa4b8{ url=file:/C:/app/jboss-eap/jboss-as/server/default/deploy/shared-ejb.jar/ ,addedOrder=37} *******
                  2008-11-07 11:12:05,249 INFO  [ApplicationService] **** Address entity Classloader in app: course is: org.jboss.mx.loading.UnifiedClassLoader3@10fa4b8{ url=file:/C:/app/jboss-eap/jboss-as/server/default/deploy/shared-ejb.jar/ ,addedOrder=37} *******
                  



                  Note the different app codes in the log lines. To me that looks suspiciously like we're loading everything with the same classloader, which is what we wanted to achieve.

                  • 21. Re: share an EJB3 layer between two ears?
                    deanhiller2000

                    interesting....not what I expected.  I was more expecting the two ears to use their own classloader and those two classloaders to be children of the ejb(so they can use the ejb).  In fact, if I want to redeploy my app, I think that causes problems as the classes are already loaded(both apps too)....so I think they are not yet to the power of OSGi where I can redeploy the same app without having to redeploy the EJB layer(which I can do in OSGi without redeploying my other apps using that EJB layer).


                    I guess it is better than anything I have had up to this point though.  Man, I need to go write my own app server based on OSGi. 


                    Out of curiosity, have you redeployed one of the two apps.  My guess is that it reloads all apps at that point since it would need to destroy the classloader???

                    • 22. Re: share an EJB3 layer between two ears?
                      rhills

                      Hi Dean,


                      I'm not at the point yet where I can tell you anything about that.  I've finished refining the build process for now and am about to replicate it to the other apps.  Once that's done, I can start looking at what happens when we redeploy individual apps.


                      Re the single classloader, if I understand things correctly, we've forced a single classloader for all apps by specifying the same loader name in the xxx.ear/META-INF/jboss-app.xml like this:


                      <jboss-app>
                         <loader-repository>
                            seam.jboss.org:loader=ourApp
                         </loader-repository>
                      </jboss-app>
                      



                      Not sure what would happen if we change that.  I may experiment with this later on.

                      • 23. Re: share an EJB3 layer between two ears?
                        gjeudy

                        Hey Rob,


                        I don't know you may be hitting a jboss bug. I didn't know you could deploy an ejb jar stand-alone straight into server/default/deploy dir which is I assume what you do ?


                        I thought that the only way to deploy an ejb-module is to package it inside an EAR and declare it in the application.xml. I use this approach and i'm able to deploy several persistence units in different persistence.xml packaged in separate jars and have the EntityManagerFactories published to JNDI properly.


                        Since your approach is to deploy the ejb-jar directly in the deploy dir maybe you could try splitting into separate ejb-jars per persistence unit and see how it behaves ?


                        Let me know how it goes.

                        • 24. Re: share an EJB3 layer between two ears?
                          rhills

                          Hi Guillaume,



                          Guillaume Jeudy wrote on Nov 07, 2008 15:14:


                          I don't know you may be hitting a jboss bug. I didn't know you could deploy an ejb jar stand-alone straight into server/default/deploy dir which is I assume what you do ?


                          I just happened to notice there were a number of other jars in the server/default directory (that come with JBoss-AS by default) so I experimented and found something that seems to work (mostly).



                          I thought that the only way to deploy an ejb-module is to package it inside an EAR and declare it in the application.xml. I use this approach and i'm able to deploy several persistence units in different persistence.xml packaged in separate jars and have the EntityManagerFactories published to JNDI properly.


                          We have a couple of EARs as well, both use the same EJBs so this works out to be a good way to share them.



                          Since your approach is to deploy the ejb-jar directly in the deploy dir maybe you could try splitting into separate ejb-jars per persistence unit and see how it behaves ?


                          The problem with that approach is there is a class that's currently shared between the two persistence units - it's a UserType class so it really belongs with the entities.  If we split the persistence units into two jars, I have to work out what to do with our usertype class.  ATM we have our two EARs and our EJBs all loaded into a single shared classloader space which works well for our requirements.  However, if I put a copy of the usertype into two separate EJB jars, I anticipate I will probably get ClassCastExceptions (which I was getting last week when I was working this all out and I had two copies of the EJB classes).  The only solution I can think of would be to put our Usertype class into its own jar and record the dependency in the Manifest of each of the EJB jars.  However, that approach seems to be a bit untidy to me, though I can try it if it helps people to work out if this is a JBOSS bug or not.


                          I'd much prefer to be able to package my EJB classes together WRT the server/default/deploy directory and then split them up inside the jar.  As the Java Jar spec seems to support this, I'd like to think that JBOSS would be able to work with it.

                          • 25. Re: share an EJB3 layer between two ears?
                            rhills

                            Thought I'd update this thread with our current position...



                            Rob Hills wrote on Nov 10, 2008 01:53:



                            Since your approach is to deploy the ejb-jar directly in the deploy dir maybe you could try splitting into separate ejb-jars per persistence unit and see how it behaves ?


                            The problem with that approach is there is a class that's currently shared between the two persistence units - it's a UserType class so it really belongs with the entities.  If we split the persistence units into two jars, I have to work out what to do with our usertype class.  ATM we have our two EARs and our EJBs all loaded into a single shared classloader space which works well for our requirements.  However, if I put a copy of the usertype into two separate EJB jars, I anticipate I will probably get ClassCastExceptions (which I was getting last week when I was working this all out and I had two copies of the EJB classes).


                            We now have 3 EARs sharing 2 EJB jars.  I ended up splitting the two sets of EJBs into 2 jars, one for the SQLServer persistence unit and another containing 2 Oracle persistence units.  I tried putting a copy of the common UserType class in each jar and we haven't seen any classloader issues with that setup.


                            To summarise the setup, we have in our server/default/deploy/ directory:




                            • app1.ear

                            • app2.ear

                            • app3.ear

                            • common-classes.jar (META-INF/MANIFEST.MF contains

                            • sqlserver-ejb.jar (contains a META-INF/persistence.xml + ejb classes + UserType classes + META-INF/MANIFEST.MF with Class-Path: attribute referring to common-classes.jar)




                            • oracle-ejb.jar (contains a META-INF/persistence.xml + ejb classes + UserType classes + META-INF/MANIFEST.MF with Class-Path: attribute referring to common-classes.jar)



                            Hope this helps someone else trying to share EJBs.

                            • 26. Re: share an EJB3 layer between two ears?
                              dilipramji
                              • 27. Re: share an EJB3 layer between two ears?
                                deanhiller2000

                                My app1.ear cannot find the persistence unit


                                17:15:00,671 ERROR [AbstractKernelController] Error installing to Real: name=vfs
                                zip:/D:/BBROOT/jboss-5.0.0.CR2/server/default/deploy/voicelog_web_rates.ear stat
                                e=PreReal mode=Manual requiredState=Real
                                org.jboss.deployers.spi.DeploymentException: Error deploying voicelog_web_rates.
                                jar: Can't find a persistence unit named 'voicelog_web_rates' in AbstractVFSDepl
                                oymentContext@31189248{vfszip:/D:/BBROOT/jboss-5.0.0.CR2/server/default/deploy/v
                                oicelog_web_rates.ear/voicelog_web_rates.jar}
                                



                                Do you have some kind of persistence.xml file in your app1.ear file?
                                thanks,
                                Dean

                                • 28. Re: share an EJB3 layer between two ears?
                                  dilipramji

                                  Never mind, I found a second copy of my shared jar in the lib folder, that's what was causing the problem. Interestingly, though, I found that I needed to place my jboss-seam.jar in the deploy directory and not the lib directory because otherwise I get a TimerServiceDispatcher not bound error. I'll check more into that.


                                  Right now things are working for me.


                                  Thanks,


                                  Dilip

                                  1 2 Previous Next