Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    DataSourceManager fails to find entity manager in cdi bean

    I have a CDI bean that is being invoked by an event chain outside of the web container. It is responding to entity changes performed server side, and trying to parse them and update clients. I originally used DataTools.getProperties to get fields from the entity into a real time message to the client. However, for entities that have bi directional relationships, that particular tool does not break the recursion caused by the relationship, which ends up causing problems on the client. While investigating I found that when doing a fetch against these entities, these recursive relationships are broken as expected, and so I decided to use DataSource.getProperties in an attempt to resolve this issue. However, I end up with the following exception because emf provider is not being invoked from the context it expects when making the jndi lookup. Is there some other way that I am missing to retrieve a DataSource instance from outside of the web context?
    Last edited by jpappalardo; 24 Sep 2014, 10:56.

    #2
    Just out of curiosity, since I am in the CDI context it would be very easy for me to inject an EMF or EM to pass into the DataSourceManager if you exposed such an api method? Would that be simpler to resolve than fixing the jndi being called to an improper context?

    Comment


      #3
      I was able to confirm that the issue is performing the jndi lookup from the cdi context rather than web application context. I have a servlet that on init actually sends an event that the cdi bean listens to for creation (it is application scoped), thus ensuring that the servlet container has started, and your init and pre cache servlets have executed prior to trying to send updates to the client, as entity changes are made during the startup process. So I implemented a hack in the servlets init function to create a map of DataSource objects, that the cdi bean could then use directly. Unfortunately, when the datasource runs into a property with a related datasource, I get the same error as it attempts to create the related ds.

      Comment


        #4
        Am I using the wrong emf provider? I have tried using a few other ones and none of the other ones seem to work correctly for even simple fetches, though I may be configuring them incorrectly. The documentation is a bit confusing.

        Comment


          #5
          Having your own EMFProvider seems to be the best choice for your setup.
          You can implement com.isomorphic.jpa.EMFProviderInterface or extend one of the existing EMFProvider implementations and change its functionality. That way you will gain control over creation/lookup of EntityManager.

          You will need to use jpa.emfProvider setting in server.properties to make Smartclient use your provider implementation.

          Comment


            #6
            I have an implementation that appears to be working, but for the sake of ensuring that I am not causing an unforeseen issue can you review this code?

            EMFProviderBean:


            EMFProviderImpl:


            jpa.emfProvider: com.sncorp.gs2.gsui.server.datasource.EMFProviderImpl
            Last edited by jpappalardo; 24 Sep 2014, 09:03.

            Comment


              #7
              Your implementation misses code for get/commit/rollback transaction methods. You could either implement those yourself accordingly to EMFProviderInterface javadoc requirements for those methods or partly use one of existing implementations.

              I'd suggest extending EMFProviderLMT - EMF provider for Locally Managed Transactions. Just as you did originally in your implementation override get and getEntityManager methods, and leave the rest for super class.
              Code:
              public class EMFProviderImpl extends EMFProviderLMT {
              	
                  private final EMFProviderBean emfProviderBean;
              
                  public EMFProviderImpl() {
                      emfProviderBean = CDI.current().select(EMFProviderBean.class).get();
                  }
              
                  @Override
                  public EntityManagerFactory get() {
                      return emfProviderBean.getEmf();
                  }
              
                  @Override
                  public EntityManager getEntityManager() throws PersistenceException {
                      return emfProviderBean.getEm();
                  }
              
              }

              Comment


                #8
                That is not a logical solution because I am injecting the EMF and EM, so they are container managed, so perhaps I should be extending CMT? I tried doing as you requested and it blows because a JTA EntityManager cannot use getTransaction. The injected em, or an em created via the injected emf returns an EntityTransaction rather than a UserTransaction.

                Comment


                  #9
                  This on the other hand does appear to work succesfully, though am I supposed to do something in getTransaction if em is not null?


                  ***EDIT***

                  To answer my own question I believe this is appropriate



                  *** EDIT 2 ***

                  The above does not work because there is no transaction to join, the transaction only exists within the boundaries of the EMFProviderBean methods, so exceptions are thrown by em.joinTransaction. I am having a really hard time making sense of how I can provide for container managed transactions and access to the emf/em regardless of the context I am in.
                  Last edited by jpappalardo; 24 Sep 2014, 09:03.

                  Comment


                    #10
                    Can you please provide some explanation on how exactly one is supposed to use EMFProviderCMT?

                    I cannot seem to resolve the appropriate way to do so.

                    server.properties
                    Code:
                    jpa.emfProvider: com.isomorphic.jpa.EMFProviderCMT
                    jpa.entityManager: persistence/em
                    jpa.entityManagerFactory: persistence/emf
                    ejb-jar.xml


                    GSUIEntityEventObserver.java


                    I presume from your documentation this should make it such that I can succesfully use DataSource.getProperties via an EJBContext however I now get the following errors when trying to do a simple fetch


                    FetchOnlyJPA2DataSource


                    From what I am observing it would seem that my custom datasource needs to be @Stateless, however I cannot find any property I can set on the ds.xml to provide a factory so that the custom ds is actually container managed. What am I missing here?
                    Last edited by jpappalardo; 24 Sep 2014, 09:04.

                    Comment


                      #11
                      The stack trace you've shown contains all the answers. It says: "No EjbContext available as no EJB invocation is active"

                      I understand that you do not have an EJB context, i.e. at the moment of execution EJB context cannot be get via lookup. You may check this yourself by trying to execute code shown below for example via DMI or similar.
                      Code:
                      EJBContext ctx = (EJBContext) new InitialContext().lookup("java:comp/EJBContext");
                      Most likely you will see same exception that caused exception during EMFProviderCMT.getTransaction execution. In fact EMFProviderCMT does the same thing - tries to lookup java:comp/EJBContext, and if container does not provide it, then it is not EJB managed application or is not managed properly.

                      Another notice about the stacktrace - EJB is not there, if you were working in EJB environment I expect to see its classes in stacktrace which is not the case. This allows to think that may be you did not configure your EJB application correctly and your application is running on EJB server, but just as simple Web application.

                      Comment


                        #12
                        This implementation seems to work well:

                        EMFProviderBean


                        EMFProviderImpl
                        Last edited by jpappalardo; 24 Sep 2014, 09:05.

                        Comment


                          #13
                          Originally posted by jpappalardo View Post
                          This implementation seems to work well:

                          EMFProviderBean


                          EMFProviderImpl
                          Hi jpappalardo!

                          I have the same issue now, can you provide the implementation which worked for you finally?

                          Thanks!

                          Comment

                          Working...
                          X