14 Replies Latest reply on Sep 30, 2009 3:53 AM by Jo L

    avoid lazy-initialization exceptions

    mazz Master

      We have a pretty big problem that I was hoping to get some feedback on how others solved the problem. Its a common-enough problem that I think others must have run into this.

      We've got some SLSBs that return EJB3/JPA entities. We've annotated our SLSBs with @WebService so we can have a python client call into the SLSB via the web service interface.

      Some of the SLSB methods return entities that are not fully, eagerly loaded. That is, for performance reasons, we don't need to eagerly load in all relationships before the SLSB returns the entity to the web service client.

      However, when the web service container attempts to encode the entity in its XML representation, it calls the getters for those relationships we haven't loaded. This, in turn, throws the infamous LazyInitializationException.

      Clearly, the answer is not to force web service apps to eagerly load all entity relationships. However, for maintenance purposes, we don't want to have to create another layer of data-transfer objects just to support the web service stuff (the SLSB API works fine from a Java UI client - the only problem occurs when needing web service access since this only happens when the web service container needs to encode the returned entity in XML).

      Is there some way (annotation? configuration?) that we can tell the web service container to null out or otherwise not call the getters that we know won't work? Can we get the web service container to catch these lazy-init exceptions and just null out the XML value when returning the entity to the client?

      How do others solve this problem?

        • 1. Re: avoid lazy-initialization exceptions
          Thomas Diesler Master

          Hi John,

          please show us your SLSB and one of your entities. The JAXB default is to bind all public members. Please have a look at the JAXB spec for finer control. e.g. @XMLAccessType

          • 2. Re: avoid lazy-initialization exceptions
            mazz Master

            The SLSB and entity is very simple. I've included the relevant SLSB interface definition and entity code below. The specific issue here is our login() method returns a Subject - and the Subject, although it has a Role relationship, we do not eagerly load it. The caller to login() never needs to know the role relationship data, and because of that, we increase performance by never asking the persistence layer to load that data before returning the Subject. While this is a very specific example, this design paradigm is all through our code. In fact, I have to believe this is a general problem everyone will have (since this is a big reason behind using LAZY vs. EAGER loading).

            Not sure how to annotate the entity, because would that mean we can either ALWAYS or NEVER the data? But we know will we need it sometimes, but not always (i.e. sometimes a caller will call a different method that returns a Subject and that Subject WILL have the role information loaded and we thus WILL want that data go through the JAXB engine and returned to the web client).

            I think we need some way to customize the way JAXB processes the returned entity or we need some annotation on a per-SLSB method. We are looking at @XMLJavaTypeAdapter as a possible way to do something, but we aren't sure yet until we try it out.

            @WebService
            @SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
            public interface SubjectManagerRemote {
             @WebMethod
             Subject login(String user, String password);


            @Entity
            @Table(name = "ON_SUBJECT")
            public class Subject implements Externalizable {
            ...
             @ManyToMany
             @JoinTable(name="ON_SUBJECT_ROLE_MAP",
             joinColumns={@JoinColumn(name="SUBJECT_ID")},
             inverseJoinColumns={@JoinColumn(name="ROLE_ID")})
             private java.util.Set<Role> roles;
            ...
             public java.util.Set<Role> getRoles() {
             return this.roles;
             }
            




            • 3. Re: avoid lazy-initialization exceptions
              Greg Hinkle Newbie

              So I went and tried a few different ways to solve this. I implemented a XmlAdapter thinking I could inject some code into the marshalling process to null out the relationships before jaxb tries to do its thing.

              @javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(org.jboss.on.domain.util.EJBXMLTypeAdaptor.class)
              


              Doing this on the entity class and having the adapter just returned the cleaned-up object results in:

              javax.xml.bind.JAXBException: org.jboss.on.domain.auth.Subject nor any of its super class is known to this context
              


              It doesn't look like many people understand XmlAdapters given what I've been able to find on Google. Any ideas how to just get access to the object, but still have JAXB do the marshalling?

              I also tried putting the adapter on the relationship collection field that is lazy (and set the accessor type to FIELD), but it never ran my adapter in that case. Putting the adapter on the accessors didn't seem to get picked up.




              • 4. Re: avoid lazy-initialization exceptions
                Greg Hinkle Newbie

                I've now introduced an extended ServiceEndpointInvoker for the ejb3 calls. It's not perfect, but it allows me to get rid of all the PersistentCollections before soap and jaxb get their hands on my result objects. I'd still be interested if there's a better way though.

                • 5. Re: avoid lazy-initialization exceptions
                  Florian Bauer Newbie

                  I have the same problems using JAX-WS and EJB3.
                  I am very interested in a solution for this problem.

                  ghinkle introduced an extended ServiceEndpointInvoker - is there any document how to do that or can you gif me a small intro please.

                  • 7. Re: avoid lazy-initialization exceptions
                    Chris Baxter Newbie

                    Has anyone come up with a better solution to this problem? Is there any way that in the XML marshaling code, the logic could check if the instance was a PersistentBag. If it was, and it had not yet been initialized, then serialize it as either null or an empty collection. Is there a way to make this happen?

                    • 8. Re: avoid lazy-initialization exceptions
                      Phillip Warner Newbie

                      Even if you create another layer of DTOs, you'll still have to handle this case. I actually created such a layer and transformers to convert the Entities to DTOs (and vice-versa). The code has a relatively ugly solution, but it works.

                      The solution was to create a handler that catches all RuntimeExceptions when the transformation code iterates through to the collections. The handler looks for LazyInitializationException; if found, it logs the exception (but does not re-throw), if not found, it rethrows the (non-LIE) RuntimeException.

                      Yeah, ugly, but it works. The layer essentially removes external dependence on Hibernate. I suppose an alternative is to submit a bug report (or feature request?) to Hibernate. Alas, my hack gives me a (false?) sense of security until a Hibernate change causes the hack to break! YMMV and all. :)

                      • 10. Re: avoid lazy-initialization exceptions
                        Florian Bauer Newbie

                        another "ugly" solution is the following:

                        instad of

                        @XmlIDREF
                        

                        use:
                        @XmlJavaTypeAdapter(Entity2StringXMLAdapter.class)
                        

                        and here is the implementation for the Adapter:
                        public class Entity2StringXMLAdapter extends XmlAdapter<String,BasicEntity>
                        {
                        
                         @Override
                         public String marshal(final BasicEntity entityGot) throws Exception
                         {
                        
                         String strValue = "";
                         try
                         {
                         strValue = entityGot.getName() + "(" + entityGot.getId() + ")";
                         }
                         catch (LazyInitializationException e)
                         {
                         Long id = this.getEntityID(entityGot);
                         if(id == null)
                         id = new Long(-1);
                         strValue = "lazy (" + id + ")";
                        
                         }
                         return strValue;
                         }
                        
                        @Override
                         public BasicEntity unmarshal(final String arg0) throws Exception
                         {
                         return null;
                         }
                        
                         private Long getEntityID(final BasicEntity entityGot)
                         {
                         //dirty, but it works:
                         //long duration = System.currentTimeMillis();
                         Long entityId = null;
                         Class clazz = entityGot.getClass();
                         for(Field fieldHandler : clazz.getDeclaredFields())
                         {
                         if(fieldHandler.getType().isAssignableFrom(javassist.util.proxy.MethodHandler.class) && fieldHandler.getName().equals("handler"))
                         {
                         try
                         {
                         fieldHandler.setAccessible(true);
                         javassist.util.proxy.MethodHandler methodHandler = (javassist.util.proxy.MethodHandler) fieldHandler.get(entityGot);
                        
                         Field fieldID = AbstractLazyInitializer.class.getDeclaredField("id");
                         fieldID.setAccessible(true);
                         entityId = (Long)fieldID.get(methodHandler);
                         }
                         catch (SecurityException e1)
                         {
                         e1.printStackTrace();
                         }
                         catch (IllegalArgumentException e1)
                         {
                         e1.printStackTrace();
                         }
                         catch (IllegalAccessException e1)
                         {
                         e1.printStackTrace();
                         }
                         catch (NoSuchFieldException e1)
                         {
                         System.out.println("======= THIS EXCEPTION DEPENDS ON A NEWER HIBERNATE VERSION!!!!!! ========");
                         e1.printStackTrace();
                         }
                         break;
                         }
                         }
                         return entityId;
                         }
                        }
                        


                        BasicEntity is just Interface that is implemented by all my entities with the following methods:

                        public Long getId();
                        public void setId(final Long idGot);
                        public String getName();
                        public void setName(final String strNameGot);
                        


                        • 11. Re: avoid lazy-initialization exceptions
                          Jo L Newbie

                          I think the solution is to use @XmlAccessorFactory on Entity Bean and override default accessor !
                          But by default, Jboss do not exploit this annotation when creating JAXBContext and i can't find how to enable this Binding customization.

                          If anyone have an idea, help would be greatly appreciated
                          Jo

                          • 12. Re: avoid lazy-initialization exceptions
                            Alessio Soldano Master

                            You can provide your own JAXBContextFactory, take a look at org.jboss.ws.core.jaxws.CustomizableJAXBContextFactory for instance. This way you can setup the property you need in the context at creation.

                            Then make sure your context factory is loaded by the JBossWS ServiceLoader (basically you might want to either set your factory through a System property or add a org.jboss.ws.core.jaxws.JAXBContextFactory file in META-INF/services somewhere in the classpath).

                            • 14. Re: avoid lazy-initialization exceptions
                              Jo L Newbie

                               

                              "alessio.soldano@jboss.com" wrote:
                              You can provide your own JAXBContextFactory, take a look at org.jboss.ws.core.jaxws.CustomizableJAXBContextFactory for instance. This way you can setup the property you need in the context at creation.

                              Then make sure your context factory is loaded by the JBossWS ServiceLoader (basically you might want to either set your factory through a System property or add a org.jboss.ws.core.jaxws.JAXBContextFactory file in META-INF/services somewhere in the classpath).


                              Is there any sample on how to do that ?
                              Thanks a lot
                              Jonathan