package rocks.inspectit.server.diagnosis.engine;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import rocks.inspectit.server.diagnosis.engine.session.ISessionCallback;
import rocks.inspectit.server.diagnosis.engine.session.Session;
import rocks.inspectit.server.diagnosis.engine.session.SessionPool;
/**
* The default implementation of {@link IDiagnosisEngine}.
*
* @param <I>
* The type of input to be analyzed.
* @param <R>
* The result type.
* @author Claudio Waldvogel, Alexander Wert
*/
public class DiagnosisEngine<I, R> implements IDiagnosisEngine<I> {
/**
* The slf4j logger.
*/
private static Logger log = LoggerFactory.getLogger(DiagnosisEngine.class);
/**
* The {@link DiagnosisEngineConfiguration} which configured this engine instance.
*/
private final DiagnosisEngineConfiguration<I, R> configuration;
/**
* The {@link SessionPool} providing {@link Session} instances to this engine instance. The
* configuration of the pool depends on the {@link DiagnosisEngineConfiguration}.
*/
private final SessionPool<I, R> sessionPool;
/**
* The {@link ListeningExecutorService} to dispatch the diagnosis sessions.
*/
private final ListeningExecutorService sessionExecutor;
/**
* Default constructor to create a new DiagnosisEngine instance.
*
* @param configuration
* The {@link DiagnosisEngineConfiguration} to be used. Must not be null.
* @throws DiagnosisEngineException
* If the provided configuration is not valid.
*/
public DiagnosisEngine(DiagnosisEngineConfiguration<I, R> configuration) throws DiagnosisEngineException {
this.configuration = checkNotNull(configuration, "The configuration must not be null.");
this.configuration.validate();
this.sessionPool = new SessionPool<>(configuration);
// Wrap in listing executor
this.sessionExecutor = MoreExecutors.listeningDecorator(configuration.getExecutorService());
}
// -------------------------------------------------------------
// Interface Implementation: IDiagnosisEngine
// -------------------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
public void analyze(I input) throws DiagnosisEngineException {
analyze(input, Session.EMPTY_SESSION_VARIABLES);
}
/**
* {@inheritDoc}
*
* @throws DiagnosisEngineException
*/
@Override
public void analyze(I input, Map<String, ?> variables) throws DiagnosisEngineException {
if (isShutdown()) {
throw new DiagnosisEngineException("Analysis failed! Diagnosis Engine has been shut down.");
}
final Session<I, R> session;
try {
session = sessionPool.borrowObject(input, variables);
} catch (Exception e) {
throw new DiagnosisEngineException("Failed to borrow Object from SessionPool.", e);
}
// Kick of the session execution
Futures.addCallback(sessionExecutor.submit(session), new FutureCallback<R>() {
@Override
public void onSuccess(R result) {
returnSession();
for (ISessionCallback<R> handler : configuration.getSessionCallbacks()) {
handler.onSuccess(result);
}
}
@Override
public void onFailure(Throwable t) {
returnSession();
for (ISessionCallback<R> handler : configuration.getSessionCallbacks()) {
handler.onFailure(t);
}
}
private void returnSession() {
try {
sessionPool.returnObject(session);
} catch (Exception e) {
for (ISessionCallback<R> handler : configuration.getSessionCallbacks()) {
handler.onFailure(new DiagnosisEngineException("Failed to return Session to ObjectPool.", e));
}
}
}
});
}
/**
* {@inheritDoc}
*/
@Override
public void shutdown(boolean awaitShutdown) {
// 1. Shutdown the ExecutorService
if (!sessionExecutor.isShutdown()) {
sessionExecutor.shutdown();
if (awaitShutdown) {
try {
if (!sessionExecutor.awaitTermination(configuration.getShutdownTimeout(), TimeUnit.SECONDS)) {
log.error("DiagnosisEngine executor did not shutdown within: {} seconds.", configuration.getShutdownTimeout());
}
} catch (InterruptedException e) {
log.error("InterruptedException occured during termination of the DiagnosisEngine executor.", e);
}
}
}
// 2. Shutdown the session pool
try {
sessionPool.close();
} catch (Exception e) {
log.error("Failed closing DiagnosisEngine session pool.", e);
}
}
/**
* Checks whether the {@link DiagnosisEngine} is shut down.
*
* @return Returns true, if engine is shut down.
*/
private boolean isShutdown() {
return sessionExecutor.isShutdown() || sessionPool.isClosed();
}
}