简体   繁体   中英

Handling a timeout in EJB3 without using threads

I have the following situation. I have a job that:

  • May time out after a given amount of time, and if so occurs needs to throw an exception
  • If it does not time out, will return a result
  • If this job returns a result, it must be returned as quickly as possible, because performance is very much an issue. Asynchronous solutions are hence off the table, and naturally tying up the system by hammering isn't an option either.
  • Lastly, the system has to conform to the EJB standard, so AFAIK using ordinary threads is not an option, as this is strictly forbidden.

Our current solution uses a thread that will throw an exception after having existed for a certain amount of time without being interrupted by an external process, but as this clearly breaks the EJB standard, we're trying to solve it with some other means.

Any ideas?

Edited to add: Naturally, a job which has timed out needs to be removed (or interrupted) as well.

Edited to add 2: This issue doesn't seem to have any solution, because detecting a deadlock seems to be mostly impossible sticking to pure EJB3 standards. Since Enno Shioji's comments below reflect this, I'm setting his suggestion as the correct answer.

With Bean Managed Transaction, the timeout for the specific transaction can be specified by using UserTransaction interface.

Modify the timeout value that is associated with transactions started by the current thread with the begin method.

void setTransactionTimeout(int seconds) throws SystemException
  • Transaction will timeout after specified seconds & may not get propagated further. If exception is not thrown implicitly, then can throw it explicitly based on the result.
  • Will return a result on successful completion within specified time.
  • Can use it with stateless session beans so there may not be a performance issue.
  • Its EJB standard so that will not be an issue to implement.

With little-bit work around, it should work fine in the given scenario.

Edit: Also can use server specific properties to manage transaction timeout.

JBoss : At either at class or method level annotation @TransactionTimeout(100) can be applied.

Weblogic : Specifying the parameters in weblogic-ejb-jar.xml

<transaction-descriptor>
     <trans-timeout-seconds>100</trans-timeout-seconds> 
</transaction-descriptor>

GlassFish : Using the optional cmt-timeout-in-seconds element in sun-ejb-jar.xml

This is more like a request for clarification, but it's too long to fit as a comment..

I'm not sure how you are doing it right now, since from what you wrote, just using the request processing thread seems to be the way to go. Like this:

//Some webservice method (synchronous)
public Result process(Blah blah){
    try{
        return getResult(TimeUnit.SECONDS, 10);
    }catch(InterruptedException e){
        //No result within 10 seconds!
        throw new ServiceUnavailableException("blah");
    }
}

I'm not sure why you are creating threads at all. If you are forced to use threads because the getResult method doesn't timeout at all, you would have a thread leak. If it timeouts after a longer time and thus you want to "shortcut" your reply to the user, that would be the only case I'd consider using a thread like I imagine how you are using it. This could result in Threads piling up under load and I'd strive to avoid such situation.

Maybe you can post some code and let us know why you are creating in your service at all?

Also, what's your client interface? Sounds like it's a synchronous webservice or something?


In that case, if I were you I would use a HashedWheelTimer as a singleton... this mechanism should work great with your requirement ( here is an implementation ). However, this unfortunately seem to conflict with the ban on threading AND the ban on singleton in the EJB spec. In reality though there really isn't a problem if you would do this. See this discussion for example. We have also used the singleton pattern in our EJB app. which used JBoss. However, if this isn't a viable choice then I might look at isolating the processing in its own JVM by defining a new web service (and deploy it in a web-container or something), and call that service from the EJB app. This would however obviously incur performance hit and now you would have another whole new app.

Stick the process and it's timeout thread in to a class annotated with @WebService, put that class in to a WAR, then invoke the WebService from your EJB.

WARs don't have the same limitations or live under the same contract that EJBs do, so they can safely run threads.

Yes, I consider this a "hack", but it meets the letter of the requirements, and it's portable.

You can create threads using the commonj WorkManager. There are implementations built into WebSphere and Weblogic as they proposed the standard, but you can also find implementations for other appservers as well.

Basically, the WorkManager allows you to create managed threads inside the container, much like using an Executor in regular Java. Your only other alternative would be to use MDB's, but that would be a 'heavier' solution.

Since I don't know your actual platform, you will have to google commonj with your platform yourself 8-)

Here is a non IBM or Oracle solution.

Note: This is not an actual standard, but it is widely available for different platforms and should suit your purposes nicely.

For EJBs, there is a concept of "Container Managed Transactions". By specifying @TransactionAttribute on your bean, or specific method, the container will create a transaction when ever the method(s) are invoked. If the execution of the code takes longer than the transaction threshold, the container will throw an exception. If the call finishes under the transaction threshold, it will return as usual. You can catch the exception in your calling code and handle it appropriately.

For more on container managed transactions, check out: http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/Transaction3.html and http://download.oracle.com/javaee/5/tutorial/doc/bncij.html

You could use @TimeOut . Something like:

@Stateless
public class TimedBean {

  @Resource
  private TimerService timerService;

  static private AtomicInteger counter = new AtomicInteger(0);
  static private Map<Integer, AtomicBoolean> canIRunStore = new ...;

  public void doSomething() {
    Integer myId = counter.getAndIncrement();
    AtomicBoolean canIRun = new AtomicBoolean(true);
    canIRunStore.put(myId, canIRun);

    timerService.createTimer(1000, 0, myId);

    while (canIRun.get() /* && some other condition */) {
      // do my work ... untill timeout ... 
    }
  }

  @Timeout
  @PermitAll
  public void timeout(Timer timer) {
    Integer expiredId = (Integer) timer.getInfo();
    AtomicBoolean canHeRun = canIRunStore.get(expiredId);
    canIRunStore.remove(expiredId);
    canHeRun.set(false);
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM