8 Replies Latest reply on Dec 6, 2009 9:47 PM by vaughnb.vaughn.butt.datacom.co.nz

    Best Process for calling another application from seam application

    valatharv
      What is the process of calling another java application from seam application.

      Example : Consider two applications
      a) seamApplication.ear
      b) externalApplication.jar

      c) We have externalApplication application packaged as a jar. This application uses hibernate to communicate with database and does some processing.

      d) On every create from seamApplication we need to execute a function in externalApplication passing the id from seam home component and externalApplication will do its processing.

      What is the best way to handle this, so that seam can continue without waiting for completion of externalApplication.

      Sample Home:
      @Name("experimentHome")
      public class experimentHome extends EntityHome<Experiment> {

           public String persist(){     

                return super.persist();

           }

      }
        • 1. Re: Best Process for calling another application from seam application
          stefanotravelli

          In a similar scenario I found asynchronous events the perfect solution.


          Call your external application in a Stateless Seam component with an @Observer method and raise an asynchronous event in the seam application.



          @Name("externalApplicationInvoker")
          @Scope(ScopeType.STATELSS)
          public class ExternalApplicationInvoker
          {
             
             @Observer("somethingOccurred")
             @Transactional
             public void doStuff(Long param)
             {
                // do whatever
          
             }
          }
          
            



          In the Seam application, when you want the external process to be invoked:



            Events.instance().raiseAsynchronousEvent("somethingOccurred", param);


          • 2. Re: Best Process for calling another application from seam application
            valatharv
            Thanks for replying, I need few clarification:

            I need to call a function of external application and pass some arguments including Experiment ID from persist(); as per code below.

            So, as per your valuable suggestion, please suggest :

            a) I should use either (b) or (c) ?

            b) INSTEAD OF DIRECTLY CALLING ExternalApplication function (executeExternalApplication) from "experimentHome.persist()". I should call externalApplicationInvoker.doStuff(args...) from "experimentHome.persist()"
            In this case, will I get "persisted" message and be able to continue navigating seam pages.
            Simultaneously, ExternalApplication process will continue ?

            c) Events.instance().raiseAsynchronousEvent("somethingOccurred", param);
            How can I invoke "ExternalApplication" using this ?, any code sample...

            //SEAM APPLICATION
            @Name("experimentHome")
            public class experimentHome extends EntityHome<Experiment> {

            public String persist(){

                 String persistResult = super.update();
                 
                 //INSTEAD OF DIRECTLY CALLING ExternalApplication here I should call ExternalApplicationInvoker here ?
                 //Note I need to pass some arguments including Experiment ID

                 //executeItraqAnalysisSql("Argxxx",this.getInstance().getId().toString());

                 return persistResult;
                 }

            }

            @Name("externalApplicationInvoker")
            @Scope(ScopeType.STATELSS)
            public class ExternalApplicationInvoker
            {
              
               @Observer("somethingOccurred")
               @Transactional
               public void doStuff(Long param)
               {
                  // do whatever
                   executeExternalApplication(param);
               }

               //EXTERNAL APPLICATION
                 public void executeExternalApplication(String quantType, String qHjid) {
                 
                      String[] args=new String[]{quantType, qHjid};
                                
                      ExternalApplication externalApplication=new ExternalApplication();
                      externalApplication.run(args);     

                 }
            }
            • 3. Re: Best Process for calling another application from seam application
              stefanotravelli

              What I was suggesting is the use of the seam event infrastructure, so option (c). This way you can decouple your ExternalApplication avoiding dependencies.


              The call to the doStuff() method happens as follow: since the method is annotated with @Observer("somethingOccured"), Seam will invoke it when an event of type somethingOccurred is raised.


              In order to raise an event of type somethingOccurred you use the API Events.instance().raiseEvent("somethingOccurred").


              For instance:



              @Override
              public String persist()
              {
                 String result = super.persist();
                 Events.instance().raiseEvent("somethingOccurred");
                 return result;
              }
              



              At this point you have several options.


              First, you can add a parameter to the event. Seam expects this parameter in the observer method signature:


              @Override
              public String persist()
              {
                 String result = super.persist();
                 Events.instance().raiseEvent("somethingOccurred", getId());
                 return result;
              }
              



              Then you can choose to raise the event asynchronously, so that navigation continue while the method processing goes in background (in a separate transaction).


              @Override
              public String persist()
              {
                 String result = super.persist();
                 Events.instance().raiseAsynchronousEvent("somethingOccurred", getId());
                 return result;
              }
              



              Finally, It should be pointed out that EntityHome objects are already raising events while performing CRUD operations.


              In fact, EntityHome.persist() raises an event of type org.jboss.seam.afterTransactionSuccess.Experiment, where Experiment is the name of your class.


              So, without touching your application, you can write an observer that will be notified of the entity being persisted, updated, etc.


              Such events are raised in a special manner, that is after the transaction is successfully committed.


              In the following code an observer listen for afterTransactionSuccess event end raises an asynchronous event in order to call the external application in background:


              @In(create = true)
              ExperimentHome experimentHome;
              
              @Observer("org.jboss.seam.afterTransactionSuccess.Experiment")
              public void experimentSaved()
              {
                 // get the id of the saved instance
                 Long id = experimentHome.getExperimentId();
                ExternalApplication externalApplication=new ExternalApplication();
                externalApplication.run(id);
              
              }
              



              This should work without any change in the EntityHome.


              Hope this helps


              stefano

              • 4. Re: Best Process for calling another application from seam application
                valatharv
                I am really thankful to you and I am also digging/ reading more into this interesting (Seam's one more wonderful) approach. I am new to this so I might ask basic questions but please bear with me...

                When I click "Save"..

                a) quantExperimentHome.persist() is called, Experiment is created and I get correct message as "Successfully created".
                Please consider Experiment as QuantExperiment (refactored...)

                b) Control goes to externalApplicationInvoker but I get the following exception.

                I didn't changed any thing in quantExperimentHome.persist() and below is the code.

                -------------------------------------------------------------
                ERROR [org.jboss.seam.transaction.SynchronizationRegistry] Exception processing transaction Synchronization after completion
                org.jboss.seam.RequiredException: @In attribute requires non-null value: externalApplicationInvoker.quantExperimentHome
                     at org.jboss.seam.Component.getValueToInject(Component.java:2178)
                     at org.jboss.seam.Component.injectAttributes(Component.java:1601)
                ----------------------------------------------------------------

                -- quantExperimentHome.persist()
                public String persist(){     
                     return super.persist();
                }

                -- For testing I am just printing id. This is ExternalApplicationInvoker. Even if I comment "Long id", exception is same.

                @Name("externalApplicationInvoker")
                @Scope(ScopeType.STATELESS)
                public class ExternalApplicationInvoker
                {
                    @In(create = true)
                    QuantExperimentHome quantExperimentHome;

                   @Observer("org.jboss.seam.afterTransactionSuccess.QuantExperiment")
                   public void experimentSaved()
                   {
                     System.out.println("************** ExternalApplicationInvoker.experimentSaved(), ENTERED ");
                      // get the id of the saved instance
                     Long id = experimentHome.getQuantExperimentHjid();
                System.out.println("************** ExternalApplicationInvoker.experimentSaved(), id = "+id);
                     //ExternalApplication externalApplication=new ExternalApplication();
                     //externalApplication.run(id);
                   }
                }
                • 5. Re: Best Process for calling another application from seam application
                  stefanotravelli

                  Check if the name of the home component is exactly "quantExperimentHome".


                  The injection that occurs with:



                  @In
                  QuantExperimentHome quantExperimentHome; <<= THIS NAME...
                  
                  



                  requires that the name of the variable quantExperimentHome corresponds exactly to the name of the component:



                  @Name("quantExperimentHome") <<= ... SHOULD BE EQUAL TO THIS NAME
                  public class QuantExperimentHome extends EntityHome<....
                  


                  • 6. Re: Best Process for calling another application from seam application
                    valatharv
                    Thanks a lot, I am able to execute external application (GenerateItraqAnalysisSql) using the code below.

                    I am facing other issues, not sure if I should place a separate request for it ?

                    Our external application is packaged as a jar and we are invoking it from Seam using externalApplicationInvoker code below.

                    This external application uses hibernate to communicate with the same database which seam uses and does some processing correctly (generates a sql file).

                    When I call this from seam it does the processing but gives below exception :

                    ERROR [org.hibernate.impl.SessionFactoryObjectFactory] Invalid JNDI name: 
                    javax.naming.InvalidNameException
                         at org.jnp.server.NamingServer.rebind(NamingServer.java:159)

                    Please suggest what configuration I am doing wrong.

                    ------------------------------------------
                    @Name("externalApplicationInvoker")
                    @Scope(ScopeType.STATELESS)
                    public class ExternalApplicationInvoker
                    {
                       @In(create = true)
                       QuantExperimentHome quantExperimentHome;
                      
                       @Observer("org.jboss.seam.afterTransactionSuccess.QuantExperiment")
                       public void experimentSaved()
                       {
                          Long qHjid = quantExperimentHome.getInstance().getHjid();
                         String quantType = quantExperimentHome.getInstance().getQuantitationType();    
                         try {

                              String[] args=new String[]{quantType, qHjid.toString()};
                              System.out.println("************** ExternalApplicationInvoker.experimentSaved(), calling GenerateItraqAnalysisSql.run with args : "+quantType+", and "+qHjid);
                              GenerateItraqAnalysisSql generateItraqAnalysisSql=new GenerateItraqAnalysisSql();
                              generateItraqAnalysisSql.run(args);
                              System.out.println("************** ExternalApplicationInvoker.experimentSaved(), CALLED GenerateItraqAnalysisSql.run");
                         } catch (Exception e) {
                              // TODO Auto-generated catch block
                              e.printStackTrace();
                         }
                       }
                    }
                    ------------------------------------------

                    Console output:
                    ---------------
                    INFO  [STDOUT] ************** ExternalApplicationInvoker.experimentSaved(), calling GenerateItraqAnalysisSql.run with args : -Litraq4, and 31
                    INFO  [org.hibernate.cfg.Configuration] configuring from resource: /hibernate.cfg.xml
                    INFO  [org.hibernate.cfg.Configuration] Configuration resource: /hibernate.cfg.xml
                    INFO  [org.hibernate.cfg.Configuration] Configured SessionFactory:
                    INFO  [org.hibernate.cfg.AnnotationBinder] Binding entity from annotated class: com.entity.QuantExperiment
                    INFO  [org.hibernate.cfg.annotations.EntityBinder] Bind entity com.entity.QuantExperiment on table MET_QUANT_EXPERIMENT
                    .....
                    INFO  [org.hibernate.connection.DriverManagerConnectionProvider] Using Hibernate built-in connection pool (not for production use!)
                    INFO  [org.hibernate.connection.DriverManagerConnectionProvider] Hibernate connection pool size: 20
                    .....
                    INFO  [org.hibernate.cfg.SettingsFactory] Echoing all SQL to stdout
                    ....
                    INFO  [org.hibernate.impl.SessionFactoryImpl] building session factory
                    INFO  [org.hibernate.impl.SessionFactoryObjectFactory] Factory name:
                    INFO  [org.hibernate.util.NamingHelper] JNDI InitialContext properties:{}
                    ERROR [org.hibernate.impl.SessionFactoryObjectFactory] Invalid JNDI name:
                    javax.naming.InvalidNameException
                         at org.jnp.server.NamingServer.rebind(NamingServer.java:159)
                         at org.jnp.interfaces.NamingContext.rebind(NamingContext.java:516)
                         at javax.naming.InitialContext.rebind(InitialContext.java:371)
                         at org.hibernate.util.NamingHelper.bind(NamingHelper.java:74)
                         at org.hibernate.impl.SessionFactoryObjectFactory.addInstance(SessionFactoryObjectFactory.java:90)
                         at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:306)
                         at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1294)
                         at util.HibernateUtil.<clinit>(Unknown Source)
                         at com.GenerateItraqAnalysisSql.<init>(Unknown Source)
                         at com.session.ExternalApplicationInvoker.experimentSaved(ExternalApplicationInvoker.java:67)
                         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                    ...
                         at org.jboss.seam.util.Reflections.invoke(Reflections.java:21)
                         at org.jboss.seam.intercept.RootInvocationContext.proceed(RootInvocationContext.java:31)
                    ..
                         at org.jboss.seam.Component.callComponentMethod(Component.java:2092)
                    ...
                         at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
                         at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:446)
                         at java.lang.Thread.run(Thread.java:595)
                    INFO  [STDOUT] GenerateItraqAnalysisSql run Entered, args are :-Litraq4, 31
                    INFO  [STDOUT] GenerateItraqAnalysisSql start processing
                    INFO  [STDOUT] GenerateItraqAnalysisSql, args are : -Litraq4, 31
                    INFO  [STDOUT] GenerateItraqAnalysisSql.getQuantExperimentEntityInstance, Check connection, isConnected :true
                    INFO  [STDOUT] ====> Final SQL is : 'gdfgdfg' AS TREATMENT_1_COMPOUND, 2220.0 AS TREATMENT_1_CONCENTRATION, 'nM' AS TREATMENT_1_CONCENTRATION_UNIT, 111 AS TREATMENT_1_TIME, 'Seconds' AS TREATMENT_1_TIME_UNIT,
                    INFO  [STDOUT] GenerateItraqAnalysisSql SQL processed, End.
                    INFO  [STDOUT] ************** ExternalApplicationInvoker.experimentSaved(), CALLED GenerateItraqAnalysisSql.run
                    WARN  [javax.enterprise.resource.webcontainer.jsf.renderkit] 'for' attribute cannot be null
                    • 7. Re: Best Process for calling another application from seam application
                      valatharv

                      How can I get return message for remove like (Successfully deleted) ExternalApplicationInvoker.


                      Observer is working fine, and function experimentSavedOrUpdated is called on Create, Update and Delete.


                      I need to provide condition that call to executeGenerateItraqAnalysisSql should not be made on DELETE.


                      @Name(externalApplicationInvoker)
                      @Scope(ScopeType.STATELESS)
                      public class ExternalApplicationInvoker
                      {


                      @Observer(org.jboss.seam.afterTransactionSuccess.QuantExperiment)
                         public void experimentSavedOrUpdated()
                         {    
                           //PROVIDE CONDITION here DO NOT CALL ON DELETE
                            executeGenerateItraqAnalysisSql(getQuantitationType(), getExperimentHjid());
                         }     
                      }

                      • 8. Re: Best Process for calling another application from seam application
                        vaughnb.vaughn.butt.datacom.co.nz

                        I think you get the error message:


                        ERROR [org.hibernate.impl.SessionFactoryObjectFactory] Invalid JNDI name:
                        javax.naming.InvalidNameException
                        at org.jnp.server.NamingServer.rebind(NamingServer.java:159)


                        when your hibernate configuration specifies an empty string for the session-factory name :



                        <hibernate-configuration>
                        <session-factory name="">
                          <property name="show_sql">false</property>
                        ...
                        </hibernate-configuration>


                        Removing the name attribute from session element will fix this problem. So will making the name attribute non-empty. See the documentation here.


                        Setting org.hibernate logging to INFO and looking for "INFO  [org.hibernate.impl.SessionFactoryObjectFactory] Factory name:" will show you that hibernate is trying to register the SessionFactory with a name with no non-whitespace.