Help

Controls

PermLinkWikiLink

Built with Seam

You can find the full source code for this website in the Seam package in the directory /examples/wiki. It is licensed under the LGPL.

Forum: Seam Users Forum ListTopic List
13. Feb 2008, 01:35 CET | Link

Well, we seem to be between forums, so I'm going to risk it and repost my question to the new forum. (original thread was here: http://www.jboss.com/index.html?module=bb&op=viewtopic&t=129546)


Bump...

Can someone point me at some documentation, or an example of how to

1) mark a Seam POJO's action method to not be transactional (is it sufficient to add @Transactional(NEVER) annotation?

2) and then how to start and commit a manual user transaction in a way compatible with an application otherwise using Seam managed transactions?

Thanks!

11 Replies:
13. Feb 2008, 04:17 CET | Link
Steve Tynor wrote on Feb 13, 2008 01:35 AM:
Can someone point me at some documentation, or an example of how to 1) mark a Seam POJO's action method to not be transactional (is it sufficient to add @Transactional(NEVER) annotation?

Well, a Seam POJO that isn't marked @Transactional will never start a transaction upon method entry. But if you want any method in your bean to throw an exception if entered when a transaction is active, then yes, @Transactional(NEVER) will do the trick.

2) and then how to start and commit a manual user transaction in a way compatible with an application otherwise using Seam managed transactions?

You can do:

import org.jboss.seam.transaction.Transaction;
UserTransaction tx = Transaction.instance();
if (!tx.isActiveOrMarkedRollback())
   tx.begin();

or if you're calling code you wish to be transactional, this is easier:

import org.jboss.seam.util.Work;
new Work() {
  protected Object work() throws Exception {
     //do something here
  }
}.workInTransaction();

 
Clint Popetz
http://42lines.net
Scalable Web Application Development
13. Feb 2008, 14:34 CET | Link
Well, a Seam POJO that isn't marked @Transactional will never start a transaction upon method entry. But if you want any method in your bean to throw an exception if entered when a transaction is active, then yes, @Transactional(NEVER) will do the trick.

I guess I'm sloppy in my description -- it's not a Seam POJO - it's a (non-EJB) Seam Component's action method -- and it's most definitely being called inside a transaction from my JSF EL (it's behavior of committing the transaction upon return of the action method is what prompted this thread on the old forum). How I can stop the Seam container transaction from being started automagically for just this one action method?

Thanks for the Work() example -- that looks very clean (should I have been able to find that documented somehwere? Perhaps I'm just not looking in the right places?)

13. Feb 2008, 16:00 CET | Link
Steve Tynor wrote on Feb 13, 2008 02:34 PM:
Well, a Seam POJO that isn't marked @Transactional will never start a transaction upon method entry. But if you want any method in your bean to throw an exception if entered when a transaction is active, then yes, @Transactional(NEVER) will do the trick.
I guess I'm sloppy in my description -- it's not a Seam POJO - it's a (non-EJB) Seam Component's action method

To be clear, what I meant by Seam POJO was any java class with a @Name attribute that's not an EJB session or entity bean. So it sounds like we're talking about a method in such a bean.

-- and it's most definitely being called inside a transaction from my JSF EL (it's behavior of committing the transaction upon return of the action method is what prompted this thread on the old forum). How I can stop the Seam container transaction from being started automagically for just this one action method?

Entering/exiting a non-EJB Seam component's method won't do anything with respect to transactions unless you've marked the method or the class as @Transactional. If it's marked @Transactional, it won't cause an existing transaction to commit. The seam interceptor for transactional components says does this method need a transaction, and if it does it starts one and then commits it after the method returns, or rolls it back if an exception is thrown. (It's more complicated than that, but that's the main idea.)

Thanks for the Work() example -- that looks very clean (should I have been able to find that documented somehwere? Perhaps I'm just not looking in the right places?)

Not as far as I know; it's in the source. So Work() may be considered by Seam to be an internal class and thus its API or semantics may be subject to change. But it's a nice paradigm, pretty small in code size, and if they rip it out, I'll just clone it an put it in my own tree :P

 
Clint Popetz
http://42lines.net
Scalable Web Application Development
13. Feb 2008, 16:04 CET | Link

Well... Marking the action method @Transactional(NEVER) appears to be not enough to tell the Seam transaction interceptor to not create a transaction for my action method...


@Transactional(TransactionPropagationType.NEVER)
public void generate() throws Exception {        
...

I get the following:


Caused by: java.lang.IllegalStateException: Transaction active on call to NEVER method
        at org.jboss.seam.annotations.TransactionPropagationType.isNewTransactio
nRequired(TransactionPropagationType.java:38)

How do I tell Seam to not transact my action?

13. Feb 2008, 16:17 CET | Link
Steve Tynor wrote on Feb 13, 2008 04:04 PM:
Well... Marking the action method @Transactional(NEVER) appears to be not enough to tell the Seam transaction interceptor to not create a transaction for my action method...

@Transactional(TransactionPropagationType.NEVER)
public void generate() throws Exception {        
...
I get the following:

Caused by: java.lang.IllegalStateException: Transaction active on call to NEVER method
        at org.jboss.seam.annotations.TransactionPropagationType.isNewTransactio
nRequired(TransactionPropagationType.java:38)
How do I tell Seam to not transact my action?

Ok, I misunderstood you at first. You want your action, which is invoked as part of the jsf lifecycle, to not have a transaction started at all. There's no way to do that on a per-action basis (because seam starts the transaction before it could know what actions are going to be invoked...it starts the transaction as part of participating in the jsf lifecycle as a phase listener, and not directly before invoking your action method.) But you can turn off seam-managed transactions by putting this in components.xml:

<core:init transaction-management-enabled="false"/>

Of course, there are real disadvantages to that...see the manual.

 
Clint Popetz
http://42lines.net
Scalable Web Application Development
13. Feb 2008, 16:41 CET | Link

Thanks Clint. Yes - you've diagnosed my problem correctly.

Turning off global transactions is not an acceptable solution for my application - as you say, it has significant disadvantages and would require significant rewrite of my application.

But I really do need to manage my own transaction for this one case (I need to ensure that all data is committed to the database so I can invoke a BIRT report which uses that data before I return from the JSF-invoked action method).

Perhaps instead of preventing the Seam transaction interceptor from creating a transaction, I can just run my updates inside a nested transaction for which I can commit before the outer transaction does. Is that possible?

Thanks, Steve

Clint Popetz wrote on Feb 13, 2008 04:17 PM:
Ok, I misunderstood you at first. You want your action, which is invoked as part of the jsf lifecycle, to not have a transaction started at all. There's no way to do that on a per-action basis (because seam starts the transaction before it could know what actions are going to be invoked...it starts the transaction as part of participating in the jsf lifecycle as a phase listener, and not directly before invoking your action method.) But you can turn off seam-managed transactions by putting this in components.xml: <core:init transaction-management-enabled="false"/> Of course, there are real disadvantages to that...see the manual.
13. Feb 2008, 18:20 CET | Link

FWIW, I think I've solved my problem by using the following snippet to commit, then begin another transaction -- this seems to work. Can someone confirm that this is the proper and clean way to do commit a Seam managed transaction started by the transaction interceptor before returning from a JSF invoked action method?


Transaction.instance().commit();
Transaction.instance().begin();
Steve Tynor wrote on Feb 13, 2008 04:41 PM:
... But I really do need to manage my own transaction for this one case (I need to ensure that all data is committed to the database so I can invoke a BIRT report which uses that data before I return from the JSF-invoked action method). ...
13. Feb 2008, 23:45 CET | Link
Steve Tynor wrote on Feb 13, 2008 04:41 PM:
But I really do need to manage my own transaction for this one case (I need to ensure that all data is committed to the database so I can invoke a BIRT report which uses that data before I return from the JSF-invoked action method). Perhaps instead of preventing the Seam transaction interceptor from creating a transaction, I can just run my updates inside a nested transaction for which I can commit before the outer transaction does. Is that possible?

First, to be clear, the seam transaction interceptor isn't creating the transaction...if it was, you could just annotate your bean to not use transactions. It's the seam JSF PhaseListener that's creating the transaction.

I've had no end of woes with EJB REQUIRES_NEW (nested transactions) under jboss and hibernate. So if you go there, go with god ;)

Why do you need to commit before you can invoke a BIRT report...if you flush, and the data is there, any operations will see it without a commit.

 
Clint Popetz
http://42lines.net
Scalable Web Application Development
14. Feb 2008, 00:00 CET | Link
Steve Tynor wrote on Feb 13, 2008 06:20 PM:
FWIW, I think I've solved my problem by using the following snippet to commit, then begin another transaction -- this seems to work. Can someone confirm that this is the proper and clean way to do commit a Seam managed transaction started by the transaction interceptor before returning from a JSF invoked action method?

Transaction.instance().commit();
Transaction.instance().begin();

That will probably work, because Seam won't commit the transaction you committed when it sees it's already committed. But IMHO that's a fragile approach...and if it were me I'd search for a different one. Committing a transaction you didn't begin just sounds like a bad idea.

 
Clint Popetz
http://42lines.net
Scalable Web Application Development
14. Feb 2008, 00:02 CET | Link

Clint,

Thanks for clarifying where the transaction is getting started.

Before starting this whole thread about committing transactions, we had simply assumed that the following would work:


// insert some data into the database
entityManager.flush();
// run BIRT

But in fact, BIRT does NOT see the data after that flush. Nor can the mysql command line client. The data is not visible outside the entityManager until after the transaction commits (when my action method returns).

Thanks to your earlier hints, I poked around enough to find the Transaction.instance().commit() and Transaction.instance().begin() which I've been able to use to prematurely commit the transaction and create a new one so that the PhaseListener doesnt complain:


// insert some data into the database
entityManager.flush();
Transaction.instance().commit();
Transaction.instance().begin();
// run BIRT

This works, but I have no idea if this is considered right and proper. If not, what should I do?

Clint Popetz wrote on Feb 13, 2008 11:45 PM:
First, to be clear, the seam transaction interceptor isn't creating the transaction...if it was, you could just annotate your bean to not use transactions. It's the seam JSF PhaseListener that's creating the transaction. I've had no end of woes with EJB REQUIRES_NEW (nested transactions) under jboss and hibernate. So if you go there, go with god ;) Why do you need to commit before you can invoke a BIRT report...if you flush, and the data is there, any operations will see it without a commit.
14. Feb 2008, 01:57 CET | Link
Steve Tynor wrote on Feb 14, 2008 12:02 AM:
Before starting this whole thread about committing transactions, we had simply assumed that the following would work:

// insert some data into the database
entityManager.flush();
// run BIRT
But in fact, BIRT does NOT see the data after that flush. Nor can the mysql command line client.

I can understand the command line not seeing the data, because if you're at READ COMMITTED then it's not visible outside the transaction. Whether BIRT sees it depends on whether BIRT is using your hibernate session, and therefore the same jdbc connection. If BIRT is an external tool that won't let you specify the jdbc connection to use, you're stuck.

The data is not visible outside the entityManager until after the transaction commits (when my action method returns). Thanks to your earlier hints, I poked around enough to find the Transaction.instance().commit() and Transaction.instance().begin() which I've been able to use to prematurely commit the transaction and create a new one so that the PhaseListener doesnt complain:

// insert some data into the database
entityManager.flush();
Transaction.instance().commit();
Transaction.instance().begin();
// run BIRT
This works, but I have no idea if this is considered right and proper. If not, what should I do?

I think that if you can't feed your BIRT code a jdbc connection, I'd break the action up into two actions, one which commits the data, and one which runs birt, and redirect between the two. But if that's not feasible, your existing solution isn't wrong really, just a little unclean in my opinion. But we've all written unclean code before :P

 
Clint Popetz
http://42lines.net
Scalable Web Application Development