Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » Eclipse Scout » How to participate in service transactions?
How to participate in service transactions? [message #1220310] Wed, 11 December 2013 02:34 Go to next message
Patrick Mackinlay is currently offline Patrick MackinlayFriend
Messages: 10
Registered: December 2013
Junior Member
I understand that all Scout service operations run within a transaction - that is, the framework begins a transaction at the entry of a service operation, and presumably either commit or roll it back at the end of the operation, depending upon any errors that might have been flagged during processing.

I also understand that the SqlService derivative implementations somehow "hook into" this in order to perform the underlying database operations necessary to implement the behaviour (e.g. executing the underlying database "commit" or "rollback" statements, etc.).

In my case, I'm wanting to implement a new service based on the Neo4j graph database engine (www.neo4j.org), in embedded mode. Neo4j is not an SQL-based database, and I don't believe it would make much sense to try to implement it as a derivative of the SqlService in Scout. I do, however, need to be able to hook into the Scout service transaction mechanism, so that all service operations are automatically wrapped in an underlying Neo4j transaction.

I'm quite lost trying to understand how to go about this - there's nothing obvious I can see to implement on my service to alert Scout that it needs its own transaction implementation logic. Where do I implement the needed transaction calls, and how do I ensure those calls are integrated with Scout's transaction management system?

PS: I'm really unsure, but perhaps if a Neo4j embedded database instance were directly registered as an OSGi service (rather than dealing with it as a library), some kind of JTA or XA magic might automatically perform the above, or is that just wishful thinking?
Re: How to participate in service transactions? [message #1220329 is a reply to message #1220310] Wed, 11 December 2013 06:57 Go to previous messageGo to next message
Stephan Leicht Vogt is currently offline Stephan Leicht VogtFriend
Messages: 104
Registered: July 2015
Senior Member
Hi Patrick

That is a nice idea. Participating in a Scout Transaction is not so difficult. But as you realized the SqlService is not so easy to look how it works. I just had to work through it to get the Scout JAX-WS implementation participating in a transaction.

In your Neo4j Service I presume you have a "getConnection" method or something similar. There you can put following code:

    ITransaction reg = ThreadContext.getTransaction();
    if (reg == null) {
      throw new ProcessingException("no ITransaction available, use ServerJob to run transaction");
    }
    P_Neo4jTransactionMember member = (P_Neo4jTransactionMember) reg.getMember(getTransactionMemberId());
    if (member == null) {
      _Neo4jConnection_ conn;
      try {
        conn = _create or lease or get the connection_
        member = new P_Neo4jTransactionMember(getTransactionMemberId(), conn);
        reg.registerMember(member);
        // this is the start of the transaction
        execBeginTransaction();
      }
      catch (ProcessingException e) {
        throw e;
      }
      catch (Throwable e) {
        throw new ProcessingException("getTransaction", e);
      }
    }
    return member.getConnection();


The transactionMemberId can be set like following snippet in an initConfig method or more sophisticated if needed:
    String tid = getConfiguredTransactionMemberId();
    if (tid == null) {
      tid = getClass().getSimpleName() + "." + "transaction";
    }
    setTransactionMemberId(tid);


The class P_Neo4jTransactionMember is a private class which extends AbstractTransactionMember and could look like:

  private final class P_Neo4jTransactionMember extends AbstractTransactionMember {
    private final _Neo4jConnection_ m_conn;
    /** true during completion phase (commit/rollback) */
    private boolean m_finishingTransaction;

    public P_Neo4jTransactionMember(String transactionMemberId, _Neo4jConnection_ conn) {
      super(transactionMemberId);
      m_conn = conn;
    }

    public _Neo4jConnection_ getConnection() {
      return m_conn;
    }

    protected void setFinishingTransaction(boolean finishingTransaction) {
      m_finishingTransaction = finishingTransaction;
    }

    @Override
    public boolean needsCommit() {
      return true;
    }

    @Override
    public boolean commitPhase1() {
      return execPreCommit(m_conn);
    }

    @Override
    public void commitPhase2() {
      try {
        // this is the end of the transaction
        try {
          setFinishingTransaction(true);
          execEndTransaction(false);
        }
        finally {
          setFinishingTransaction(false);
        }
        execCommit(m_conn);
      }
      catch (Exception e) {
        LOG.error(null, e);
      }
    }

    @Override
    public void rollback() {
      try {
        // this is the end of the transaction
        try {
          setFinishingTransaction(true);
          execEndTransaction(false);
        }
        finally {
          setFinishingTransaction(false);
        }
        execRollback(m_conn);
      }
      catch (Exception e) {
        if (!ThreadContext.getTransaction().isCancelled()) {
          LOG.error(null, e);
        }
      }
    }

    @Override
    public void release() {
      releaseConnection(m_conn);
    }
  }// end private class


The missing exec..-methods in the P_Neo4jTransactionMember lead to execMethods in your Neo4jService where they do their job of committing/rollbacking etc.

I hope with this you get yourself an nice TransactionMember. If you need any further pointer do not hesitate to ask.

Greetings
Stephan
Re: How to participate in service transactions? [message #1220387 is a reply to message #1220329] Wed, 11 December 2013 12:49 Go to previous messageGo to next message
Patrick Mackinlay is currently offline Patrick MackinlayFriend
Messages: 10
Registered: December 2013
Junior Member
Stephan, thanks very much for your excellent help.

If I've understood the implementation correctly, it's less "tricky" than I thought. It basically depends on every user of the service to call a particular method (in your example, the getConnection() method), which is responsible for installing the necessary transaction helper object into the active thread's transaction container. This helper object contains the callbacks that the system will call when it wants to commit or roll back the current transaction. Is my understanding approximately right?

Given this, I'm pretty sure I can implement something that works, but I'm uncertain about the mapping to the underlying transaction mechanism. What is the difference between commitPhase1() and commitPhase2()? The Neo4j API works like this:

  Transaction tx = db.beginTx(); // starts a transaction

  tx.success(); // marks a transaction as successful
  tx.failure(); // marks a transaction as failed

  tx.close(); // ends the transaction


Should I map these as:


  • commitPhase1() -> ?
  • commitPhase2() -> tx.success()
  • rollback() -> tx.failure()
  • release() -> tx.close()


Appreciate your feedback.
Re: How to participate in service transactions? [message #1220441 is a reply to message #1220387] Wed, 11 December 2013 18:33 Go to previous message
Stephan Leicht Vogt is currently offline Stephan Leicht VogtFriend
Messages: 104
Registered: July 2015
Senior Member
Patrick, you are absolutely on the right track. Your understanding of the call flow is correct. The mapping is also right. The commitPhase1 is often unused - also in the SQL Service. Here you can just return true.

Greetings
Stephan
Previous Topic:Clean server shutdown
Next Topic:Groovy support?
Goto Forum:
  


Current Time: Tue Apr 23 09:06:29 GMT 2024

Powered by FUDForum. Page generated in 0.03921 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top