12 Replies Latest reply on Jul 9, 2013 6:58 PM by Tony Herstell

    Conversation (Old Seam vs CDI/EJB)

    Tony Herstell Master

      I have a very clean JEE 6 project (No Seam3/CODI addons etc.).

       

       

      In "old" Seam 2 a conversation was "transactional". If the conversation was not ended then all updates were rolled back.

      Brillaint, simple to understand and use and basically "easy".

       

      I have been struggling with CDI/EJB to replicate the same thing and pobably am confused.

       

      Let me explain...

       

      I set up my bean:

         Make it stateful (to get Transactions

         Make it conversational

       

      //Tell prettyfaces to use this bean when the pattern matches and show the manage Organisations page. Also support a Query Param (cid).
      @URLMappings(mappings = { @URLMapping(id = "manageOrganisations", pattern = "/organisation/manage", viewId = "/pages/organisation/manageOrganisations.xhtml"),
            @URLMapping(id = "createOrganisation", pattern = "/organisation/create", viewId = "/pages/organisation/CRUDOrganisation.xhtml"),
            @URLMapping(id = "readOrganisation", pattern = "/organisation/view", viewId = "/pages/organisation/CRUDOrganisation.xhtml"),
            @URLMapping(id = "updateOrganisation", pattern = "/organisation/update", viewId = "/pages/organisation/CRUDOrganisation.xhtml"),
            @URLMapping(id = "deleteOrganisation", pattern = "/organisation/delete", viewId = "/pages/organisation/CRUDOrganisation.xhtml") })
      // Leverage EJB to get Transactional Support
      @Stateful
      // Lets be long running (multiple client-server round trips) - Needs Extended on
      // PersistanceContext too to hold onto my objects and not get LIEs.
      @ConversationScoped
      // EL Can can find me...
      @Named
      public class OrganisationManagementController extends BaseController {
      
         @URLQueryParameter("cid")
         private String cid;
      
      
         // DON'T let any Entities be Proxied by WELD so just use class local.
         // According to this definition, JPA entities are technically managed beans.
         // However, entities have
         // their own special lifecycle, state and identity model and are usually
         // instantiated by JPA or using
         // new. Therefore we don't recommend directly injecting an entity class. We
         // especially recommend
         // against assigning a scope other than @Dependent to an entity class, since
         // JPA is not able to
         // persist injected CDI proxies.
         private List<Organisation> organisations;
      
      ...
      
         // Access to the persistence store so we can read from the DB with Long
         // Running extension to go with Conversation Scope above.
         @PersistenceContext(type = PersistenceContextType.EXTENDED)
         private EntityManager em;
      
         // Inject this to make conversation magic happen
         @Inject
         private Conversation conversation;
      
      ...
      

       

       

      I enter the conversation like this:

       

       

         // A good place to START a conversation from. ** land here off a menu
         // click...
         public String init() {
            this.logger.info(">>>init");
            if (!this.conversation.isTransient()) {
               this.logger.info("Existing conversation found:" + this.conversation.getId() + " Ending it...");
               this.conversation.end();
            }
            this.logger.info("+++CONVERSATION START");
            this.conversation.begin(); // START THE LONG RUNNING CONVERSATION
            this.logger.info("conversation:" + this.conversation.getId());
            this.logger.info("<<<init");
            return "pretty:manageOrganisations";
         }
      
      

       

      and ping-pong through user requests and responses.

       

       

      Now...

      Create and Update take effect immediately in the DB.

       

      public String updateOrganisation(Organisation organisation) {
            this.logger.info(">>> updateOrganisation " + organisation.getName());
            this.logger.info("conversation:" + this.conversation.getId());
            this.em.merge(organisation);
            this.updateEvent.fire(organisation);
            this.logger.info("Updated " + organisation.getName());
            // Clear the organisation
            this.organisationToBeAffected = null;
            String[] params = { organisation.getName() };
            this.postGlobalMessage("organisation_updated_successfully", params, null, null, FacesMessage.SEVERITY_INFO);
            this.logger.info("<<< updateOrganisation " + organisation.getName());
            return "pretty:manageOrganisations";
         }
      
      

      Delete has no effect in the DB.

         public String deleteOrganisation(Organisation organisation) {
            String navigationToReturn = null;
            this.logger.info(">>> Deleting " + organisation.getName());
            this.logger.info("conversation:" + this.conversation.getId());
            this.deleteEvent.fire(organisation);
            this.organisations.remove(organisation);
            this.em.remove(organisation);
            navigationToReturn = "pretty:manageOrganisations";
            this.logger.info("<<< Deleting " + organisation.getName());
            return navigationToReturn;
         }
      

       

      I expected nothing to get perisited until I ended the conversation (and stuff to never get perisited if I didnt!)...

       

       

         public String finished() { // land on here when finished up to return to
                                    // Main Page (a "done" button).
            this.logger.info(">>>finished");
            this.logger.info("+++CONVERSATION END for conversation:" + this.conversation.getId());
            if (!this.conversation.isTransient()) { // May have been an error (don't
                                                    // get trapped on the page).
               this.conversation.end(); // END THE LONG RUNNING CONVERSATION
            }
            this.logger.info("<<<finished");
            return "pretty:home";
         }
      

       

       

      I know that CDI Conversations are subtely differnt to Seam conversations.

       

      Please can you suggest how to get the simple Seam2 Conversation capability back.

        • 1. Re: Conversation (Old Seam vs CDI/EJB)
          Tony Herstell Master

          Just for clairity when I call the controllers to launch the Update or Delete confirmation page I use JSF and pass params (from each line on a table as it happens)

           

                            <p:commandButton icon="ui-icon ui-icon-disk" action="#{organisationManagementController.launchUpdateOrganisation(eachOrganisation)}"
                               value="#{messages.button_update}">
                               <f:param name="cid" value="#{organisationManagementController.cid}" /> <!-- FORCE ConverstionId to be added -->
                            </p:commandButton>
                            <p:commandButton icon="ui-icon ui-icon-trash" action="#{organisationManagementController.launchDeleteOrganisation(eachOrganisation)}"
                               value="#{messages.button_delete}">
                               <f:param name="cid" value="#{organisationManagementController.cid}" /> <!-- FORCE ConverstionId to be added -->
                            </p:commandButton>
          
          

           

          When I confirm the Update/Delete etc :

           

           

                       <ui:fragment rendered="#{organisationManagementController.CRUDMode eq 'UPDATE'}">
                         <p:commandButton id="updateButton" update=":organisationForm:organisationPanel :messagesForm" icon="ui-icon ui-icon-disk"
                            action="#{organisationManagementController.updateOrganisation(organisationManagementController.organisation)}"
                            value="#{messages.button_update} #{messages.organisation}" onstart="return confirm('#{messages.are_you_sure}')">
                            <f:param name="cid" value="#{organisationManagementController.cid}" />
                            <!-- FORCE ConverstionId to be added -->
                         </p:commandButton>
                      </ui:fragment>
          
          
                      <ui:fragment rendered="#{organisationManagementController.CRUDMode eq 'DELETE'}">
                         <p:commandButton id="deleteButton" update=":organisationForm:organisationPanel :messagesForm" icon="ui-icon ui-icon-disk"
                            action="#{organisationManagementController.deleteOrganisation(organisationManagementController.organisation)}"
                            value="#{messages.button_delete} #{messages.organisation}" onstart="return confirm('#{messages.are_you_really_sure}')">
                            <f:param name="cid" value="#{organisationManagementController.cid}" />
                            <!-- FORCE ConverstionId to be added -->
                         </p:commandButton>
                      </ui:fragment>
          
          
          • 2. Re: Conversation (Old Seam vs CDI/EJB)
            Tony Herstell Master

            Doing a ruthless refactor....

            Please ignore above (apart from fact its not Transactional!)

            Moving the list of "organisations" to be managed by its own Controller in Session scope (events used to update the list of Organisations)

            Moving the "actions" required (CRUD - Create, Read, Update, Delete) to a new Controller is Conversation scope (such that any changes duing CRUD should be rolled back if the conversation is abandoned).

            Inject the Action Controller into the Manage Controller - Create shortcuts straight to Action Controller.

            ...

            • 3. Re: Conversation (Old Seam vs CDI/EJB)
              Lincoln Baxter III Master

              Hey Tony,

               

              I'm pretty sure that you need to use an @PersistenceContext EntityManager em; Just inject that into the @ConversationScoped bean and you should be good... I "think" that should do it, but it seems like that's not working for you, so I don't really have an instant answer. I know I *used* to understand this stuff, and I wrote about it here, so maybe this article will help?

               

              http://ocpsoft.org/java/spring-to-java-ee-a-migration-guide-cdi-jsf-jpa-jta-ejb/#section-8

               

              I'm sorry, it's been a long time since I did any of this stuff. Too long. I'll try to do a little digging, but I have a busy weekend

              ~Lincoln

              • 4. Re: Conversation (Old Seam vs CDI/EJB)
                Tony Herstell Master

                Hi Lincoln,

                 

                Your link is a link I often send to other people! go figure

                 

                I did the refactor and although I am happier with the code it's not solved the problems So... I roped in a friend (bloody genius actually) and we did some digging ( not much as he was busy too - somthing about some event in American History and Broken Ribs ).

                 

                Anyhow; the outcome was not favourable . And based on the fact that I had pressed on so far on the project "just expecting" delete to, well, actually delete ... and transactions, to actually just work there were no "simple" cases in the project to fiddle with.

                 

                Coming from Seam I have been a tad underwhelmed by J6EE due to the amount of trouble I have had...

                e.g. using:

                • your (someones) fix to get messages propogated (MultiPageMessageSupport)
                • Having to apply a DateTime convertor as one does not come as standard it seems???
                • Handling Exceptions
                • Some hack to allow the faster processing of huge chuncks of data with JSF (NO I don't now why I need this but it certainly speeds up things!)

                 

                @ConversationScoped
                @Named
                //@FacesConverter(forClass=Attribute.class) - Not till JSF 2.2 as the conversation doesn't get injected (see page when you fix this as can remove the name from page)
                public class AttributeConverter implements Converter, Serializable {
                
                    private Logger logger = Logger.getLogger(AttributeConverter.class.getName());
                
                    final private Map<String, Object> converterMap = new HashMap<String, Object>();
                    final private Map<Object, String> reverseConverterMap = new HashMap<Object, String>();
                
                    @Inject
                    private transient Conversation conversation;
                
                    private int incrementor = 1;
                
                    @Override
                    public Object getAsObject(FacesContext context, UIComponent uIcomponent, String value) {
                        if (this.conversation.isTransient()) {
                           logger.warning("Conversion attempted without a long running conversation");
                        }
                        return this.converterMap.get(value);
                    }
                
                
                    @Override
                    public String getAsString(FacesContext context, UIComponent uIcomponent, Object value) {
                        if (this.conversation.isTransient()) {
                            logger.warning("Conversion attempted without a long running conversation");
                        }
                
                
                        if (this.reverseConverterMap.containsKey(value)) {
                            return this.reverseConverterMap.get(value);
                        } else {
                            final String incrementorStringValue = String.valueOf(this.incrementor++);
                            this.converterMap.put(incrementorStringValue, value);
                            this.reverseConverterMap.put(value, incrementorStringValue);
                            return incrementorStringValue;
                        }
                    }
                
                }
                
                

                 

                Well; you actually create concrete instances for it (e.g.):

                 

                 

                                  <!-- parent -->
                                  <h:outputLabel for="parentCU" value="#{messages.customer_parent}" />
                                  <h:outputLabel for="parentCU" styleClass="mandatory" value="" />
                                  <p:selectOneMenu id="parentCU" value="#{customer.parent}" converter="#{customerConverter}">
                                    <f:selectItems value="#{customerManagementController.customerValues}"/>
                                  </p:selectOneMenu>
                                  <p:message for="parentCU" />
                
                

                 

                 

                    private void setupCustomerValues() {
                        List<Customer> allCustomers = this.getCustomers();
                        this.customerSelectOptions = new SelectItem[allCustomers.size() + 1];
                        FacesContext fc = FacesContext.getCurrentInstance();
                        Locale myLocale = fc.getExternalContext().getRequestLocale();
                        ResourceBundle myResources = ResourceBundle.getBundle("messages", myLocale);
                        int index = 0;
                        this.customerSelectOptions[index++] = new SelectItem(null, myResources.getString("none_selected"));
                        for (Customer eachCustomer : allCustomers) {
                            this.customerSelectOptions[index++] = new SelectItem(eachCustomer, eachCustomer.getName());
                        }
                    }
                
                    public SelectItem[] getCustomerValues() {
                        return this.customerSelectOptions;
                    }
                
                

                 

                 

                @ConversationScoped
                @Named
                //@FacesConverter(forClass=Customer.class) - Not till JSF 2.2 as the conversation doesn't get injected (see page when you fix this as can remove the name from page)
                public class CustomerConverter implements Converter, Serializable {
                
                
                    private static final long serialVersionUID = -736745113207882316L;
                
                
                    private Logger logger = Logger.getLogger(CustomerConverter.class.getName());
                
                    final private Map<String, Object> converterMap = new HashMap<String, Object>();
                    final private Map<Object, String> reverseConverterMap = new HashMap<Object, String>();
                
                    @Inject
                    private transient Conversation conversation;
                
                    private int incrementor = 1;
                
                
                    @Override
                    public Object getAsObject(FacesContext context, UIComponent uIcomponent, String value) {
                        if (this.conversation.isTransient()) {
                           logger.warning("Conversion attempted without a long running conversation");
                        }
                        return this.converterMap.get(value);
                    }
                
                
                    @Override
                    public String getAsString(FacesContext context, UIComponent uIcomponent, Object value) {
                        if (this.conversation.isTransient()) {
                            logger.warning("Conversion attempted without a long running conversation");
                        }
                
                
                        if (this.reverseConverterMap.containsKey(value)) {
                            return this.reverseConverterMap.get(value);
                        } else {
                            final String incrementorStringValue = String.valueOf(this.incrementor++);
                            this.converterMap.put(incrementorStringValue, value);
                            this.reverseConverterMap.put(value, incrementorStringValue);
                            return incrementorStringValue;
                        }
                    }
                }
                
                
                

                 

                 

                 

                 

                So moving on....

                 

                In Seam I never had to use any transactions as they were usefully hidden away behind conversations and I just refactoroed any complexity to that, sensible, boundary.

                 

                Again in Seam if you stated a conversation and did any updates then you could just ditch the conversation and "hey presto" all your updated were "rolled back" (or not applied)... I think you have hane had to set flush to manaul, but not even sure abouyt that either as it just worked and you forget about it and just copy forwards that stuff from project to poject once you trust it.

                 

                So, What we found was that delete was not sending deletes to the DB...

                 

                 

                 

                   /**
                    * Key person to contact about this Organisation (and display)
                    */
                   @XmlElement(name = "key_decision_maker")
                   @XmlIDREF
                   @NotNull
                   @ManyToOne(fetch = FetchType.EAGER, cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH })
                   private User keyDecisionMaker;
                
                

                 

                Note I added the

                 

                 cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }
                

                during debugging session to try to tell Hibernate to leave it alone!

                 

                I had to go all "EAGER" to get JAX-RX/JAX-B to work....

                 

                Now; I have

                 

                <property name="hibernate.show_sql" value="true" />
                

                 

                So I can see the hibernate chatter to the DB in the server log (displayed in MyEclipse).

                I ALSO checked this in the MySQL server log file that I dug around for in the filesystem that happily loogeed stuff once we pushed the commant at MySQL to log stuff.

                 

                We found that if we foricbly made the references (a back reference in this case to a user) to null then hIbenrate actioned the delete; but then I had a constraint violation due to the fact the field had to be @NotNull.

                 

                This was about as far as we got and I will look into Hibernate Validator that I suck in.

                 

                 


                <properties>


                <!-- Explicitly declaring the source encoding eliminates the following message: -->


                <!-- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! -->


                <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>


                <!-- Timestamp format for the maven.build.timestamp property -->


                <!-- You can reference property in pom.xml or filtered resources (must enable third-party plugin if using Maven < 2.1) -->


                <maven.build.timestamp.format>yyyyMMdd'T'HHmmss</maven.build.timestamp.format>


                <!-- To specify the JBoss AS directory the "Maven way", set the jboss.home property in an active profile in $HOME/.m2/settings.xml -->


                <!-- By default, we assign it to the value of the JBOSS_HOME envrionment variable -->

                </properties>

                 

                 


                <build>



                <plugins>




                <plugin>





                <artifactId>maven-compiler-plugin</artifactId>




                <version>2.3.2</version>




                <configuration>





                <source>1.6</source>





                <target>1.6</target>




                </configuration>



                </plugin>




                <!-- Ensure we have the Maven Surefire Plugin for Arquilliann -->




                <plugin>





                <artifactId>maven-surefire-plugin</artifactId>




                <version>2.12</version>




                <configuration>





                <argLine>-Xmx1024m -XX:MaxPermSize=256m</argLine>




                </configuration>



                </plugin>



                </plugins>


                </build>

                 

                ...

                 



                <dependency>



                <groupId>org.primefaces</groupId>



                <artifactId>primefaces</artifactId>



                <version>3.5</version>


                </dependency>

                 

                 



                <dependency>



                <groupId>com.ocpsoft</groupId>



                <artifactId>prettyfaces-jsf2</artifactId>



                <version>3.3.3</version>


                </dependency>

                 

                 



                <dependency>



                <groupId>com.googlecode.libphonenumber</groupId>



                <artifactId>libphonenumber</artifactId>



                <version>[4.5,5.0)</version>


                </dependency>

                 

                 



                <dependency>



                <groupId>org.hibernate</groupId>



                <artifactId>hibernate-validator</artifactId>



                <version>4.1.0.Final</version>



                <scope>provided</scope>



                <exclusions>




                <exclusion>





                <groupId>org.slf4j</groupId>





                <artifactId>slf4j-api</artifactId>




                </exclusion>



                </exclusions>


                </dependency>

                 

                 

                 

                 

                Apart from Arquillian (err.a little out of date) and ModeShape (also a little out of date) this is ALL I pull; in so its quite clean project...

                 

                 

                Conclusion

                I am left with the sad impression that JEE 6 will run quite well stateless but not much work has been done to look into long running stateful stuff (more suited to businesses) and look forward to that improving over time.

                 

                As for me, due to lack of time I wiill go with Plan B.. Don't actually delete ANYTHING just marked it as deleted and filter the deleted records out in the SQL.. after all its only Storage and saves working out how to Audit stuff later.

                 

                Tony.


                • 5. Re: Conversation (Old Seam vs CDI/EJB)
                  Lincoln Baxter III Master

                  stupid question. is prettyfaces propagating the conversationId when it redirects you after navigation?

                  • 6. Re: Conversation (Old Seam vs CDI/EJB)
                    Tony Herstell Master

                    Only stupid question is the one you never asked

                     

                    Are you meaning this?

                     

                    https://community.jboss.org/thread/194639

                    • 7. Re: Conversation (Old Seam vs CDI/EJB)
                      Lincoln Baxter III Master

                      Not exactly the answer to the question I was asking. I mean... is that actually working. E.g. If you look at the network traffic in your browser, you should see a 302 redirect temporary to some application URL with a cid=? parameter.

                       

                      Is that the case? if so, does the CID match? If not... that may be the bug, and you should try upgrading to the latest version of PrettyFaces (based on rewrite)

                      • 8. Re: Conversation (Old Seam vs CDI/EJB)
                        Tony Herstell Master

                        I have had no problems at all with Conversations with Prettyfaces.

                         

                        You see the workround as the CID is not being propogated but otherwise I dont see a problem.

                         

                        HINT:

                        I dont get into "redirect" anywhere... I may happen automagically; especailly as I use your annotations... !

                         

                        //Tell prettyfaces to use this bean when the pattern matches and show the manageUsers page. Also support a Query Param (cid).
                        @URLMappings(mappings = { @URLMapping(id = "registerUser", pattern = "/users/register", viewId = "/pages/users/CRUDUser.xhtml"),
                              @URLMapping(id = "createUser", pattern = "/users/create", viewId = "/pages/users/CRUDUser.xhtml"),
                              @URLMapping(id = "readUser", pattern = "/users/view", viewId = "/pages/users/CRUDUser.xhtml"),
                              @URLMapping(id = "updateUser", pattern = "/users/update", viewId = "/pages/users/CRUDUser.xhtml"),
                              @URLMapping(id = "deleteUser", pattern = "/users/delete", viewId = "/pages/users/CRUDUser.xhtml") })
                        

                         

                        Typical button mashing code:

                         

                                    <ui:fragment rendered="#{userManagementActionController.CRUDMode eq 'UPDATE'}">
                                       <p:commandButton id="updateButton" update=":userForm:userPanel :messagesForm :loginStatusArea" icon="ui-icon ui-icon-disk"
                                          action="#{userManagementActionController.updateUser(userManagementActionController.user)}" value="#{messages.button_update} #{messages.user}"
                                          onstart="return confirm('#{messages.are_you_sure}')">
                                          <f:param name="cid" value="#{userManagementActionController.cid}" />
                                          <!-- FORCE ConverstionId to be added -->
                                       </p:commandButton>
                                    </ui:fragment>
                        

                         

                         

                        Even with pretty-config.xml is trivial (as I use your annotations if ia all possible - configuration for the Controller IN the Controller makes more sense to me):

                         

                        <?xml version="1.0" encoding="UTF-8"?>
                        <pretty-config xmlns="http://ocpsoft.com/prettyfaces/3.3.2"
                                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                  xsi:schemaLocation="
                                http://ocpsoft.com/prettyfaces/3.3.2
                                http://ocpsoft.com/xml/ns/prettyfaces/ocpsoft-pretty-faces-3.3.2.xsd">
                        
                        
                                  <!-- PrettyFaces always navigates with POST-REDIRECT-GET
                                       ===================================================
                                       No need for ?faces-redirect=true
                        
                        
                                        pretty-config.xml is :
                                          <url-mapping id="manageEvents">
                                              <pattern value="/events/manage" />
                                              <view-id value="/pages/events/manageEvents.xhtml" />
                                          </url-mapping>
                                           ...
                                          public String goManageEvents() {
                                             // ...
                                             return "pretty:manageEvents";
                                          }
                                  -->
                        
                        
                                  <url-mapping id="home">
                                            <pattern value="/"></pattern>
                                            <view-id value="/pages/landing.xhtml" />
                                  </url-mapping>
                        
                        
                                  <!-- Cant annotate this as passing the query-param does not work; so leave 
                                            in here for now -->
                                  <url-mapping id="error">
                                            <pattern value="/error"></pattern>
                                            <query-param name="exception">#{exception}</query-param>
                                            <view-id value="/pages/error/generalError.xhtml" />
                                  </url-mapping>
                        
                        
                                  <url-mapping id="unauthorised">
                                            <pattern value="/unauthorised"></pattern>
                                            <view-id value="/pages/error/unauthorisedAccess.xhtml" />
                                  </url-mapping>
                          
                        </pretty-config>
                        
                        

                         

                         

                        I did look into moving to rewrite but I didnt see these annotations available... yet...

                        As you know; I have had a few plays with it

                           http://ocpsoft.org/support/topic/not-seeing-css-or-images/

                        But went back to 3.3.3 as its just "simple" and works for now as didnt have time to learn it all.

                        Sadly; unlikely to move over soon either... due to starting a new business and being the only coder at the moment!

                         

                        I have also avoided delete by not delieting anyhting for now (especially after the stuff below)

                         

                        ....

                        On another note...

                         

                        Following on for JEE and Conversations...

                        This is a classic:

                         

                        Update works fine

                        Delete says the Entity is unattached.... Go Figure....

                         

                        Both (part of same conversational bean) called in exactly the same way.

                        Update and Delete both launched off a dataTable of users through a Session Scoped Controller.

                         

                           public String updateUser(User user) {
                              this.logger.info(">>> updateUser " + user.getLastName() + ", " + user.getFirstName());
                              this.logger.info("conversation:" + this.conversation.getId());
                              this.em.merge(user);
                              this.logger.info("Updated " + user.getLastName() + ", " + user.getFirstName());
                              this.updateEvent.fire(user);
                              // Clear the user
                              this.userToBeAffected = null;
                              String[] params = { user.getFirstName() };
                              this.postGlobalMessage("user_updated_successfully", params, null, null, FacesMessage.SEVERITY_INFO);
                              this.logger.info("<<< updateUser " + user.getLastName() + ", " + user.getFirstName());
                              return "pretty:manageUsers";
                           }
                        
                        
                           public String deleteUser(User user) {
                              String navigationToReturn = null;
                              this.logger.info(">>> Deleting " + user.getLastName() + ", " + user.getFirstName());
                              this.logger.info("conversation:" + this.conversation.getId());
                              if (this.identity.getLoggedInUserId() == user.getId()) {
                                 // Trying to delete self
                                 this.postGlobalMessage("users_cannot_delete_yourself", FacesMessage.SEVERITY_WARN);
                              } else {
                                 this.deleteEvent.fire(user);
                                 this.em.remove(user);
                                 navigationToReturn = "pretty:manageUsers";
                              }
                              this.logger.info("<<< Deleting " + user.getLastName() + ", " + user.getFirstName());
                              return navigationToReturn;
                           }
                        

                         

                        Hell, it may be me, but I think Conversations in EE are wee bit borked/untested.

                        • 9. Re: Conversation (Old Seam vs CDI/EJB)
                          Lincoln Baxter III Master

                          Yeah, conversations are a PITA in EE. They really tied them to JSF and they really shouldn't have.

                           

                          Regarding the redirect - prettyfaces is doing this for you using the post-redirect-get pattern. If you say the CID is being propagated, I'll trust you on that.

                           

                          Regarding moving to Rewrite - check out http://ocpsoft.org/prettyfaces/ - do you think it's worth creating another Uber-JAR for prettyfaces, or are two jars manageable enough?

                           

                          Not sure what else to do about Conversations. You might try asking in one of the related IRC channels like #wildfly or #weld. I'll see if I can ping a few people for you and get them to come look at this. Sorry I can't be of more help.

                          • 10. Re: Conversation (Old Seam vs CDI/EJB)
                            Tony Herstell Master

                            > Yeah, conversations are a PITA in EE. They really tied them to JSF and they really shouldn't have.

                            I like JSF heaps... it's great as long as you don't try to write it like struts/JSP/etc.

                            It's a server side technology and IMHO thats why people find it such a pain as they try to make JSF what XXX did (and code in the view with JQuery etc).

                            JSF + EL is awesome IMHO and add in the component suites like Prime/Rich/Ice faces (that do the heavy lifting for you) and even ADF ? etc. and you "got it"... RAD!

                             

                             

                            It may be me but I don't think JEE comittee did Conversations well either; when compared to seam (you got to wonder why Gavin King was not involved!).

                            e.g.

                               You can "end" a conversation; but I want to Abort one (and have all changes rolled back and free the memory) and you can't!

                               You have to add cruff like this:    

                             // Start a Conversation
                                  if (!this.conversation.isTransient()) {
                                     this.logger.info("Existing conversation found:" + this.conversation.getId() + " Ending it...");
                                     this.conversation.end();
                                  }
                                  this.logger.info("+++CONVERSATION START");
                                  this.conversation.begin(); // START THE LONG RUNNING CONVERSATION
                                  this.logger.info("conversation:" + this.conversation.getId());
                            

                               You cant Nest Conversations

                                There are no annotations for Scops like @Begin(scope=ScopeKind.conversation)

                             

                            Seam had conversations working; well!

                             

                             

                            >Regarding the redirect - prettyfaces is doing this for you using the post-redirect-get pattern.

                            Thank you

                             

                             

                            > Regarding moving to Rewrite - check out http://ocpsoft.org/prettyfaces/ - do you think it's worth creating another Uber-JAR for prettyfaces, or are two jars manageable enough?

                            Humm...

                            I think its more to with a lack of ability to think!

                            If I can add an annotation like

                               @URLMappings(mappings = { @URLMapping(id = "createSite", pattern = "/site/create", viewId = "/pages/site/CRUDSite.xhtml"),
                                  @URLMapping(id = "readSite", pattern = "/site/view", viewId = "/pages/site/CRUDSite.xhtml"),
                                  @URLMapping(id = "updateSite", pattern = "/site/update", viewId = "/pages/site/CRUDSite.xhtml"),
                                  @URLMapping(id = "deleteSite", pattern = "/site/delete", viewId = "/pages/site/CRUDSite.xhtml") })
                            

                            then it means I dont have to think much really...

                            My Controller can spit out those short URLS and Prettyfaces "fixes" things.

                             

                            With re-write you need to understand a lot more (and with that comes the power) and I DID get into it a bit a while ago as you know.

                             

                            However; with a mountain of code to write and no income then time is critial and I can go back later to, errr. do some functional tests! and add in ReWrite etc.

                             

                            I will struggle through the conversation problems anyhow; not to worry too much ... ill just re-engineer round it.

                             

                            Thx heaps for corresponding (in public!).

                            • 11. Re: Conversation (Old Seam vs CDI/EJB)
                              Tony Herstell Master

                              For Example...

                              Here is some "nasty" code (Simple+++ Security ) I did; that could be done far better with ReWrite...

                              (It will probably make your eyes water!)

                               

                               

                              But, evem then, then I could look to Picket Fence or the like.

                               

                              I chose not to go Picket Fence or the like as the user, in this project, can have access to multiple organisations and have different (and multiple) "roles" in each organisation; so I simply coded all that as I didn't have the time to learn how another package could (possibly) handle that and deal with the compromises.

                               

                              @WebFilter(urlPatterns = "/*")
                              public class LoggedInFilter implements Filter {
                                
                                  private Logger logger = Logger.getLogger(LoggedInFilter.class.getName());
                              
                                  private static final boolean filterLoggingOverride = false;
                              
                                  @Inject
                                  Identity identity;
                              
                                  @Inject
                                  FacesContextBuilder facesContextBuilder;
                              
                                  static final String DIRECT_ACCESS_TO_RESOURCES = "/apiarymanager/resources";
                              
                                  private List<String> allowedPagesWhenNotLoggedIn;
                                  static final String LANDING_PAGE_URL = "/apiarymanager/";
                                  static final String RESTFUL_PAGE_BASE_URL = "/apiarymanager/rest";
                                  static final String UNAUTHORISED_PAGE = "/apiarymanager/unauthorised";
                              
                                  @Override
                                  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
                                          throws IOException, ServletException {
                              
                                      HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
                                      HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
                              
                                      boolean bypassCheck = false;
                                      boolean passedLoginTrap = false;
                              
                                      if (filterLoggingOverride) {
                                          this.logger.info(">> =========== doFilter");
                                      }
                                      if (this.identity.isLoggedIn()) {
                                          if (filterLoggingOverride) {
                                              this.logger.info("User: " + this.identity.getName() + "(ID:" + this.identity.getLoggedInUserId() + ")");
                                          }
                                          passedLoginTrap = true;
                                      }
                                      else {
                                          if (filterLoggingOverride) {
                                              this.logger.info("User not logged in");
                                          }
                                          String pageHeadingFor = httpServletRequest.getRequestURI();
                                          if (filterLoggingOverride) {
                                              this.logger.info("heading to page: " + pageHeadingFor);
                                          }
                                          if (pageHeadingFor.contains("javax.faces.resource") || pageHeadingFor.contains(DIRECT_ACCESS_TO_RESOURCES)) {
                                              if (filterLoggingOverride) {
                                                  this.logger.info("It's a resource so ignore...");
                                              }
                                              bypassCheck = true;
                                          }
                                          else if (pageHeadingFor.contains(RESTFUL_PAGE_BASE_URL)) {
                                              if (filterLoggingOverride) {
                                                  this.logger.info("It's a restful call so ignore...");
                                              }
                                              bypassCheck = true;
                                          }
                              
                                          else {
                                              LOGIN_PAGE_TRAP: for (String eachPage : this.allowedPagesWhenNotLoggedIn) {
                                                  if (pageHeadingFor.equalsIgnoreCase(eachPage)) {
                                                      if (filterLoggingOverride) {
                                                          this.logger.info("Passed Login Trap...");
                                                      }
                                                      passedLoginTrap = true;
                                                      break LOGIN_PAGE_TRAP;
                                                  }
                                              }
                                          }
                                      }
                              
                                      if (!passedLoginTrap && !bypassCheck) {
                                          if (filterLoggingOverride) {
                                              this.logger.info("Redirecting...");
                                          }
                                          httpServletResponse.sendRedirect(UNAUTHORISED_PAGE);
                                      }
                                      else {
                                          filterChain.doFilter(servletRequest, servletResponse);
                                      }
                                      if (filterLoggingOverride) {
                                          this.logger.info("<< =========== doFilter");
                                      }
                                  }
                              
                                  @Override
                                  public void init(FilterConfig arg0) throws ServletException {
                                      if (filterLoggingOverride) {
                                          this.logger.info(">> =========== init");
                                      }
                                      this.allowedPagesWhenNotLoggedIn = new ArrayList<String>();
                                      this.allowedPagesWhenNotLoggedIn.add(LANDING_PAGE_URL);
                                      this.allowedPagesWhenNotLoggedIn.add("/apiarymanager/users/register");
                                      this.allowedPagesWhenNotLoggedIn.add("/apiarymanager/error");
                                      this.allowedPagesWhenNotLoggedIn.add("/apiarymanager/unauthorised");
                                      this.allowedPagesWhenNotLoggedIn.add("/apiarymanager/viewExpired");
                                      if (filterLoggingOverride) {
                                          this.logger.info("<< =========== init");
                                      }
                                  }
                              
                                  @Override
                                  public void destroy() {
                                      if (filterLoggingOverride) {
                                          this.logger.info(">> =========== destroy");
                                          this.logger.info("<< =========== destroy");
                                      }
                                  }
                              }
                              
                              
                              
                              • 12. Re: Conversation (Old Seam vs CDI/EJB)
                                Tony Herstell Master

                                Oh yes...

                                Back to Conversations;

                                   The Weld doco says you can set a Conversation timeout and "the server can ignore it"...

                                 

                                Thats really great...

                                 

                                When a conversation times out it locks up the page (literally).

                                 

                                I handles session timout on the page with a popup and forced re-direct to home page (logging out in the process)... but with a timed out conversation the "components" tend to just hang.

                                 

                                Sigh...