/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 gobblin.runtime.instance;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.AbstractIdleService;
import gobblin.broker.gobblin_scopes.GobblinScopeTypes;
import gobblin.broker.iface.SharedResourcesBroker;
import gobblin.instrumented.Instrumented;
import gobblin.metrics.GobblinMetrics;
import gobblin.metrics.MetricContext;
import gobblin.metrics.Tag;
import gobblin.runtime.JobState.RunningState;
import gobblin.runtime.api.Configurable;
import gobblin.runtime.api.GobblinInstanceDriver;
import gobblin.runtime.api.GobblinInstanceLauncher.ConfigAccessor;
import gobblin.runtime.api.JobCatalog;
import gobblin.runtime.api.JobExecutionDriver;
import gobblin.runtime.api.JobExecutionLauncher;
import gobblin.runtime.api.JobExecutionState;
import gobblin.runtime.api.JobLifecycleListener;
import gobblin.runtime.api.JobSpec;
import gobblin.runtime.api.JobSpecMonitorFactory;
import gobblin.runtime.api.JobSpecScheduler;
import gobblin.runtime.api.MutableJobCatalog;
import gobblin.runtime.job_spec.ResolvedJobSpec;
import gobblin.runtime.std.DefaultJobCatalogListenerImpl;
import gobblin.runtime.std.DefaultJobExecutionStateListenerImpl;
import gobblin.runtime.std.JobLifecycleListenersList;
import gobblin.util.ExecutorsUtils;
/**
* A default implementation of {@link GobblinInstanceDriver}. It accepts already instantiated
* {@link JobCatalog}, {@link JobSpecMonitorFactory}, {@link JobSpecScheduler},
* {@link JobExecutionLauncher}. It is responsibility of the caller to manage those (e.g. start,
* shutdown, etc.)
*
*/
public class DefaultGobblinInstanceDriverImpl extends AbstractIdleService
implements GobblinInstanceDriver {
protected final Logger _log;
protected final String _instanceName;
protected final Configurable _sysConfig;
protected final JobCatalog _jobCatalog;
protected final JobSpecScheduler _jobScheduler;
protected final JobExecutionLauncher _jobLauncher;
protected final ConfigAccessor _instanceCfg;
protected final JobLifecycleListenersList _callbacksDispatcher;
private final boolean _instrumentationEnabled;
protected final MetricContext _metricCtx;
protected JobSpecListener _jobSpecListener;
private final StandardMetrics _metrics;
private final SharedResourcesBroker<GobblinScopeTypes> _instanceBroker;
public DefaultGobblinInstanceDriverImpl(String instanceName,
Configurable sysConfig, JobCatalog jobCatalog,
JobSpecScheduler jobScheduler,
JobExecutionLauncher jobLauncher,
Optional<MetricContext> baseMetricContext,
Optional<Logger> log,
SharedResourcesBroker<GobblinScopeTypes> instanceBroker) {
Preconditions.checkNotNull(jobCatalog);
Preconditions.checkNotNull(jobScheduler);
Preconditions.checkNotNull(jobLauncher);
Preconditions.checkNotNull(sysConfig);
_instanceName = instanceName;
_log = log.or(LoggerFactory.getLogger(getClass()));
_metricCtx = baseMetricContext.or(constructMetricContext(sysConfig, _log));
_instrumentationEnabled = null != _metricCtx && GobblinMetrics.isEnabled(sysConfig.getConfig());
_jobCatalog = jobCatalog;
_jobScheduler = jobScheduler;
_jobLauncher = jobLauncher;
_sysConfig = sysConfig;
_instanceCfg = ConfigAccessor.createFromGlobalConfig(_sysConfig.getConfig());
_callbacksDispatcher = new JobLifecycleListenersList(_jobCatalog, _jobScheduler, _log);
_instanceBroker = instanceBroker;
_metrics = new StandardMetrics(this);
}
private MetricContext constructMetricContext(Configurable sysConfig, Logger log) {
gobblin.configuration.State tmpState = new gobblin.configuration.State(sysConfig.getConfigAsProperties());
return GobblinMetrics.isEnabled(sysConfig.getConfig()) ?
Instrumented.getMetricContext(tmpState, getClass())
: null;
}
/** {@inheritDoc} */
@Override public JobCatalog getJobCatalog() {
return _jobCatalog;
}
/** {@inheritDoc} */
@Override public MutableJobCatalog getMutableJobCatalog() {
return (MutableJobCatalog)_jobCatalog;
}
/** {@inheritDoc} */
@Override public JobSpecScheduler getJobScheduler() {
return _jobScheduler;
}
/** {@inheritDoc} */
@Override public JobExecutionLauncher getJobLauncher() {
return _jobLauncher;
}
/** {@inheritDoc} */
@Override public Configurable getSysConfig() {
return _sysConfig;
}
/** {@inheritDoc} */
@Override public SharedResourcesBroker<GobblinScopeTypes> getInstanceBroker() {
return _instanceBroker;
}
/** {@inheritDoc} */
@Override public Logger getLog() {
return _log;
}
@Override protected void startUp() throws Exception {
getLog().info("Default driver: starting ...");
_jobSpecListener = new JobSpecListener();
_jobCatalog.addListener(_jobSpecListener);
getLog().info("Default driver: started.");
}
@Override protected void shutDown() throws Exception {
getLog().info("Default driver: shuttind down ...");
if (null != _jobSpecListener) {
_jobCatalog.removeListener(_jobSpecListener);
}
_callbacksDispatcher.close();
getLog().info("Default driver: shut down.");
}
/** Keeps track of a job execution */
class JobStateTracker extends DefaultJobExecutionStateListenerImpl {
public JobStateTracker() {
super(LoggerFactory.getLogger(DefaultGobblinInstanceDriverImpl.this._log.getName() +
"_jobExecutionListener"));
}
@Override public String toString() {
return _log.get().getName();
}
@Override public void onStatusChange(JobExecutionState state, RunningState previousStatus, RunningState newStatus) {
super.onStatusChange(state, previousStatus, newStatus);
_callbacksDispatcher.onStatusChange(state, previousStatus, newStatus);
}
@Override
public void onStageTransition(JobExecutionState state, String previousStage, String newStage) {
super.onStageTransition(state, previousStage, newStage);
_callbacksDispatcher.onStageTransition(state, previousStage, newStage);
}
@Override
public void onMetadataChange(JobExecutionState state, String key, Object oldValue, Object newValue) {
super.onMetadataChange(state, key, oldValue, newValue);
_callbacksDispatcher.onMetadataChange(state, key, oldValue, newValue);
}
}
/** The runnable invoked by the Job scheduler */
class JobSpecRunnable implements Runnable {
private final JobSpec _jobSpec;
private final GobblinInstanceDriver _instanceDriver;
public JobSpecRunnable(JobSpec jobSpec, GobblinInstanceDriver instanceDriver) {
_jobSpec = jobSpec;
_instanceDriver = instanceDriver;
}
@Override
public void run() {
try {
JobExecutionDriver driver = _jobLauncher.launchJob(new ResolvedJobSpec(_jobSpec, _instanceDriver));
_callbacksDispatcher.onJobLaunch(driver);
driver.registerStateListener(new JobStateTracker());
ExecutorsUtils.newThreadFactory(Optional.of(_log), Optional.of("gobblin-instance-driver")).newThread(driver).start();
}
catch (Throwable t) {
_log.error("Job launch failed: " + t, t);
}
}
}
/** Listens to changes in the Job catalog and schedules/un-schedules jobs. */
protected class JobSpecListener extends DefaultJobCatalogListenerImpl {
public JobSpecListener() {
super(LoggerFactory.getLogger(DefaultGobblinInstanceDriverImpl.this._log.getName() +
"_jobSpecListener"));
}
@Override public String toString() {
return _log.get().getName();
}
@Override public void onAddJob(JobSpec addedJob) {
super.onAddJob(addedJob);
_jobScheduler.scheduleJob(addedJob, createJobSpecRunnable(addedJob));
}
@Override public void onDeleteJob(URI deletedJobURI, String deletedJobVersion) {
super.onDeleteJob(deletedJobURI, deletedJobVersion);
_jobScheduler.unscheduleJob(deletedJobURI);
}
@Override public void onUpdateJob(JobSpec updatedJob) {
super.onUpdateJob(updatedJob);
_jobScheduler.scheduleJob(updatedJob, createJobSpecRunnable(updatedJob));
}
}
@VisibleForTesting JobSpecRunnable createJobSpecRunnable(JobSpec addedJob) {
return new JobSpecRunnable(addedJob, this);
}
ConfigAccessor getInstanceCfg() {
return _instanceCfg;
}
@Override
public void registerJobLifecycleListener(JobLifecycleListener listener) {
_callbacksDispatcher.registerJobLifecycleListener(listener);
}
@Override
public void unregisterJobLifecycleListener(JobLifecycleListener listener) {
_callbacksDispatcher.unregisterJobLifecycleListener(listener);
}
@Override
public List<JobLifecycleListener> getJobLifecycleListeners() {
return _callbacksDispatcher.getJobLifecycleListeners();
}
@Override
public void registerWeakJobLifecycleListener(JobLifecycleListener listener) {
_callbacksDispatcher.registerWeakJobLifecycleListener(listener);
}
@Override public MetricContext getMetricContext() {
return _metricCtx;
}
@Override public boolean isInstrumentationEnabled() {
return _instrumentationEnabled;
}
@Override public List<Tag<?>> generateTags(gobblin.configuration.State state) {
return Collections.emptyList();
}
@Override public void switchMetricContext(List<Tag<?>> tags) {
throw new UnsupportedOperationException();
}
@Override
public void switchMetricContext(MetricContext context) {
throw new UnsupportedOperationException();
}
@Override public StandardMetrics getMetrics() {
return _metrics;
}
@Override
public String getInstanceName() {
return _instanceName;
}
}