/* * Copyright © 2014-2015 Cask Data, 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 co.cask.cdap.internal.app.runtime.service; import co.cask.cdap.api.app.ApplicationSpecification; import co.cask.cdap.api.metrics.MetricsCollectionService; import co.cask.cdap.api.service.ServiceSpecification; import co.cask.cdap.app.program.Program; import co.cask.cdap.app.runtime.ProgramController; import co.cask.cdap.app.runtime.ProgramOptions; import co.cask.cdap.app.runtime.ProgramRunner; import co.cask.cdap.common.app.RunIds; import co.cask.cdap.common.conf.CConfiguration; import co.cask.cdap.data2.dataset2.DatasetFramework; import co.cask.cdap.data2.metadata.writer.ProgramContextAware; import co.cask.cdap.internal.app.runtime.AbstractProgramRunnerWithPlugin; import co.cask.cdap.internal.app.runtime.DataFabricFacadeFactory; import co.cask.cdap.internal.app.runtime.ProgramControllerServiceAdapter; import co.cask.cdap.internal.app.runtime.ProgramOptionConstants; import co.cask.cdap.internal.app.runtime.plugin.PluginInstantiator; import co.cask.cdap.internal.app.services.ServiceHttpServer; import co.cask.cdap.proto.Id; import co.cask.cdap.proto.ProgramType; import co.cask.tephra.TransactionSystemClient; import com.google.common.base.Preconditions; import com.google.common.io.Closeables; import com.google.common.util.concurrent.Service; import com.google.inject.Inject; import org.apache.twill.api.RunId; import org.apache.twill.api.ServiceAnnouncer; import org.apache.twill.common.Threads; import org.apache.twill.discovery.DiscoveryServiceClient; import org.apache.twill.internal.ServiceListenerAdapter; /** * A {@link ProgramRunner} that runs a component inside a Service (either a HTTP Server or a Worker). */ public class ServiceProgramRunner extends AbstractProgramRunnerWithPlugin { private final MetricsCollectionService metricsCollectionService; private final DatasetFramework datasetFramework; private final DiscoveryServiceClient discoveryServiceClient; private final TransactionSystemClient txClient; private final ServiceAnnouncer serviceAnnouncer; private final DataFabricFacadeFactory dataFabricFacadeFactory; @Inject public ServiceProgramRunner(CConfiguration cConf, MetricsCollectionService metricsCollectionService, DatasetFramework datasetFramework, DiscoveryServiceClient discoveryServiceClient, TransactionSystemClient txClient, ServiceAnnouncer serviceAnnouncer, DataFabricFacadeFactory dataFabricFacadeFactory) { super(cConf); this.metricsCollectionService = metricsCollectionService; this.datasetFramework = datasetFramework; this.discoveryServiceClient = discoveryServiceClient; this.txClient = txClient; this.serviceAnnouncer = serviceAnnouncer; this.dataFabricFacadeFactory = dataFabricFacadeFactory; } @Override public ProgramController run(Program program, ProgramOptions options) { int instanceId = Integer.parseInt(options.getArguments().getOption(ProgramOptionConstants.INSTANCE_ID, "-1")); Preconditions.checkArgument(instanceId >= 0, "Missing instance Id"); int instanceCount = Integer.parseInt(options.getArguments().getOption(ProgramOptionConstants.INSTANCES, "0")); Preconditions.checkArgument(instanceCount > 0, "Invalid or missing instance count"); String runIdOption = options.getArguments().getOption(ProgramOptionConstants.RUN_ID); Preconditions.checkNotNull(runIdOption, "Missing runId"); RunId runId = RunIds.fromString(runIdOption); ApplicationSpecification appSpec = program.getApplicationSpecification(); Preconditions.checkNotNull(appSpec, "Missing application specification."); ProgramType programType = program.getType(); Preconditions.checkNotNull(programType, "Missing processor type."); Preconditions.checkArgument(programType == ProgramType.SERVICE, "Only Service process type is supported."); ServiceSpecification spec = appSpec.getServices().get(program.getName()); String host = options.getArguments().getOption(ProgramOptionConstants.HOST); Preconditions.checkArgument(host != null, "No hostname is provided"); // Setup dataset framework context, if required if (datasetFramework instanceof ProgramContextAware) { Id.Program programId = program.getId(); ((ProgramContextAware) datasetFramework).initContext(new Id.Run(programId, runId.getId())); } final PluginInstantiator pluginInstantiator = createPluginInstantiator(options, program.getClassLoader()); try { ServiceHttpServer component = new ServiceHttpServer(host, program, spec, runId, options.getUserArguments(), instanceId, instanceCount, serviceAnnouncer, metricsCollectionService, datasetFramework, dataFabricFacadeFactory, txClient, discoveryServiceClient, pluginInstantiator); // Add a service listener to make sure the plugin instantiator is closed when the worker driver finished. component.addListener(new ServiceListenerAdapter() { @Override public void terminated(Service.State from) { Closeables.closeQuietly(pluginInstantiator); } @Override public void failed(Service.State from, Throwable failure) { Closeables.closeQuietly(pluginInstantiator); } }, Threads.SAME_THREAD_EXECUTOR); ProgramController controller = new ServiceProgramControllerAdapter(component, program.getId(), runId, spec.getName() + "-" + instanceId); component.start(); return controller; } catch (Throwable t) { Closeables.closeQuietly(pluginInstantiator); throw t; } } private static final class ServiceProgramControllerAdapter extends ProgramControllerServiceAdapter { private final ServiceHttpServer service; public ServiceProgramControllerAdapter(ServiceHttpServer service, Id.Program programId, RunId runId, String componentName) { super(service, programId, runId, componentName); this.service = service; } @Override protected void doCommand(String name, Object value) throws Exception { super.doCommand(name, value); if (!ProgramOptionConstants.INSTANCES.equals(name) || !(value instanceof Integer)) { return; } service.setInstanceCount((Integer) value); } } }