Hi,

Here is the solution that with Gerhard's help is working well.

Firstly using the StartConversationEvent does not work as it creates a logical cycle (observing a event it produces).

Create a conversation scoped transaction wrapper class which starts the transaction on @PostConstruct and commits the transaction on @PreDestroy Then in a listener class which observes @BeforeFacesRequest inject the transaction wrapper class. This will start the transaction if a conversation/transaction is not active. I used a threadvar to tell if the conversation/transaction was created in this thread, if so do nothing otherwise resume the transaction.

In a method that observes @AfterFacesRequest suspend the transaction.

I created a dedicated conversation group for the conversation/transaction scoped wrapper class. On any action that requires the transaction to be committed I then call windowContext.closeConversationGroup(TransactionConversationGroup.class);

Thanks
Pieter

On 03/08/2011 10:46, Gerhard Petracek wrote:
hi pieter,

since it's quite different from the classical approach and it depends on the
concrete usage, i would suggest that we talk about it in our irc channel
(afterwards we post the solution).

regards,
gerhard

http://www.irian.at

Your JSF powerhouse -
JSF Consulting, Development and
Courses in English and German

Professional Support for Apache MyFaces



2011/8/3 Gerhard Petracek<[email protected]>

hi pieter,

ok - in this case that won't work (that's a logical cycle).
i thought more about a producer which creates a dependent scoped
transaction. however, as i see that won't help you in this special case.
imo something like a lazy transaction should work. i'll think about it and
create a demo.

regards,
gerhard

http://www.irian.at

Your JSF powerhouse -
JSF Consulting, Development and
Courses in English and German

Professional Support for Apache MyFaces



2011/8/3 Pieter Martin<[email protected]>

Hi,

The code below does have a producer for the transaction. But it threw an
exception.
I changed the code slightly now but am still getting the same exception.


public class ConversationListener {

    @ApplicationScopedDb
    @Inject
    NakedGraph db;

    @ConversationScoped
    @Produces
    public Transaction getTransaction() {
        db.setTransactionMode(Mode.**MANUAL);
        db.startTransaction();
        return db.getTransaction();
    }

    public void onStartConversation(@Observes StartConversationEvent event,
Transaction t) {
        System.out.println(t);

    }

    public void onCloseConversation(@Observes CloseConversationEvent event)
{
        db.stopTransaction(Conclusion.**SUCCESS);
    }

}

The StartConversationEvent is fired but the transaction parameter can not
be resolved. Getting the following exception


Caused by: java.lang.NullPointerException
    at org.apache.myfaces.extensions.**cdi.core.impl.scope.**conversation.
**ConversationContextAdapter.**get(**ConversationContextAdapter.**
java:100)
    at org.apache.webbeans.context.**CustomContextImpl.get(**
CustomContextImpl.java:48)
    at org.apache.webbeans.context.**CustomPassivatingContextImpl.**get(**
CustomPassivatingContextImpl.**java:47)
    at org.apache.webbeans.intercept.**NormalScopedBeanInterceptorHan**
dler.getContextualInstance(**NormalScopedBeanInterceptorHan**
dler.java:127)
    at org.apache.webbeans.intercept.**NormalScopedBeanInterceptorHan**
dler.invoke(**NormalScopedBeanInterceptorHan**dler.java:95)
    at org.javassist.tmp.java.lang.**Object_$$_javassist_26.**
toString(Object_$$_javassist_**26.java)
    at java.lang.String.valueOf(**String.java:2838)
    at java.io.PrintStream.println(**PrintStream.java:788)
    at org.nakeduml.environment.**ConversationListener.**
onStartConversation(**ConversationListener.java:38)

Not sure why the producer method can not be called. Is the producer method
incorrect, does it need a qualifier?

Thanks
Pieter


On 02/08/2011 21:19, Gerhard Petracek wrote:

hi pieter,

the concept of outjection is deprecated.

you could create a producer method for the transaction.
if you need the transaction in an observer, you just have to add it as
additional parameter and the produced transaction will be injected into
the
observer method by cdi.

regards,
gerhard

http://www.irian.at

Your JSF powerhouse -
JSF Consulting, Development and
Courses in English and German

Professional Support for Apache MyFaces




2011/8/2 Pieter Martin<[email protected]**>

  Hi,
I have the following requirement / problem.

I would like to implement a long running transaction that is in sync
with a
conversation. I.e. I want to start a transaction when the conversation
starts and commit the transaction when the conversation ends.

So the transaction will be suspended and resumed in between requests.

So far I am able to observe the StartConversationEvent and start a
transaction. But I do not know how then to make sure that the
transaction
will be available in the conversation context.

The code is as follows.

@RequestScoped
public class ConversationListener {

    @ApplicationScopedDb
    @Inject
    NakedGraph db;
    Transaction transaction;

    @ConversationScoped
    @Produces
    public Transaction getTransaction() {
        return this.transaction;
    }

    public void onStartConversation(@Observes StartConversationEvent
event)
{
        db.startTransaction();
        transaction = db.getTransaction();
        // Force producer to be called, to plonk the transaction in the
        // conversation
        // Like seam 2 outjection
        Transaction t = getInstance(Transaction.class)****;
        //This throws a NullPointerException
        t.toString();
    }

    public void onCloseConversation(@Observes CloseConversationEvent
event)
{
        //The faces listener would have made sure the correct transaction
is
associated with this thread
        db.stopTransaction(Conclusion.****SUCCESS);
    }

    public<T>   T getInstance(Class<T>   type, Annotation... qualifiers) {
        BeanManager beanManager = getBeanManager();
        Bean<T>   bean = (Bean<T>) beanManager.resolve(****
beanManager.getBeans(type,
qualifiers));
        CreationalContext<?>   ctx = beanManager.**
createCreationalContext(bean);
        return (T) beanManager.getReference(bean, type, ctx);
    }

    private BeanManager getBeanManager() {
        return BeanManagerProvider.****getInstance().getBeanManager()**
**;
    }

}

The above code does not work, it throws the following exception

Caused by: java.lang.NullPointerException
    at org.apache.myfaces.extensions.****cdi.core.impl.scope.****
conversation.*
*ConversationContextAdapter.****get(****ConversationContextAdapter.****
java:100)
    at org.apache.webbeans.context.****CustomContextImpl.get(**
CustomContextImpl.java:48)
    at org.apache.webbeans.context.****CustomPassivatingContextImpl.***
*get(**
CustomPassivatingContextImpl.****java:47)
    at org.apache.webbeans.intercept.******
NormalScopedBeanInterceptorHan****
dler.getContextualInstance(****NormalScopedBeanInterceptorHan****
dler.java:127)
    at org.apache.webbeans.intercept.******
NormalScopedBeanInterceptorHan****
dler.invoke(****NormalScopedBeanInterceptorHan****dler.java:95)

    at org.javassist.tmp.java.lang.****Object_$$_javassist_26.**
toString(Object_$$_javassist_****26.java)

If this were to work the idea is then to suspend and resume the code in
faces listeners

@RequestScoped
public class FacesListener {
    @ApplicationScopedDb
    @Inject
    NakedGraph db;
    @Inject
    Logger logger;

    public void beforeFacesRequest(@Observes @BeforeFacesRequest
FacesContext ctx) {
        logger.fine("****beforeFacesRequest, setting db on thread");
        GraphDb.setDb(db);
        //Need to check here somehow if a conversation has been started
or
not.
        Transaction t = CdiEnvironment.getInstance().****
getComponent(Transaction.****class);
        db.resume(t);
    }

    public void afterFacesRequest(@Observes @AfterFacesRequest
FacesContext
ctx) {
        logger.fine("****afterFacesRequest, suspend transaction and
removing
db from thread");
        Transaction t = db.suspend();
        //Do not need to anything with this transaction as it is already
in
the conversation context
        GraphDb.remove();
    }

}

Hope it makes sense.
Is there a (better) way to do this?

Thanks
Pieter






Reply via email to