1 2 Previous Next 15 Replies Latest reply: Apr 27, 2012 1:57 PM by meatwad RSS

    Synchronizing calls to an EJB method

    meatwad
      I have a method inside my EJB that I want to be synchronized. According to the EJB Specification synchronization techniques are not allowed inside of an EJB so the synchronization must occur in the caller. In my application the caller is a Servlet. I can synchronize the method inside my servlet that calls this EJB but this web application that is distributed across 2 servers. So how can I accomplish what I am trying to do?
        • 1. Re: Synchronizing calls to an EJB method
          gimbal2
          I would wonder why you need it. Its a Stateless EJB right?
          • 2. Re: Synchronizing calls to an EJB method
            meatwad
            Yeah and I've posted about this problem I'm having before and you've helped me out some. Here's the setup: 2 servers each hosting application that has a servlet and a stateless EJB. The EJB method updates the database by calling a stored procedure. Even though I have a lock at the top of this stored procedure requests are still running concurrently. I'm investigating this strange behavior but I'm also looking at alternative options like synchronizing the call.
            • 3. Re: Synchronizing calls to an EJB method
              gimbal2
              Meatwad wrote:
              Yeah and I've posted about this problem I'm having before and you've helped me out some.
              (better mention that the next time and post a link to the previous question to avoid confusion - I spam in so many questions that I can't keep track of who I've attempted to help, who I've had verbal combat with and who I've ignored ;) )

              I don't know what was said before, but my initial gut reaction to this is: make it so you don't need to synchronize anything and you don't need any locks. No matter how you slice it, you are dealing with a highly concurrent environment since you not only service this through the web, but its load balanced across two different webservers on top of that. So my initial realm of investigation would be: can said stored procedure be made "atomic" so there is no need for this kind of hard to support micromanagement; if not, I would rather replace it with something that is, perhaps even moving the solution to the Java side of things.
              • 4. Re: Synchronizing calls to an EJB method
                meatwad
                Don't worry all our previous communication has been nice :)

                As for atomicity in the stored procedure, that's what I've attempted to do. This procedure is massive and it calls so many other things not written by me that I don't have control over. But it is all a single transaction (no commits or rollbacks inside it). To make it atomic I could only think of creating a row-exclusive lock (this is an Oracle database) at the beginning of the procedure. The strange thing is that when I test this stored procedure by running simultaneous scripts through sql*plus it works perfectly: no concurrent access. But when my testers try through the web using different browsers at the same time it seems that the lock isn't working.
                • 5. Re: Synchronizing calls to an EJB method
                  gimbal2
                  I'll just bet that the "row-exclusive lock" you've created only works when using through sqlplus/plsql developer and not when invoking through JDBC. Usually the DBMS manages row and table locks itself based on the action performed.

                  I see where it is going, the unwieldly stored procedure is a thorn. A way around that would be to actually do manage the lock at the Java side of things, which would make me look into what tools the Java concurrency API has to offer.

                  http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/package-summary.html

                  I have used a Semaphore or a ReentrantLock with good success to restrict "access" for example.

                  http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/Semaphore.html

                  http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/ReentrantLock.html

                  However I must confess that my experience falls short because of your load balancing situation; I have no idea how to properly apply a locking mechanism when there are multiple VMs involved :( Yeah, defer responsibility of invoking the procedure to only one VM through a sort of webservice setup. But that seems like an over-engineered solution to a simple problem.
                  • 6. Re: Synchronizing calls to an EJB method
                    meatwad
                    I've kind of been thinking the same thing. From my understanding, though, using a web service doesn't guarantee there will only be one instance of the Remote class in existence at any one time like a Servlet. Do I have that right? Sounds like I'd still need to do the IPC thing.
                    • 7. Re: Synchronizing calls to an EJB method
                      EJP
                      Why do you need it to be synchronized? If it's a stateless EJB, LOCK TABLE and transactions are all you should need. In any case, synchronization and semaphores don't work across VMs; file locks would require a shared file system with reliable locking; and IPC would require that you know where all the other clustered container instances are.
                      • 8. Re: Synchronizing calls to an EJB method
                        meatwad
                        EJP wrote:
                        Why do you need it to be synchronized? If it's a stateless EJB, LOCK TABLE and transactions are all you should need. In any case, synchronization and semaphores don't work across VMs,; file locks would require a shared file system with reliable locking; and IPC would require that you know where all the other clustered container instances are.
                        I'm not sure why but the lock I am using is not working when executed from my EJB. When I execute it from sqlplus or from a test Java class it works. Synchronizing is not what I want to do and yes, I would have to limit it to 1 JVM on 1 server. But that may be what I have to do if I can't figure this out. Got any ideas? I'm using a "select for update" statement to do the locking.
                        • 9. Re: Synchronizing calls to an EJB method
                          EJP
                          There's no reason I can think of why SELECT FOR UPDATE shouldn't work the same in an EJB as in a test class, and if it doesn't I would expect it to fail completely rather than just not lock the rows concerned. Are you sure it isn't working? This is the line I would be investigating, not how to kludge up something around it in Java code.
                          • 10. Re: Synchronizing calls to an EJB method
                            meatwad
                            Could there be anything that releases this type of lock other than issuing a commit/rollback?
                            • 11. Re: Synchronizing calls to an EJB method
                              EJP
                              Closing the connection would do an implicit commit or rollback itself. Are you managing the connection and statement yourself? or are you using the EJB entity manager or Hibernate or something higher level.
                              • 12. Re: Synchronizing calls to an EJB method
                                jtahlborn
                                are you locking an existing row or a new row? are you using JPA or straight JDBC? any chance you could show some code, that would make it a lot easier to give suggestions?
                                • 13. Re: Synchronizing calls to an EJB method
                                  meatwad
                                  I'm locking a group of existing rows based on their account number using JDBC. It might be possible that a new row is being added after the lock is created and while another process is attempting this same lock. Would that invalidate my lock?

                                  Here's the Java code (sensitive/private variable names renamed)
                                      Connection con          = null;
                                      CallableStatement cstmt = null;
                                      
                                      try
                                      {
                                        InitialContext ic = new InitialContext();
                                        String resource   = (String) ic.lookup("java:comp/env/datasource");
                                        DataSource ds     = (DataSource) ic.lookup(resource);
                                        con               = ds.getConnection();
                                        
                                        // Turn off autocommit.
                                        con.setAutoCommit(false);
                                        
                                        // Pay off the account.
                                        cstmt = con.prepareCall("{ call myschema.pay_account(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) }");
                                        
                                        cstmt.setString(1, field1);
                                        cstmt.setString(2, field2);
                                        cstmt.setString(3, field3);
                                        cstmt.setString(4, field4);
                                        cstmt.setNull(5, Types.CHAR);
                                        cstmt.setString(6, field6);
                                        cstmt.setString(7, field7);
                                        cstmt.setString(8, field8d);
                                        cstmt.setBigDecimal(9, field9);
                                        cstmt.setBigDecimal(10, field10);
                                        cstmt.setString(11, field11);
                                        cstmt.setString(12, field12);
                                        cstmt.setString(13, field13);
                                        cstmt.setString(14, field14);
                                        cstmt.setString(15, field15);
                                        cstmt.setString(16, field16);
                                        cstmt.setString(17, field17);
                                        cstmt.setString(18, field18);
                                        cstmt.setString(19, field19);
                                        cstmt.execute();
                                        
                                        con.commit();
                                      }
                                      // If any exceptions were thrown from the stored procedure then rollback
                                      // all changes and re-throw the exception.
                                      catch (SQLException e)
                                      {
                                        con.rollback();
                                        
                                        throw e;
                                      }
                                      finally
                                      {
                                        if (cstmt != null)  { cstmt.close();  }
                                        if (con != null)    { con.close();    }
                                      }
                                  And here's the beginning of the stored procedure where the lock is (sensitive/private column names renamed). You'll notice there's an issue here on the last line of this SQL where I'm not uppercasing the last name passed in but the data that reproduces the error is all uppercase so that isn't the issue.
                                  select fee.balance bulk collect into fee_balances
                                  from
                                  myschema.charges join myschema.person
                                    on charges.pid = person.pid
                                  join myschema.fee
                                    on  charges.case_id = fee.case_id
                                    and charges.pid     = fee.pid
                                  where
                                  charges.charge_num           = in_charge_num
                                  and person.change_ind        is null
                                  and upper(person.last_name)  = in_lastname
                                  for update of fee.balance;
                                  • 14. Re: Synchronizing calls to an EJB method
                                    meatwad
                                    Ok guys I figured out the problem. It was a data issue. The SELECT FOR UPDATE SQL statement is exactly what is needed in this type of situation. So for about the 100th time I learned again to never make assumptions about someone else's logic.
                                    1 2 Previous Next