/** * */ 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.UsageRecord; import org.gcube.accounting.datamodel.implementations.ServiceUsageRecord; import org.gcube.accounting.exception.InvalidValueException; 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; private static File file(File file) throws IllegalArgumentException { if (file.isDirectory()) throw new IllegalArgumentException(file.getAbsolutePath() + " cannot be used in write mode because it's folder"); //create folder structure it does not exist if (!file.getParentFile().exists()) file.getParentFile().mkdirs(); return file; } 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 = file(new File(".", ACCOUTING_FALLBACK_FILENAME).getAbsolutePath()); } */ file = file(new File(".", ACCOUTING_FALLBACK_FILENAME)); fallback = new FallbackPersistence(file); try { /* ServiceLoader 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 UsageRecord createTestRawUsageRecord(){ UsageRecord usageRecord = new ServiceUsageRecord(); try { usageRecord.setCreatorId("accounting"); usageRecord.setConsumerId("accounting"); 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; }