package rocks.inspectit.server.diagnosis.engine;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.commons.collections.CollectionUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import rocks.inspectit.server.diagnosis.engine.rule.store.DefaultRuleOutputStorage;
import rocks.inspectit.server.diagnosis.engine.rule.store.IRuleOutputStorage;
import rocks.inspectit.server.diagnosis.engine.session.ISessionCallback;
import rocks.inspectit.server.diagnosis.engine.session.ISessionResultCollector;
import rocks.inspectit.server.diagnosis.engine.session.Session;
/**
* Class to configure a {@link DiagnosisEngine}.
*
* @param <I>
* The type of the input to the engine.
* @param <R>
* The expected return type. Ensure that <R> matches the return type of the
* {@link ISessionResultCollector}.
* @author Claudio Waldvogel, Alexander Wert
*/
public class DiagnosisEngineConfiguration<I, R> {
/**
* The amount of threads which will run {@link Session}s.
*/
private int numSessionWorkers = 2;
/**
* Specifies how long the the engine waits to shutdown properly. <b>Timeout is specified in
* seconds.</b>
*/
private int shutdownTimeout = 2;
/**
* The set of classes implementing diagnosis rules.
*/
private final Set<Class<?>> ruleClasses = Sets.newHashSet();
/**
* The {@link ExecutorService} to be used. If not provided, a default will be created
*/
private ExecutorService executorService;
/**
* The {@link IRuleOutputStorage} implementation to be used.
*/
private Class<? extends IRuleOutputStorage> storageClass = DefaultRuleOutputStorage.class;
/**
* The {@link ISessionCallback}s to be invoked if a session complete. If not provided, it is
* still possible to provided results from rules directly somehow. <b>It is strongly recommended
* to use callback mechanism instead of work around in rule implementations</b>
*/
private final List<ISessionCallback<R>> callbacks = Lists.newArrayList();
/**
* The {@link ISessionResultCollector} to be used. Must not be null.
*/
private ISessionResultCollector<I, R> resultCollector;
/**
* Sets {@link #numSessionWorkers}.
*
* @param numSessionWorkers
* New value for {@link #numRuleWorkers}
* @return DiagnosisEngineConfiguration itself
*/
public DiagnosisEngineConfiguration<I, R> setNumSessionWorkers(int numSessionWorkers) {
checkArgument(numSessionWorkers > 0, "numSessionWorkers must be at least 1.");
this.numSessionWorkers = numSessionWorkers;
return this;
}
/**
* Sets {@link #executorService}.
*
* @param executorService
* New value for {@link #executorService}
* @return DiagnosisEngineConfiguration itself
*/
public DiagnosisEngineConfiguration<I, R> setExecutorService(ExecutorService executorService) {
this.executorService = checkNotNull(executorService, "The ExecutorService must not be null.");
return this;
}
/**
* Adds new Classes to {@link #ruleClasses}.
*
* @param ruleClasses
* New values for {@link #ruleClasses}
* @return DiagnosisEngineConfiguration itself
*/
public DiagnosisEngineConfiguration<I, R> addRuleClasses(Collection<Class<?>> ruleClasses) {
checkNotNull(ruleClasses, "Set of rule classes must not be null.");
for (Class<?> ruleClass : ruleClasses) {
addRuleClass(ruleClass);
}
return this;
}
/**
* Adds new Classes to {@link #ruleClasses}.
*
* @param ruleClasses
* New values for {@link #ruleClasses}
* @return DiagnosisEngineConfiguration itself
*/
public DiagnosisEngineConfiguration<I, R> addRuleClasses(Class<?>... ruleClasses) {
addRuleClasses(Arrays.asList(checkNotNull(ruleClasses, "Array of rule classes must not be null.")));
return this;
}
/**
* Adds a new Class to {@link #ruleClasses}.
*
* @param ruleClass
* A new value for {@link #ruleClasses}
* @return DiagnosisEngineConfiguration itself
*/
public DiagnosisEngineConfiguration<I, R> addRuleClass(Class<?> ruleClass) {
this.ruleClasses.add(checkNotNull(ruleClass, "Rule class must not be null."));
return this;
}
/**
* Sets {@link #shutdownTimeout}.
*
* @param shutdownTimeout
* New value for {@link #shutdownTimeout} in seconds.
* @return DiagnosisEngineConfiguration itself
*/
public DiagnosisEngineConfiguration<I, R> setShutdownTimeout(int shutdownTimeout) {
checkArgument(shutdownTimeout > 0, "shutdownTimeout must be at least 1.");
this.shutdownTimeout = shutdownTimeout;
return this;
}
/**
* Sets {@link #storageClass}.
*
* @param storageClass
* New value for {@link #storageClass}
* @return DiagnosisEngineConfiguration itself
*/
public DiagnosisEngineConfiguration<I, R> setStorageClass(Class<? extends IRuleOutputStorage> storageClass) {
this.storageClass = checkNotNull(storageClass, "Storage classes must not be null.");
return this;
}
/**
* Sets {@link #resultCollector}.
*
* @param resultCollector
* New value for {@link #resultCollector}
* @return DiagnosisEngineConfiguration itself
*/
public DiagnosisEngineConfiguration<I, R> setResultCollector(ISessionResultCollector<I, R> resultCollector) {
this.resultCollector = checkNotNull(resultCollector, "Result collector must not be null.");
return this;
}
/**
* Adds a new callback to {@link #callbacks}.
*
* @param callback
* A new callback for {@link #callbacks}
* @return DiagnosisEngineConfiguration itself
*/
public DiagnosisEngineConfiguration<I, R> addSessionCallback(ISessionCallback<R> callback) {
this.callbacks.add(checkNotNull(callback, "Session callback must not be null."));
return this;
}
/**
* Adds a list of new {@link ISessionCallback}s {@link #callbacks}.
*
* @param sessionCallbacks
* New entries for {@link #callbacks}
* @return DiagnosisEngineConfiguration itself
*/
public DiagnosisEngineConfiguration<I, R> addSessionCallbacks(List<ISessionCallback<R>> sessionCallbacks) {
for (ISessionCallback<R> callback : sessionCallbacks) {
addSessionCallback(callback);
}
return this;
}
/**
* Validates the {@link DiagnosisEngineConfiguration}.
*
* @throws DiagnosisEngineException
* If any of the configuration elements is invalid or is missing.
*/
public void validate() throws DiagnosisEngineException {
try {
checkArgument(numSessionWorkers > 0, "numSessionWorkers must be at least 1.");
checkArgument(shutdownTimeout > 0, "shutdownTimeout must be at least 1.");
checkArgument(CollectionUtils.isNotEmpty(ruleClasses), "At least one rule class must be specified.");
checkNotNull(resultCollector, "Result collector must not be null.");
if (executorService == null) {
executorService = Executors.newFixedThreadPool(getNumSessionWorkers());
}
} catch (IllegalArgumentException e) {
throw new DiagnosisEngineException("Invalid Diagnosis Engine configuration!", e);
}
}
// -------------------------------------------------------------
// Accessors
// -------------------------------------------------------------
/**
* Gets {@link #numSessionWorkers}.
*
* @return {@link #numSessionWorkers}
*/
public int getNumSessionWorkers() {
return numSessionWorkers;
}
/**
* Gets {@link #shutdownTimeout}.
*
* @return {@link #shutdownTimeout}
*/
public int getShutdownTimeout() {
return shutdownTimeout;
}
/**
* Gets {@link #ruleClasses}.
*
* @return {@link #ruleClasses}
*/
public Set<Class<?>> getRuleClasses() {
return ruleClasses;
}
/**
* Gets {@link #executorService}.
*
* @return {@link #executorService}
*/
public ExecutorService getExecutorService() {
return executorService;
}
/**
* Gets {@link #storageClass}.
*
* @return {@link #storageClass}
*/
public Class<? extends IRuleOutputStorage> getStorageClass() {
return storageClass;
}
/**
* Gets {@link #resultCollector}.
*
* @return {@link #resultCollector}
*/
public ISessionResultCollector<I, R> getResultCollector() {
return resultCollector;
}
/**
* Gets {@link #callbacks}.
*
* @return {@link #callbacks}. Is never <code>null</code>.
*/
public List<ISessionCallback<R>> getSessionCallbacks() {
return callbacks;
}
}