package cz.cuni.mff.d3s.been.objectrepository;
import static cz.cuni.mff.d3s.been.objectrepository.ObjectRepositoryServiceInfoConstants.SERVICE_NAME;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cz.cuni.mff.d3s.been.cluster.*;
import cz.cuni.mff.d3s.been.cluster.context.ClusterContext;
import cz.cuni.mff.d3s.been.util.PropertyReader;
import cz.cuni.mff.d3s.been.core.service.ServiceInfo;
import cz.cuni.mff.d3s.been.core.service.ServiceState;
import cz.cuni.mff.d3s.been.objectrepository.janitor.Janitor;
import cz.cuni.mff.d3s.been.storage.Storage;
/**
* Generic persistence layer for BEEN entities
*/
public final class ObjectRepository implements IClusterService {
private static final Logger log = LoggerFactory.getLogger(ObjectRepository.class);
private final ClusterContext ctx;
private final Storage storage;
private final String beenId;
private PersistentQueueDrain entityDrain;
private QueryQueueDrain queryDrain;
private Janitor janitor;
private ServiceInfo info;
private final PropertyReader propertyReader;
private ObjectRepository(ClusterContext ctx, Storage storage, String beenId) {
this.ctx = ctx;
this.storage = storage;
this.beenId = beenId;
this.propertyReader = PropertyReader.on(ctx.getProperties());
}
/**
* Build an object repository over a ready (but not running) persistence layer
*
* @param ctx
* Cluster context to work in (provides shared queues to work with)
* @param storage
* Persistence layer to use
* @param beenId ID unique to this JVM
*
* @return The object repository
*/
public static ObjectRepository create(ClusterContext ctx, Storage storage, String beenId) {
return new ObjectRepository(ctx, storage, beenId);
}
@Override
public void start() throws ServiceException {
log.info("Starting ObjectRepository...");
if (storage == null) {
throw new ServiceException("Cannot start an object repository over a null Storage");
}
// create sub-services
entityDrain = PersistentQueueDrain.create(ctx, Names.PERSISTENCE_QUEUE_NAME, storage);
queryDrain = QueryQueueDrain.create(ctx, storage);
janitor = Janitor.create(ctx, storage);
// start sub-services
storage.start();
entityDrain.start();
queryDrain.start();
janitor.start();
info = new ServiceInfo(SERVICE_NAME, beenId);
info.setServiceInfo("ObjectRepository running");
info.setServiceState(ServiceState.OK);
info.setHazelcastUuid(ctx.getInstanceType() != NodeType.NATIVE ? ctx.getCluster().getLocalMember().getUuid() : null);
int period = 30;
int timeout = 45;
Runnable serviceInfoUpdater = new ServiceInfoUpdater(ctx, info, timeout) {
@Override
public void run() {
if (storage.isConnected()) {
info.setServiceInfo("Storage is connected");
info.setServiceState(ServiceState.OK);
} else {
info.setServiceInfo("Storage is disconnected");
info.setServiceState(ServiceState.WARN);
}
super.run();
}
};
ctx.schedule(serviceInfoUpdater, 0, period, TimeUnit.SECONDS);
log.info("ObjectRepository started.");
}
@Override
public void stop() {
log.info("Stopping ObjectRepository...");
janitor.interrupt();
try {
janitor.join();
} catch (InterruptedException e) {
log.warn("Interrupted when waiting for janitor termination. Exiting dirty");
}
entityDrain.stop();
queryDrain.stop();
storage.stop();
try {
ctx.removeServiceInfo(info);
} catch (IllegalStateException e) {
// unregistering over a Hazelcast instance that is no longer active
log.warn("Failed to unhook ObjectRepository from the cluster. ObjectRepository info is likely to linger.", e);
}
log.info("ObjectRepository stopped.");
}
@Override
public Reaper createReaper() {
final Reaper reaper = new Reaper() {
@Override
protected void reap() throws InterruptedException {
ObjectRepository.this.stop();
}
};
return reaper;
}
}