/*
* Copyright 2012 LinkedIn, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.linkedin.parseq;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import org.slf4j.ILoggerFactory;
import org.slf4j.LoggerFactory;
import com.linkedin.parseq.internal.ArgumentUtil;
import com.linkedin.parseq.internal.CachedLoggerFactory;
import com.linkedin.parseq.internal.LIFOBiPriorityQueue;
import com.linkedin.parseq.internal.PlanCompletionListener;
import com.linkedin.parseq.internal.PlanDeactivationListener;
/**
* A configurable builder that makes {@link Engine}s.
* <p>
* Minimum required configuration:
* <ul>
* <li>taskExecutor</li>
* <li>timerScheduler</li>
* </ul>
*
* @author Chris Pettitt (cpettitt@linkedin.com)
* @author Jaroslaw Odzga (jodzga@linkedin.com)
*/
public class EngineBuilder {
private Executor _taskExecutor;
private DelayedExecutor _timerScheduler;
private ILoggerFactory _loggerFactory;
private PlanDeactivationListener _planDeactivationListener;
private PlanCompletionListener _planCompletionListener;
private TaskQueueFactory _taskQueueFactory;
private Map<String, Object> _properties = new HashMap<String, Object>();
/**
* Sets plan activity listener for the engine. The listener will be notified
* when plan becomes deactivated.
* Plan becomes deactivated when there are no tasks that can be executed e.g.
* asynchronous operations are in progress and subsequent tasks depend on their results.
* <p>
* For given plan id deactivation listener is called sequentially
* with respect to tasks belonging to that plan. For arbitrary plan ids methods on
* {@code PlanActivityListener} can be called in parallel.
*
* @param planDeactivationListener the listener that will be notified when plan
* becomes deactivated
*/
public void setPlanDeactivationListener(PlanDeactivationListener planDeactivationListener) {
ArgumentUtil.requireNotNull(planDeactivationListener, "planDeactivationListener");
_planDeactivationListener = planDeactivationListener;
// TODO: should have returned the builder here to make it chainable.
// Need to fix in the next major version release.
}
public EngineBuilder setPlanCompletionListener(PlanCompletionListener planCompletionListener) {
ArgumentUtil.requireNotNull(planCompletionListener, "planCompletionListener");
_planCompletionListener = planCompletionListener;
return this;
}
public EngineBuilder setTaskQueueFactory(TaskQueueFactory taskQueueFactory) {
ArgumentUtil.requireNotNull(taskQueueFactory, "taskQueueFactory");
_taskQueueFactory = taskQueueFactory;
return this;
}
/**
* Sets the task executor for the engine.
* <p>
* The lifecycle of the executor is not managed by the engine.
*
* @param taskExecutor the executor to use for the engine
* @return this builder
*/
public EngineBuilder setTaskExecutor(final Executor taskExecutor) {
ArgumentUtil.requireNotNull(taskExecutor, "taskExecutor");
_taskExecutor = taskExecutor;
return this;
}
/**
* Sets the timer scheduler for the engine.
* <p>
* The lifecycle of the scheduler is not managed by the engine.
*
* @param timerScheduler the scheduler to use for the engine
* @return this builder
*/
public EngineBuilder setTimerScheduler(final DelayedExecutor timerScheduler) {
ArgumentUtil.requireNotNull(timerScheduler, "timerScheduler");
_timerScheduler = timerScheduler;
return this;
}
/**
* Sets the timer scheduler for the engine.
* <p>
* The lifecycle of the scheduler is not managed by the engine.
*
* @param timerScheduler the scheduler to use for the engine
* @return this builder
*/
public EngineBuilder setTimerScheduler(final ScheduledExecutorService timerScheduler) {
ArgumentUtil.requireNotNull(timerScheduler, "timerScheduler");
setTimerScheduler(adaptTimerScheduler(timerScheduler));
return this;
}
/**
* Sets the logger factory that will be used by the engine. By default
* the engine will use whatever log binding SLF4J has detected.
*
* @param loggerFactory the logger factory to used by the engine.
* @return this builder
*/
public EngineBuilder setLoggerFactory(final ILoggerFactory loggerFactory) {
ArgumentUtil.requireNotNull(loggerFactory, "loggerFactory");
_loggerFactory = loggerFactory;
return this;
}
/**
* Sets an engine related property on the engine.
* That property can then be accessed by tasks via the Context.
*
* @param key
* @param value
* @return this builder
*/
public EngineBuilder setEngineProperty(String key, Object value) {
_properties.put(key, value);
return this;
}
/**
* Checks that the require configuration has been set and then constructs
* and returns a new {@link Engine}.
*
* @return a new {@link Engine} using the configuration in this builder.
* @throws IllegalStateException if the configuration in this builder is invalid.
*/
public Engine build() {
if (_taskExecutor == null) {
throw new IllegalStateException("Task executor is required to create an Engine, but it is not set");
}
if (_timerScheduler == null) {
throw new IllegalStateException("Timer scheduler is required to create an Engine, but it is not set");
}
return new Engine(_taskExecutor, new IndirectDelayedExecutor(_timerScheduler),
_loggerFactory != null ? _loggerFactory : new CachedLoggerFactory(LoggerFactory.getILoggerFactory()),
_properties, _planDeactivationListener != null ? _planDeactivationListener : planContext -> {},
_planCompletionListener != null ? _planCompletionListener : planContext -> {},
_taskQueueFactory);
}
/**
* Helper method to convert a {@link ScheduledExecutorService} to a simpler
* {@link DelayedExecutor}.
*
* @param timerScheduler the scheduler to convert
* @return the converted scheduler
*/
private static DelayedExecutor adaptTimerScheduler(final ScheduledExecutorService timerScheduler) {
return new DelayedExecutorAdapter(timerScheduler);
}
// package private. used in test only.
PlanCompletionListener getPlanCompletionListener() {
return _planCompletionListener;
}
}