accounting-lib/src/main/java/org/gcube/accounting/persistence/Persistence.java

189 lines
5.7 KiB
Java

/**
*
*/
package org.gcube.accounting.persistence;
import java.io.File;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.gcube.accounting.datamodel.RawUsageRecord;
import org.gcube.accounting.datamodel.UsageRecord;
import org.gcube.accounting.exception.InvalidValueException;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.provider.ProviderFactory;
import org.gcube.smartgears.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/
*/
public abstract class Persistence {
private static final Logger logger = LoggerFactory.getLogger(Persistence.class);
private static final String ACCOUTING_FALLBACK_FILENAME = "accountingFallback.log";
/**
* The singleton instance of persistence
*/
protected static Persistence persistence;
protected static FallbackPersistence fallback;
static {
File file;
try {
ContainerContext containerContext = ProviderFactory.provider().containerContext();
org.gcube.smartgears.persistence.Persistence smartgearPersistence = containerContext.persistence();
file = smartgearPersistence.writefile(ACCOUTING_FALLBACK_FILENAME);
} catch(Exception e){
file = Utils.fileAt(new File(".", ACCOUTING_FALLBACK_FILENAME).getAbsolutePath()).toWrite();
}
fallback = new FallbackPersistence(file);
try {
/*
ServiceLoader<Persistence> serviceLoader = ServiceLoader.load(Persistence.class);
for (Persistence foundPersistence : serviceLoader) {
if(foundPersistence.getClass().isInstance(FallbackPersistence.class)){
continue;
}
try {
logger.debug(String.format("Testing %s", persistence.getClass().getSimpleName()));
foundPersistence.prepareConnection();
persistence = foundPersistence;
break;
} catch (Exception e) {
logger.debug(String.format("%s not initialized correctly. It will not be used", persistence.getClass().getSimpleName()));
}
}
if(persistence==null){
persistence = fallback;
}
*/
persistence = new CouchDBPersistence();
} catch(Exception e){
logger.error("Unable to instance {}. Using fallback as default",
CouchDBPersistence.class.getSimpleName());
persistence = fallback;
}
}
public static RawUsageRecord createTestRawUsageRecord(){
RawUsageRecord usageRecord = new RawUsageRecord();
try {
usageRecord.setCreatorId("accounting");
usageRecord.setConsumerId("accounting");
usageRecord.setResourceOwner("accounting.lib");
usageRecord.setResourceScope("/gcube/devsec");
Calendar creatingTime = new GregorianCalendar();
Calendar startTime = new GregorianCalendar();
Calendar endTime = new GregorianCalendar();
usageRecord.setCreationTime(creatingTime);
usageRecord.setStartTime(startTime);
usageRecord.setEndTime(endTime);
usageRecord.setResourceSpecificProperty("ConnectionTest", "Test");
} catch (InvalidValueException e1) {
}
return usageRecord;
}
/**
* Pool for thread execution
*/
private ExecutorService pool;
/**
* @return the singleton instance of persistence
* @throws Exception if fails
*/
public static Persistence getInstance() {
return persistence;
}
protected Persistence() {
pool = Executors.newCachedThreadPool();
}
/**
* Prepare the connection to persistence.
* This method must be used by implementation class to open
* the connection with the persistence storage, DB, file etc.
* @throws Exception if fails
*/
protected abstract void prepareConnection() throws Exception;
/**
* Prepare the connection and try to write a test record on default
* persistence and fallback persistence.
* This method should not be re-implemented from subclass.
* @throws Exception if fails
*/
public void connect() throws Exception {
prepareConnection();
this.account(createTestRawUsageRecord());
}
/**
* This method contains the code to save the {@link #UsageRecord}
*
*/
protected abstract void reallyAccount(UsageRecord usageRecord) throws Exception;
private void accountWithFallback(UsageRecord usageRecord) throws Exception {
String persistenceName = getInstance().getClass().getSimpleName();
try {
//logger.debug("Going to account {} using {}", usageRecord, persistenceName);
persistence.reallyAccount(usageRecord);
logger.debug("{} accounted succesfully from {}.", usageRecord, persistenceName);
} catch (Exception e) {
String fallabackPersistenceName = fallback.getClass().getSimpleName();
try {
logger.error("{} was not accounted succesfully from {}. Trying to use {}.",
usageRecord, persistenceName, fallabackPersistenceName);
fallback.reallyAccount(usageRecord);
logger.debug("{} accounted succesfully from {}",
usageRecord, fallabackPersistenceName);
}catch(Exception ex){
logger.error("{} was not accounted at all", usageRecord);
throw e;
}
}
}
/**
* Persist the {@link #UsageRecord}.
* This method account the record in a separated thread. So that the
* program can continue the execution.
* If the persistence fails the class write that the record in a local file
* so that the {@link #UsageRecord} can be recorder later.
* @param usageRecord the {@link #UsageRecord} to persist
*/
public void account(final UsageRecord usageRecord){
Runnable runnable = new Runnable(){
@Override
public void run(){
try {
persistence.accountWithFallback(usageRecord);
} catch (Exception e) {
logger.error("Error accouting UsageRecod", e.getCause());
}
}
};
pool.execute(runnable);
}
public abstract void close() throws Exception;
}