10 Replies Latest reply on Jan 2, 2009 8:46 PM by nbhatia.bhatian.comcast.net

    IdentityManager.grantRole() not persisting to database

    nbhatia.bhatian.comcast.net

      I am trying to implement users and roles in my application (similar to SeamSpace). I am able to create users, but I am not able to grant them any roles. I traced through IdentityManager.grantRole() code and it looks like I am hitting JpaIdentityStore.grantRole(String username, String role). In this method, the correct role is added to the user and mergeEntity(user) is called (as expected), but I don't see any insert to the database in my log file! See the log below:


      [myapp.OpenAccountController] >>> Entering CreateUserService.createUser()
      [myapp.CreateUserService] >>> Entering IdentityManager.createUser()
      [org.hibernate.SQL] select ... from User user0_ where user0_.username=?
      [org.hibernate.SQL] insert into User (version, enabled, username, passwordHash, person_id) values (?, ?, ?, ?, ?)
      [myapp.CreateUserService] <<< Exiting IdentityManager.createUser()
      [myapp.CreateUserService] >>> Entering IdentityManager.grantRole()
      [org.hibernate.SQL] select ... from User user0_ where user0_.username=?
      [org.hibernate.SQL] select ... from Role role0_ where role0_.name=?
      [myapp.CreateUserService] <<< Exiting IdentityManager.grantRole()
      [myapp.OpenAccountController] <<< Exiting CreateUserService.createUser()
      



      I was expecting the following insert at the end of IdentityManager.grantRole():


      insert into UserRole (user_id, role_id) values (?, ?)
      



      Can someone help me figure this out?


      My user and role classes are defined as follows:


      @Entity
      @Table(uniqueConstraints = @UniqueConstraint(columnNames = "username"))
      public class User implements Serializable {
          private String username;
          private String passwordHash;
          private Set<Role> roles;
          ...
          @UserRoles
          @ManyToMany(targetEntity = Role.class)
          @JoinTable(name = "UserRole",
                  joinColumns = @JoinColumn(name = "user_id"),
                  inverseJoinColumns = @JoinColumn(name = "role_id"))
          public Set<Role> getRoles() {
              return roles;
          }
          public void setRoles(Set<Role> roles) {
              this.roles = roles;
          }
      }
      



      @Entity
      public class Role implements Serializable {
          ...
          private String name;
      
          @RoleName
          public String getName() {
              return name;
          }
          public void setName(String name) {
              this.name = name;
          }
      }
      



      CreateUserService is defined as follows:


      @Name("createUserService")
      @AutoCreate
      public class CreateUserService {
          @In private IdentityManager identityManager;
      
          public void createUser(
                  final String username,
                  final String password,
                  final String role) {
              
              new RunAsOperation() {
                  public void execute() {
                     identityManager.createUser(username, password);
                     identityManager.grantRole(username, role);
                  }         
               }.addRole(RoleName.admin.toString())
                .run();
          }
      }
      



      Thanks.


      Naresh

        • 1. Re: IdentityManager.grantRole() not persisting to database
          zergspirit

          Might be that your data aren't persisted in the database and that you'd need to flush it somehow?

          • 2. Re: IdentityManager.grantRole() not persisting to database
            zergspirit

            Sorry for the double post, but I wasn't clear enough.


            I meant that your



            identityManager.createUser(username, password);



            Was maybe not persisted to your database when you're calling your



            identityManager.grantRole(username, role);



            So it can't add a role to a non existing user.


            • 3. Re: IdentityManager.grantRole() not persisting to database
              nbhatia.bhatian.comcast.net

              I doubt if that's the problem. Knowing Hibernate, it is very smart about managing the persistence context. If a record needs to be flushed because another operation needs it, it will flush automatically. Also the fact that user has been persisted I am really scratching my head about why grantRole() is not working. There may be something subtle in my code above that I am not seeing. Any other ideas?


              Thanks.
              Naresh

              • 4. Re: IdentityManager.grantRole() not persisting to database
                joblini

                Split your code so that each run as does only one operation. Perform a flush after each operation.

                • 5. Re: IdentityManager.grantRole() not persisting to database
                  nbhatia.bhatian.comcast.net

                  And that worked!!!! But why?


                  1) Why is the flush required? Don't remember doing this kind of ugly code with good old Hibernate.


                  2) Why is the exact same code in seam-space working (see below):


                     @End
                     public void uploadPicture() 
                     {  
                        member.setMemberSince(new Date());      
                        entityManager.persist(member);      
                        
                        new RunAsOperation() {
                           public void execute() {
                              identityManager.createUser(username, password);
                              identityManager.grantRole(username, "user");            
                           }         
                        }.addRole("admin")
                         .run();
                              
                        newAccount.setMember(member);
                        newAccount = entityManager.merge(newAccount);
                  
                        if (picture != null && picture.length > 0)
                        {
                           MemberImage img = new MemberImage();
                           img.setData(picture);
                           img.setMember(member);
                           img.setContentType(pictureContentType);
                           entityManager.persist(img);
                           member.setPicture(img);
                           
                           member = entityManager.merge(member);
                        }
                  

                  • 6. Re: IdentityManager.grantRole() not persisting to database
                    joblini

                    Flush mode is set to manual

                    • 7. Re: IdentityManager.grantRole() not persisting to database
                      meetoblivion

                      You could have also fixed this by changing your Set's flushmode like this:



                      @ManyToMany(targetEntity = Role.class,cascade=CascadeType.ALL)



                      by default, cascade is set to NONE.

                      • 8. Re: IdentityManager.grantRole() not persisting to database
                        nbhatia.bhatian.comcast.net

                        Thanks Ingo and John.


                        This flush mode stuff is pretty confusing. The only place the reference docs mention the manual flush mode is in section 9.3.2 - Using a Seam-managed Hibernate session. Section 9.3.1, which is JPA specific, does not mention it - but I am assuming that Seam uses manual flush mode in that case too. Anyway, looking at the example in section 9.3.2, I added the following property to my JPA persistence context - that did not help at all.


                        <property name="hibernate.transaction.flush_before_completion" value="true"/>
                        



                        John, I tried to add CascadeType.ALL as you suggested - makes no difference.


                        So at this point I have several questions:


                        1) If flush mode is indeed manual, is there a flush happening at least at the transaction commit time? I would assume so, because we know that user is being persisted.


                        2) Why is the flush mode not set to AUTO by default? At least that way when the second call happens, (identityManager.grantRole()), the session will be automatically flushed (because of the queries embedded in that call) and everything will work well. As you might have guessed, I don't like to call flush() explicitly - such a call does not belong in the service layer, it is a persistence concern and belongs to the persistence layer.


                        3) All this does not explain why seam-space is working without an explicit flush. Is the flush mode being changed somewhere? I tried to look for it in the source but did not find it anywhere.


                        4) Is there any way to log transaction boundries of JBoss JTA transactions? This will allow me to understand what Seam is doing in the context of a JTA transaction.


                        I will gladly accept partial answers or any other hints to understand this better.


                        Thanks.


                        Naresh

                        • 9. Re: IdentityManager.grantRole() not persisting to database
                          joblini

                          The Hibernate Entity Manager documentation has more information about this.



                          Except when you explicity flush(), there are absolutely no guarantees about when the entity manager executes the JDBC calls, only the order in which they are executed.

                          Configure log4j configuration to TRACE level for org.hibernate.  Comparing logs for SeamSpace with those of your application may reveal the reason for the different behaviour.


                          Finally, if may be interesting to call a method on the Entity Manager, getFlushMode()) to see what the current mode is.


                          PS  There is a query hint called org.hibernate.flushMode,perhaps this is being used in SeamSpace.


                          Please keep us posted as to what you discover!

                          • 10. Re: IdentityManager.grantRole() not persisting to database
                            nbhatia.bhatian.comcast.net

                            Ok, the mystery is solved! I was starting my conversation in pages.xml where I was explicitly setting the flush mode to manual:


                            <page view-id="/openaccount.xhtml">
                                <begin-conversation flush-mode="manual" join="true" />
                                ...
                            </page>
                            



                            I had copied this code from some example without knowing the implication of flush-mode=manual - what an expensive error! I was able to track down the problem by configuring the logging as suggested by you Ingo. It was puzzling that seam-space was setting the flush mode to auto whereas my application was setting it to manual. The fact that seam-space is doing NOTHING to set the flush mode to auto makes me believe that by default the flush mode is indeed auto. Mine was being set to manual accidently because of the flush-mode attribute above. The moment I took out that attribute, flush mode was being set to auto (as shown in JBoss logs) and everything started working as expected.


                            In my opinion, flush-mode=manual bleeds too much of the persistence concerns into the application layer, so I would tend to avoid it. I would like to hear other opinions on this subject. I do understand the comments in the reference manual that for long conversations, we may want explicit control over the flush, but if we run into situations like mine where insert to the database must happen so that a subsequent query behaves as expected within a conversation, then we may not be able to wait till the end of the conversation to flush.


                            BTW Ingo, EntityManager.getFlushMode() always returned a null. I think the reason is that EntityManager's flushMode attribute is different from Hibernate session's flushMode attribute - the two values do not reflect each other. In my logs while I was seeing Hibernate set the flush mode to manual, EntityManager was still reporting it as null.


                            Ingo, thanks for all your help on this issue and hope somebody else benefits from this conversation.


                            Naresh