org.yamcs.YamcsServerInstance Maven / Gradle / Ivy
package org.yamcs;
import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator;
import static org.yamcs.YamcsServer.CFG_CRASH_HANDLER_KEY;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import org.yamcs.Spec.OptionType;
import org.yamcs.logging.Log;
import org.yamcs.management.LinkManager;
import org.yamcs.mdb.DatabaseLoadException;
import org.yamcs.mdb.Mdb;
import org.yamcs.mdb.MdbFactory;
import org.yamcs.protobuf.Mdb.MissionDatabase;
import org.yamcs.protobuf.YamcsInstance;
import org.yamcs.protobuf.YamcsInstance.InstanceState;
import org.yamcs.time.RealtimeTimeService;
import org.yamcs.time.TimeService;
import org.yamcs.utils.ExceptionUtil;
import org.yamcs.utils.ServiceUtil;
import org.yamcs.utils.YObjectLoader;
import org.yamcs.xtce.Header;
import org.yamcs.yarch.YarchDatabase;
import org.yamcs.yarch.YarchDatabaseInstance;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.Service;
import com.google.common.util.concurrent.UncheckedExecutionException;
/**
* Represents a Yamcs instance together with the instance specific services and the processors
*
*/
public class YamcsServerInstance extends YamcsInstanceService {
private String name;
Log log;
TimeService timeService;
private CrashHandler crashHandler;
List services;
private Mdb mdb;
InstanceMetadata metadata;
YConfiguration config;
final Map processors = new LinkedHashMap<>();
LinkManager linkManager;
final int instanceId;
YamcsServerInstance(String name) {
this(name, new InstanceMetadata());
}
YamcsServerInstance(String name, InstanceMetadata metadata) {
this.name = name;
this.metadata = metadata;
log = new Log(getClass(), name);
this.instanceId = (YamcsServer.getServer().getServerId() + "." + name).hashCode();
}
public static Spec getSpec() {
Spec serviceSpec = new Spec();
serviceSpec.addOption("class", OptionType.STRING).withRequired(true);
serviceSpec.addOption("args", OptionType.ANY);
serviceSpec.addOption("name", OptionType.STRING);
serviceSpec.addOption("enabledAtStartup", OptionType.BOOLEAN);
Spec mdbSpec = new Spec();
mdbSpec.addOption("type", OptionType.STRING).withRequired(true);
mdbSpec.addOption("spec", OptionType.STRING);
mdbSpec.addOption("writable", OptionType.BOOLEAN).withDefault(false);
mdbSpec.addOption("args", OptionType.MAP).withSpec(Spec.ANY);
mdbSpec.addOption("subLoaders", OptionType.LIST).withElementType(OptionType.MAP).withSpec(mdbSpec);
Spec spec = new Spec();
spec.addOption("services", OptionType.LIST).withElementType(OptionType.MAP).withSpec(serviceSpec);
// Detailed validation on these is done
// in LinkManager, MdbFactory, and StreamInitializer
spec.addOption("dataLinks", OptionType.LIST).withElementType(OptionType.MAP).withSpec(Spec.ANY);
spec.addOption("streamConfig", OptionType.MAP).withSpec(Spec.ANY);
spec.addOption("mdb", OptionType.LIST).withElementType(OptionType.MAP).withSpec(mdbSpec);
spec.addOption("mdbSpec", OptionType.STRING);
spec.mutuallyExclusive("mdb", "mdbSpec");
spec.addOption("timeService", OptionType.ANY);
spec.addOption("tmIndexer", OptionType.ANY);
spec.addOption("eventDecoders", OptionType.ANY);
YarchDatabaseInstance.addSpec(spec);
// "anchors" is used to allow yaml anchors (reuse of blocks)
spec.addOption("anchors", OptionType.ANY);
YamcsServer yamcs = YamcsServer.getServer();
Map extraSections = yamcs.getConfigurationSections(ConfigScope.YAMCS_INSTANCE);
extraSections.forEach((key, sectionSpec) -> {
spec.addOption(key, OptionType.MAP).withSpec(sectionSpec)
.withApplySpecDefaults(true);
});
return spec;
}
void init(YConfiguration config) {
try {
this.config = getSpec().validate(config);
} catch (ValidationException e) {
// Don't care about stacktrace inside spec
throw new UncheckedExecutionException(new ValidationException(
e.getContext(), e.getMessage()));
}
initAsync();
try {
awaitInitialized();
} catch (IllegalStateException e) {
throw new UncheckedExecutionException(e.getCause());
}
}
@Override
public void doInit() {
try {
loadTimeService();
loadCrashHandler();
// first load the MDB (if there is an error in it, we don't want to load any other service)
mdb = MdbFactory.getInstance(name);
StreamInitializer.createStreams(name);
// create services before the link manager so that the pre-processors can find them
// if required (even uninitialized)
List serviceConfigs = config.getServiceConfigList("services");
services = YamcsServer.createServices(name, serviceConfigs, log);
linkManager = new LinkManager(name);
YamcsServer.initServices(name, services);
notifyInitialized();
} catch (Exception e) {
notifyFailed(e);
}
}
@Override
protected void doStart() {
linkManager.startLinks();
for (ServiceWithConfig swc : services) {
if (swc.enableAtStartup) {
log.debug("Starting service {}", swc.getName());
swc.service.startAsync();
} else {
log.debug("Not starting service {} because enableAtStartup is false", swc.getName());
}
}
for (ServiceWithConfig swc : services) {
if (swc.enableAtStartup) {
log.info("Awaiting start of service {}", swc.getName());
ServiceUtil.awaitServiceRunning(swc.service);
}
}
notifyStarted();
}
@Override
protected void doStop() {
linkManager.stopLinks();
ListeningExecutorService serviceStoppers = listeningDecorator(Executors.newCachedThreadPool());
List> stopFutures = new ArrayList<>();
for (ServiceWithConfig swc : services) {
stopFutures.add(serviceStoppers.submit(() -> {
swc.service.stopAsync();
log.info("Awaiting termination of service {}", swc.getName());
ServiceUtil.awaitServiceTerminated(swc.service, YamcsServer.SERVICE_STOP_GRACE_TIME, log);
}));
}
linkManager = null;
serviceStoppers.shutdown();
Futures.addCallback(Futures.allAsList(stopFutures), new FutureCallback
© 2015 - 2024 Weber Informatics LLC | Privacy Policy