1

I have a EJB Timer 3.0, and this timer has a method that approximately runs for 6 hours per call. The Timer I have implemented is below:

@Stateless
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class BatchJobConsumerTimer implements BatchJobConsumerTimerLocal {

@Resource
TimerService timerService;

@EJB
ModelOperationsLocal modelOperations;

private static final long ONE_DAY_IN_MILLISECONDS = 86400000L;

private static final Logger LOG = Logger
        .getLogger(BatchJobConsumerTimer.class);

public BatchJobConsumerTimer() {
}

@Timeout
public void timeout(Timer timer) {
    LOG.info("Entering timeout method.");
    if(TimerRunningChecker.isWorking()){
        LOG.warn("A TIMER ALREADY IS RUNNING, SECOND TIMER WANTS TO ENTER TIMEOUT METHOD");
        return;
    } else {
        TimerRunningChecker.setWorking(true);
    }

    TimerConfiguration timerConfiguration = (TimerConfiguration) timer
            .getInfo();
    if (timerConfiguration != null) {
        Calendar calendar = new GregorianCalendar();
        if (isWorkDay(timerConfiguration.getSelectedDays())) {
            Date startTime = new Date(System.currentTimeMillis());
            Boolean and = timerConfiguration.getAnd();
            Integer numberOfJobs = timerConfiguration.getNumberOfJobs();

            int jobConsumed = 0;

            INFINITE: while (true) {
                List<Job> findWaitingJobs = modelOperations
                        .getLatestUploadedWaitingBatchJobs((numberOfJobs == -1 || numberOfJobs > 100) ? 100
                                : numberOfJobs);
                if (findWaitingJobs != null) {
                    if(findWaitingJobs.size() == 0){
                        LOG.warn("There is no jobs to consume.");
                        break INFINITE;
                    }
                    for (Job job : findWaitingJobs) {
                        ++jobConsumed;// means successful and unsuccessful
                        LOG.info("Job Number: " + (jobConsumed));
                        if (and) {
                            if (numberOfJobs != -1) {

                                if (jobConsumed > numberOfJobs
                                        && isEqualsAfterEndSchedule(
                                                calendar,
                                                timerConfiguration)) {
                                    LOG.info("jobConsumed > numberOfJobs && isEqualsAfterEndSchedule(calendar) is true");
                                    break INFINITE;
                                }
                            } else {
                                if (isEqualsAfterEndSchedule(calendar,
                                        timerConfiguration)) {
                                    LOG.info("numberOfJobs = infinite && isEqualsAfterEndSchedule(calendar) is true");
                                    break INFINITE;
                                }
                            }
                        } else {
                            if (numberOfJobs != -1) {
                                if (jobConsumed > numberOfJobs
                                        || isEqualsAfterEndSchedule(
                                                calendar,
                                                timerConfiguration)) {
                                    LOG.info("jobConsumed > numberOfJobs || isEqualsAfterEndSchedule(calendar) is true");
                                    break INFINITE;
                                }
                            } else {
                                if (isEqualsAfterEndSchedule(calendar,
                                        timerConfiguration)) {
                                    LOG.info("numberOfJobs = infinite || isEqualsAfterEndSchedule(calendar) is true");
                                    break INFINITE;
                                }
                            }
                        }

                        try {
                            LOG.info(job.getServiceNumber()
                                    + " hizmet numarali is tuketilicek.");

                            modelOperations.update(job);
                        } catch (Exception e) {
                            LOG.error(e, e);
                        }
                    }
                } else {
                    // liste bos don method'dan
                    break INFINITE;
                }
            }

            //send email
            Date endTime = new Date(System.currentTimeMillis());
            try {
                modelOperations.sendBatchOperationMail(startTime, endTime);
            } catch (Exception e) {
                LOG.error(e, e);
            }
        } else {
            LOG.warn("Today is not the working day.");
        }
    } else {
        LOG.warn("TimerConfiguration is not set.");
    }

    if(!TimerRunningChecker.isWorking()){
        LOG.warn("A TIMER WANTS CHANGE THE STATE OF WORKING TO NOT WORKING STATE, BUT IT IS ALREADY SET NOT WORKING STATE");
        return;
    } else {
        TimerRunningChecker.setWorking(false);
    }
    LOG.info("Exiting timout method.");
}

@Override
public void createTimer(TimerConfiguration timerConfiguration)
        throws Exception {
    // stop the other timers.
    Collection<Timer> timers = timerService.getTimers();
    for (Timer timer : timers) {
        timer.cancel();
    }

    timerService.createTimer(
            getRemainingTimeToFirstExpiration(timerConfiguration),
            ONE_DAY_IN_MILLISECONDS, timerConfiguration);
}

private long getRemainingTimeToFirstExpiration(
        TimerConfiguration timerConfiguration) {
    Calendar now = new GregorianCalendar();
    Calendar nextStart = new GregorianCalendar();
    nextStart.set(Calendar.HOUR_OF_DAY, timerConfiguration.getBeginHour());
    nextStart.set(Calendar.MINUTE, timerConfiguration.getBeginMinute());
    nextStart.set(Calendar.SECOND, timerConfiguration.getBeginSecond());

    long diff = nextStart.getTimeInMillis() - now.getTimeInMillis();
    return (diff < 0 ? diff + ONE_DAY_IN_MILLISECONDS : diff);
}

private boolean isEqualsAfterEndSchedule(Calendar calendar,
        TimerConfiguration timerConfiguration) {
    calendar.setTimeInMillis(System.currentTimeMillis());
    int currentHour = calendar.get(Calendar.HOUR_OF_DAY);
    int currentMinute = calendar.get(Calendar.MINUTE);
    int currentSecond = calendar.get(Calendar.SECOND);

    if (currentHour > timerConfiguration.getEndHour()) {
        return true;
    } else if (currentHour == timerConfiguration.getEndHour()) {
        if (currentMinute > timerConfiguration.getEndMinute()) {
            return true;
        } else if (currentMinute == timerConfiguration.getEndMinute()) {
            if (currentSecond >= timerConfiguration.getEndSecond()) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    } else {
        return false;
    }
}

private boolean isWorkDay(String selectedDays) {
    if(selectedDays == null){
        LOG.error("selectedDays is NULL!");
        return false;
    }

    LOG.info("selectedDays: " + selectedDays);
    if (selectedDays.equals("?")) {
        return false;
    }

    if (selectedDays.equals("*")) {
        return true;
    }

    Calendar now = GregorianCalendar.getInstance();

    int i = now.get(Calendar.DAY_OF_WEEK);
    LOG.info("now.get(Calendar.DAY_OF_WEEK): " + i);
    if (selectedDays.contains("" + i)) {
        return true;
    } else {
        return false;
    }
}
}  

The application server, to which I have to deploy this app is Weblogic 10.3.4, and transaction timeout configuration has been adjusted to 30 seconds.

In the lights of the things that I mentioned above, the timeout method automatically rollbacks after 30 seconds passed from the start of execution and the weblogic retries it continuously. I have tried to remove this transaction by annotating the class, method but no success. How can I make this method transactionless? Thank for your interest.

By the way, is balusC's recommendation for the question considerable to getting over from this problem?

Community
  • 1
  • 1
st.
  • 166
  • 6
  • 24

1 Answers1

1

In this case, use BeanManagement Transaction. Otherwise, the bean Timerservice itself will start a transaction to satisfy the Transaction on Default Persistent Store (which is in every case an File Store on the WLS) that uses an XA interface. This workaround helps on Weblogic Application Server but also on other application servers.

@Stateless 

@Local(TimerBusinessService.class)
@TransactionManagement(TransactionManagementType.BEAN)
public class TimerBusinessServiceImpl implements TimerBusinessService {

    @Resource
    private TimerService timerService;

    @EJB
    private NonTxService nonTxService;

    @Override
    public void doStartTimer() throws EjpdBusinessException {
        long everyDay = 86400000;...
        timerService.createTimer(0, everyDay, ...);
    }           

    @Timeout
    public void periodicalTimerMethode(Timer timer) throws EjpdBusinessException {
        try {
            nonTxService.proceedAction ();
        } catch (…Exception ex) {
            throw new RuntimeException(“..”, ex);
        }
    }
}
amlan
  • 11
  • 1