package org.yamcs.archive;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yamcs.ConfigurationException;
import org.yamcs.StreamConfig;
import org.yamcs.StreamConfig.StandardStreamType;
import org.yamcs.StreamConfig.StreamConfigEntry;
import org.yamcs.YConfiguration;
import org.yamcs.YamcsException;
import org.yamcs.YamcsServer;
import org.yamcs.protobuf.Yamcs.IndexRequest;
import org.yamcs.yarch.Stream;
import org.yamcs.yarch.YarchDatabase;
import org.yamcs.yarch.YarchException;
import com.google.common.util.concurrent.AbstractService;
/**
* Handles index retrievals and tags
*
*/
public class IndexServer extends AbstractService {
static Logger log=LoggerFactory.getLogger(IndexServer.class.getName());
TmIndex tmIndexer;
final String yamcsInstance;
ThreadPoolExecutor executor=new ThreadPoolExecutor(10,10,10,TimeUnit.SECONDS,new ArrayBlockingQueue<>(10));
final TagDb tagDb;
/**
* Maps instance names to archive directories
*/
final HashSet<String> instances=new HashSet<>();
public IndexServer(String instance) throws IOException, YarchException {
this(instance, null);
}
public IndexServer(String yamcsInstance, Map<String, Object> config) throws YarchException, IOException {
boolean readonly = false;
this.yamcsInstance = yamcsInstance;
YConfiguration c = YConfiguration.getConfiguration("yamcs."+yamcsInstance);
if(c.containsKey("tmIndexer")) {
String icn = c.getString("tmIndexer");
tmIndexer = loadIndexerFromClass(icn, yamcsInstance, readonly);
} else {
tmIndexer = new CccsdsTmIndex(yamcsInstance, readonly);
}
if(!readonly) {
StreamConfig sc = StreamConfig.getInstance(yamcsInstance);
if(config==null) {
List<StreamConfigEntry> sceList = sc.getEntries(StandardStreamType.tm);
for(StreamConfigEntry sce: sceList){
subscribe(sce);
}
} else {
List<String> streamNames = YConfiguration.getList(config, "streams");
for(String sn: streamNames) {
StreamConfigEntry sce = sc.getEntry(StandardStreamType.tm, sn);
if(sce==null) {
throw new ConfigurationException("No stream config found for '"+sn+"'");
}
subscribe(sce);
}
}
}
tagDb = YarchDatabase.getInstance(yamcsInstance).getDefaultStorageEngine().getTagDb();
executor.allowCoreThreadTimeOut(true);
}
@Override
protected void doStart() {
notifyStarted();
}
@Override
protected void doStop() {
try {
tmIndexer.close();
tagDb.close();
notifyStopped();
} catch (IOException e) {
log.error("failed to stop the indexer", e);
notifyFailed(e);
}
}
public String getInstance() {
return yamcsInstance;
}
/**
* Asynchronously submit an index request. When the request is processed the
* provided listener will receive callbacks.
* @param req the request to be executed
* @param listener where to send the resulting data
* @throws YamcsException exception thrown if the service is not in running state or the the request is invalid
*/
public void submitIndexRequest(IndexRequest req, IndexRequestListener listener) throws YamcsException {
State state = state();
if(state!=State.RUNNING) {
throw new YamcsException("The IndexServer service is not in state RUNNING but "+state);
}
if(!YamcsServer.hasInstance(req.getInstance())) {
throw new YamcsException("Invalid instance "+req.getInstance());
}
IndexRequestProcessor p = new IndexRequestProcessor(tmIndexer, req, listener);
executor.submit(p);
}
private void subscribe(StreamConfigEntry sce) {
YarchDatabase ydb = YarchDatabase.getInstance(yamcsInstance);
Stream tmStream = ydb.getStream(sce.getName());
if(tmStream==null) {
throw new ConfigurationException("There is no stream named "+sce.getName());
}
tmStream.addSubscriber(tmIndexer);
}
private static TmIndex loadIndexerFromClass(String icn, String instance, boolean readonly) throws IOException {
try {
Class<TmIndex> ic=(Class<TmIndex>)Class.forName(icn);
Constructor<TmIndex> c=ic.getConstructor(String.class, Boolean.TYPE);
return c.newInstance(instance, readonly);
} catch (InvocationTargetException e) {
Throwable t=e.getCause();
if(t instanceof ConfigurationException) {
throw (ConfigurationException)t;
} else if(t instanceof IOException) {
throw (IOException)t;
} else {
throw new ConfigurationException(t.toString());
}
} catch (Exception e) {
throw new ConfigurationException("Cannot create indexer from class "+icn+": "+e);
}
}
}