package com.jdroid.android.usecase; import com.jdroid.android.application.AbstractApplication; import com.jdroid.android.usecase.listener.UseCaseListener; import com.jdroid.java.collections.Lists; import com.jdroid.java.date.DateUtils; import com.jdroid.java.exception.AbstractException; import com.jdroid.java.exception.UnexpectedException; import com.jdroid.java.utils.LoggerUtils; import org.slf4j.Logger; import java.io.Serializable; import java.util.List; public abstract class AbstractUseCase implements Runnable, Serializable { private static final long serialVersionUID = 3732327346852606739L; private final static Logger LOGGER = LoggerUtils.getLogger(AbstractUseCase.class); public enum UseCaseStatus { NOT_INVOKED, IN_PROGRESS, FINISHED_SUCCESSFUL, FINISHED_FAILED; } private volatile List<UseCaseListener> listeners = Lists.newArrayList(); private volatile UseCaseStatus useCaseStatus = UseCaseStatus.NOT_INVOKED; private AbstractException abstractException; private volatile Boolean notified = false; private Long executionTime = 0L; private int exceptionPriorityLevel = AbstractException.NORMAL_PRIORITY; /** * Executes the use case. */ @Override public final void run() { LOGGER.debug("Executing " + getClass().getSimpleName()); markAsInProgress(); for (UseCaseListener listener : listeners) { notifyUseCaseStart(listener); } try { LOGGER.debug("Started use case " + getClass().getSimpleName()); long startTime = DateUtils.nowMillis(); doExecute(); executionTime = DateUtils.nowMillis() - startTime; LOGGER.debug("Finished use case " + getClass().getSimpleName() + ". Execution time: " + DateUtils.formatDuration(executionTime)); markAsSuccessful(); for (UseCaseListener listener : listeners) { notifyFinishedUseCase(listener); } AbstractApplication.get().getAnalyticsSender().trackUseCaseTiming(getClass(), executionTime); } catch (RuntimeException e) { AbstractException abstractException; if (e instanceof AbstractException) { abstractException = (AbstractException)e; } else { abstractException = new UnexpectedException(e); } abstractException.setPriorityLevel(exceptionPriorityLevel); markAsFailed(abstractException); logHandledException(abstractException); for (UseCaseListener listener : listeners) { notifyFailedUseCase(abstractException, listener); } } finally { if (!listeners.isEmpty()) { markAsNotified(); } } } protected void logHandledException(AbstractException abstractException) { AbstractApplication.get().getExceptionHandler().logHandledException(abstractException); } /** * @return the executionTime */ public Long getExecutionTime() { return executionTime; } /** * Override this method with the use case functionality to be executed */ protected abstract void doExecute(); /** * Notifies the listener that the use case has started. <br/> * You can override this method for a custom notification when your listeners may be listening to multiple use cases * at a time. * * @param listener The listener to notify. */ protected void notifyUseCaseStart(UseCaseListener listener) { listener.onStartUseCase(); } /** * Notifies the listener that the use case has finished successfully. <br/> * You can override this method for a custom notification when your listeners may be listening to multiple use cases * at a time. * * @param listener The listener to notify. */ protected void notifyFinishedUseCase(UseCaseListener listener) { listener.onFinishUseCase(); } /** * Notifies the listener that the use case has failed to execute. <br/> * You can override this method for a custom notification when your listeners may be listening to multiple use cases * at a time. * * @param listener The listener to notify. */ protected void notifyFailedUseCase(AbstractException e, UseCaseListener listener) { listener.onFinishFailedUseCase(e); } /** * @return the listeners */ protected List<UseCaseListener> getListeners() { return listeners; } /** * @param listener the listener to add */ public void addListener(UseCaseListener listener) { if (listener != null && !listeners.contains(listener)) { this.listeners.add(listener); } } /** * @param listener the listener to remove */ public void removeListener(UseCaseListener listener) { if (listener != null) { this.listeners.remove(listener); } } public Boolean isNotInvoked() { return UseCaseStatus.NOT_INVOKED.equals(useCaseStatus); } public Boolean isInProgress() { return UseCaseStatus.IN_PROGRESS.equals(useCaseStatus); } public Boolean isFinishSuccessful() { return UseCaseStatus.FINISHED_SUCCESSFUL.equals(useCaseStatus); } public Boolean isFinishFailed() { return UseCaseStatus.FINISHED_FAILED.equals(useCaseStatus); } /** * @return If the use case has finished, regardless of the success or failure of its execution. */ public Boolean isFinish() { return isFinishFailed() || isFinishSuccessful(); } public void markAsNotified() { notified = true; } public void markAsNotNotified() { notified = false; } public void markAsNotInvoked() { this.useCaseStatus = UseCaseStatus.NOT_INVOKED; } protected void markAsInProgress() { notified = false; abstractException = null; this.useCaseStatus = UseCaseStatus.IN_PROGRESS; } protected void markAsSuccessful() { this.useCaseStatus = UseCaseStatus.FINISHED_SUCCESSFUL; } protected void markAsFailed(AbstractException abstractException) { this.useCaseStatus = UseCaseStatus.FINISHED_FAILED; this.abstractException = abstractException; } public AbstractException getAbstractException() { return abstractException; } public Boolean isNotified() { return notified; } public void setExceptionPriorityLevel(int exceptionPriorityLevel) { this.exceptionPriorityLevel = exceptionPriorityLevel; } }