views:

241

answers:

3

I've started to write some new JBoss timed service that was meant to use some existing seam components. But it seems that I can't access these components because of non-existing contexts. Is it possible to use them other than in the typical situation with JSF?

A little snippet to demonstrate what I want to do...

@Service
public class MyService extends DefaultTimedService implements TimedObject, DefaultServiceInterface {
    @Timeout
    public void ejbTimeout(Timer timer) {
        MyInterface loader = (MyInterface) Component.getInstance(MyInterface.SEAM_NAME, true);
        // throws no context!
    }
}

That throws the following exception for example:

java.lang.IllegalStateException: No application context active
    at org.jboss.seam.Component.forName(Component.java:1945)
    at org.jboss.seam.Component.getInstance(Component.java:2005)
+1  A: 

What scope have you defined for the component? Probably application context as it says so in the error.

...

So I poked around the source and found out that contexts are stored in a class named Contexts. All contexts seems to be thread specific, because they are encapsulated in ThreadLocal-instances. That means that has to specified for thread of the timed service...

The question remains though: how do create a context for a specific thread.

Aleksi
The one I want to use has SESSION Scope defined... I could switch to CONVERSATION if it helps.
Daniel Bleisteiner
+1  A: 

Can't you inject the loader instance, instead of of locating it with that static call? I'm not quite familiar with Seam, but perhaps (in the class body):

@In private MyInterface loader;

and then, in your method, just use the loader.

As it seems, Seam has the application /statelesss scopes, which seems the appropriate one in your case:

@Scope(ScopeType.APPLICATION)

or

@Scope(ScopeType.STATELESS)

Try one of those - since your class doesn't seem to need any information from the session/request, it's more appropriate not to use a web-related scope.

So define MyService and MyInterface in one of the above scopes, and try both injection and your lookup method.

Check the Seam tutorial on contexts and concurrency

This thread seems helpful.

And from this thread it seems there is an @Asynchronous annotation, that you might use.

Bozho
No, I can't. Injection has the same problem as with trying to access the component as described above... the context is missing. Injection only works in specific types and is normally performed during the request/response lifecycle.
Daniel Bleisteiner
injection works depending on scope. the request/response lifecycle is about converstation/session/request scopes. There are others.
Bozho
But the JBoss service isn't touched by the injection inspectors... it initializes during server startup. Is it possible to solve this?- ahh... just recognized your edit... will give it a try.
Daniel Bleisteiner
Simply adding @Scope to the service doesn't have any impact. The exceptions remains the same. So I added @Name too and now Seam recognizes the service as a component. But the running service (in its own thread) doesn't seem to live in any Seam context... the exception remains the same here too.
Daniel Bleisteiner
I added some links to my answer, check them
Bozho
I will... give me two days - we are very busy at the moment.
Daniel Bleisteiner
sure. just mention when you try it, what was the outcome
Bozho
+7  A: 

There is one way that is a little dirty and there are many developers that would not use such a hack ever but it will solve your problem:

import org.jboss.seam.contexts.Lifecycle;

@Service
public class MyService extends DefaultTimedService implements TimedObject, DefaultServiceInterface {
    @Timeout
    public void ejbTimeout(Timer timer) {
        Lifecycle.beginCall();

        MyInterface loader = (MyInterface) Component.getInstance(MyInterface.SEAM_NAME, true);
        // will not throw no context!
        // also the Component.getInstance(MyInterface.SEAM_NAME, true,true); call
        // is another way you could inject that component. 

        Lifecycle.endCall();
    }
}

I have used it in one project where I couldn't find anything else that worked. If anybody has another solution I am looking forward to seeing it here :).

Bobby
Thanks too... I'll give it a try... looks dirty but promissing :)
Daniel Bleisteiner
The beginCall() did the basics... well. Now I have to solve some other pre-conditions to get my components to work. Thanks!
Daniel Bleisteiner
One more hint here... beginCall() does not start a transaction and all EntityManager operations fail quietly. If using beginCall() und endCall() it is mandatory to handle the transaction too! By using Transaction.instance().begin() and Transaction.instance().commit() I could get it to actually do something real.
Daniel Bleisteiner