From 080cb3c1b48c599ef248168f4f5cba0ab629b515 Mon Sep 17 00:00:00 2001 From: Luca Frosini Date: Thu, 28 May 2015 11:19:25 +0000 Subject: [PATCH] Remaning common-accounting in accounting-lib git-svn-id: https://svn.d4science.research-infrastructures.eu/gcube/trunk/accounting/accounting-lib@115091 82a268e6-3cf1-43bd-a215-b396298e98cf --- .classpath | 27 ++ .settings/org.eclipse.core.resources.prefs | 3 + .settings/org.eclipse.jdt.core.prefs | 8 + .settings/org.eclipse.m2e.core.prefs | 4 + .settings/org.eclipse.wst.common.component | 5 + ....eclipse.wst.common.project.facet.core.xml | 5 + distro/INSTALL | 1 + distro/LICENSE | 8 + distro/MAINTAINERS | 2 + distro/README | 31 ++ distro/changelog.xml | 5 + distro/descriptor.xml | 42 ++ distro/profile.xml | 28 ++ distro/svnpath.txt | 1 + pom.xml | 82 ++++ .../accounting/datamodel/RawUsageRecord.java | 453 ++++++++++++++++++ .../accounting/datamodel/UsageRecord.java | 228 +++++++++ .../implementations/JobUsageRecord.java | 41 ++ .../implementations/PortletUsageRecord.java | 29 ++ .../implementations/ServiceUsageRecord.java | 42 ++ .../StorageStatusUsageRecord.java | 34 ++ .../implementations/StorageUsageRecord.java | 41 ++ .../implementations/TaskUsageRecord.java | 56 +++ .../validators/NotEmptyIfNotNull.java | 15 + .../NotEmptyIfNotNullValidator.java | 30 ++ .../datamodel/validators/UsageRecordType.java | 15 + .../validators/UsageRecordTypeValidator.java | 23 + .../datamodel/validators/ValidTime.java | 15 + .../validators/ValidTimeValidator.java | 23 + .../exception/InvalidValueException.java | 15 + .../messaging/ResourceAccounting.java | 26 + .../messaging/ResourceAccountingFactory.java | 20 + .../persistence/CouchDBPersistence.java | 117 +++++ .../persistence/FallbackPersistence.java | 56 +++ .../persistence/MongoDBPersistence.java | 17 + .../accounting/persistence/Persistence.java | 188 ++++++++ .../datamodel/RawUsageRecordTest.java | 61 +++ .../persistence/CouchDBPersistenceTest.java | 33 ++ .../persistence/PersistenceTest.java | 48 ++ .../validators/ValidTimeValidatorTest.java | 28 ++ 40 files changed, 1906 insertions(+) create mode 100644 .classpath create mode 100644 .settings/org.eclipse.core.resources.prefs create mode 100644 .settings/org.eclipse.jdt.core.prefs create mode 100644 .settings/org.eclipse.m2e.core.prefs create mode 100644 .settings/org.eclipse.wst.common.component create mode 100644 .settings/org.eclipse.wst.common.project.facet.core.xml create mode 100644 distro/INSTALL create mode 100644 distro/LICENSE create mode 100644 distro/MAINTAINERS create mode 100644 distro/README create mode 100644 distro/changelog.xml create mode 100644 distro/descriptor.xml create mode 100644 distro/profile.xml create mode 100644 distro/svnpath.txt create mode 100644 pom.xml create mode 100644 src/main/java/org/gcube/accounting/datamodel/RawUsageRecord.java create mode 100644 src/main/java/org/gcube/accounting/datamodel/UsageRecord.java create mode 100644 src/main/java/org/gcube/accounting/datamodel/implementations/JobUsageRecord.java create mode 100644 src/main/java/org/gcube/accounting/datamodel/implementations/PortletUsageRecord.java create mode 100644 src/main/java/org/gcube/accounting/datamodel/implementations/ServiceUsageRecord.java create mode 100644 src/main/java/org/gcube/accounting/datamodel/implementations/StorageStatusUsageRecord.java create mode 100644 src/main/java/org/gcube/accounting/datamodel/implementations/StorageUsageRecord.java create mode 100644 src/main/java/org/gcube/accounting/datamodel/implementations/TaskUsageRecord.java create mode 100644 src/main/java/org/gcube/accounting/datamodel/validators/NotEmptyIfNotNull.java create mode 100644 src/main/java/org/gcube/accounting/datamodel/validators/NotEmptyIfNotNullValidator.java create mode 100644 src/main/java/org/gcube/accounting/datamodel/validators/UsageRecordType.java create mode 100644 src/main/java/org/gcube/accounting/datamodel/validators/UsageRecordTypeValidator.java create mode 100644 src/main/java/org/gcube/accounting/datamodel/validators/ValidTime.java create mode 100644 src/main/java/org/gcube/accounting/datamodel/validators/ValidTimeValidator.java create mode 100644 src/main/java/org/gcube/accounting/exception/InvalidValueException.java create mode 100644 src/main/java/org/gcube/accounting/messaging/ResourceAccounting.java create mode 100644 src/main/java/org/gcube/accounting/messaging/ResourceAccountingFactory.java create mode 100644 src/main/java/org/gcube/accounting/persistence/CouchDBPersistence.java create mode 100644 src/main/java/org/gcube/accounting/persistence/FallbackPersistence.java create mode 100644 src/main/java/org/gcube/accounting/persistence/MongoDBPersistence.java create mode 100644 src/main/java/org/gcube/accounting/persistence/Persistence.java create mode 100644 src/test/java/org/gcube/accounting/datamodel/RawUsageRecordTest.java create mode 100644 src/test/java/org/gcube/accounting/datamodel/persistence/CouchDBPersistenceTest.java create mode 100644 src/test/java/org/gcube/accounting/datamodel/persistence/PersistenceTest.java create mode 100644 src/test/java/org/gcube/accounting/datamodel/validators/ValidTimeValidatorTest.java diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..a673149 --- /dev/null +++ b/.classpath @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..e9441bb --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding/=UTF-8 diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..443e085 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,8 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..f897a7f --- /dev/null +++ b/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/.settings/org.eclipse.wst.common.component b/.settings/org.eclipse.wst.common.component new file mode 100644 index 0000000..91b68a6 --- /dev/null +++ b/.settings/org.eclipse.wst.common.component @@ -0,0 +1,5 @@ + + + + + diff --git a/.settings/org.eclipse.wst.common.project.facet.core.xml b/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 0000000..1b22d70 --- /dev/null +++ b/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/distro/INSTALL b/distro/INSTALL new file mode 100644 index 0000000..049006c --- /dev/null +++ b/distro/INSTALL @@ -0,0 +1 @@ +Used as library in the gCube Framework \ No newline at end of file diff --git a/distro/LICENSE b/distro/LICENSE new file mode 100644 index 0000000..07f22ed --- /dev/null +++ b/distro/LICENSE @@ -0,0 +1,8 @@ +gCube System - License +------------------------------------------------------------ + +The gCube/gCore software is licensed as Free Open Source software conveying to +the EUPL (http://ec.europa.eu/idabc/eupl). +The software and documentation is provided by its authors/distributors "as is" +and no expressed or implied warranty is given for its use, quality or fitness +for a particular case. \ No newline at end of file diff --git a/distro/MAINTAINERS b/distro/MAINTAINERS new file mode 100644 index 0000000..2cdfd00 --- /dev/null +++ b/distro/MAINTAINERS @@ -0,0 +1,2 @@ +Luca Frosini (luca.frosini@isti.cnr.it), CNR Pisa, +Istituto di Scienza e Tecnologie dell'Informazione "A. Faedo". diff --git a/distro/README b/distro/README new file mode 100644 index 0000000..47bdbe6 --- /dev/null +++ b/distro/README @@ -0,0 +1,31 @@ +The gCube System - Accounting Common Library +------------------------------------------------------------ + +This work has been partially supported by the following European projects: DILIGENT (FP6-2003-IST-2), +D4Science (FP7-INFRA-2007-1.2.2), D4Science-II (FP7-INFRA-2008-1.2.2), iMarine (FP7-INFRASTRUCTURES-2011-2), +and EUBrazilOpenBio (FP7-ICT-2011-EU-Brazil). + +Authors +------- + +* Luca Frosini (luca.frosini@isti.cnr.it), CNR Pisa, Istituto di Scienza e Tecnologie dell'Informazione "A. Faedo". + +This work has been conceptually derived from accounting-common component made by: +* Paolo Fabriani (paolo.fabriani@eng.it) Engineering Ingegneria Informatica S.p.A. +* Ermanno Travaglino (ermanno.travaglino@eng.it) Engineering Ingegneria Informatica S.p.A. + +Version and Release Date +------------------------ + +v 1.0, 19/05/2015 + + +Description +----------- + +Common Java library for usage accounting + +Licensing +--------- + +This software is licensed under the terms you may find in the file named "LICENSE" in this directory. diff --git a/distro/changelog.xml b/distro/changelog.xml new file mode 100644 index 0000000..6b81b51 --- /dev/null +++ b/distro/changelog.xml @@ -0,0 +1,5 @@ + + + First Release + + \ No newline at end of file diff --git a/distro/descriptor.xml b/distro/descriptor.xml new file mode 100644 index 0000000..21d8c88 --- /dev/null +++ b/distro/descriptor.xml @@ -0,0 +1,42 @@ + + servicearchive + + tar.gz + + / + + + ${distroDirectory} + / + true + + README + LICENSE + INSTALL + MAINTAINERS + changelog.xml + + 755 + true + + + + + ${distroDirectory}/profile.xml + / + true + + + target/${build.finalName}.jar + /${artifactId} + + + ${distroDirectory}/svnpath.txt + /${artifactId} + true + + + \ No newline at end of file diff --git a/distro/profile.xml b/distro/profile.xml new file mode 100644 index 0000000..b13ca2b --- /dev/null +++ b/distro/profile.xml @@ -0,0 +1,28 @@ + + + + Service + + ${description} + Accounting + ${artifactId} + ${version} + + + ${description} + ${artifactId} + ${version} + + ${groupId} + ${artifactId} + ${version} + + library + + ${build.finalName}.jar + + + + + + diff --git a/distro/svnpath.txt b/distro/svnpath.txt new file mode 100644 index 0000000..dcd0d22 --- /dev/null +++ b/distro/svnpath.txt @@ -0,0 +1 @@ +${scm.url} \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..cd5b3ab --- /dev/null +++ b/pom.xml @@ -0,0 +1,82 @@ + + 4.0.0 + + + org.gcube.tools + maven-parent + 1.0.0 + + + org.gcube.accounting + accounting-lib + 1.0.0-SNAPSHOT + Common Accounting + Accounting Common Library + jar + + + UTF-8 + ${project.basedir}/distro + + + + scm:https://svn.d4science.research-infrastructures.eu/gcube/trunk/accounting/${project.artifactId} + scm:https://svn.d4science.research-infrastructures.eu/gcube/trunk/accounting/${project.artifactId} + https://svn.d4science.research-infrastructures.eu/gcube/trunk/accounting/${project.artifactId} + + + + + + org.gcube.distribution + maven-smartgears-bom + LATEST + pom + import + + + + + + + + org.slf4j + slf4j-api + provided + + + org.gcube.core + common-validator + provided + + + org.gcube.core + common-smartgears + + + + + org.ektorp + org.ektorp + 1.3.0 + jar + + + org.codehaus.jackson + jackson-core-asl + 1.9.7 + jar + + + + + junit + junit + 4.11 + test + + + + + \ No newline at end of file diff --git a/src/main/java/org/gcube/accounting/datamodel/RawUsageRecord.java b/src/main/java/org/gcube/accounting/datamodel/RawUsageRecord.java new file mode 100644 index 0000000..ae6a13b --- /dev/null +++ b/src/main/java/org/gcube/accounting/datamodel/RawUsageRecord.java @@ -0,0 +1,453 @@ +/** + * + */ +package org.gcube.accounting.datamodel; + +import java.io.Serializable; +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.UUID; + +import org.gcube.accounting.datamodel.validators.NotEmptyIfNotNull; +import org.gcube.accounting.datamodel.validators.ValidTime; +import org.gcube.accounting.exception.InvalidValueException; +import org.gcube.common.validator.annotations.FieldValidator; +import org.gcube.common.validator.annotations.NotEmpty; +import org.gcube.common.validator.annotations.ValidityChecker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/ + * + */ +public class RawUsageRecord implements UsageRecord, Serializable { + + private static Logger logger = LoggerFactory.getLogger(RawUsageRecord.class); + + @NotEmpty + public static final String ID = "id"; + @NotEmpty + public static final String CREATOR_ID = "creatorId"; + @NotEmpty + public static final String CONSUMER_ID = "consumerId"; + @ValidTime + public static final String CREATION_TIME = "creationTime"; + @ValidTime + public static final String START_TIME = "startTime"; + @ValidTime + public static final String END_TIME = "endTime"; + @NotEmpty + public static final String RESOURCE_TYPE = "resourceType"; + @NotEmpty + public static final String RESOURCE_SCOPE = "resourceScope"; + @NotEmpty + public static final String RESOURCE_OWNER = "resourceOwner"; + @NotEmptyIfNotNull + public static final String AGGREGATED_ID = "aggregatedId"; + + /** + * Generated Serial Version UID + */ + private static final long serialVersionUID = -2060728578456796388L; + + /** + * resource-specific properties + */ + protected Map resourceSpecificProperties; + + protected final Map>> validation; + + protected void initializeValidation() { + logger.debug("Initializing Field Validators"); + List fields = Arrays.asList(this.getClass().getDeclaredFields()); + for(Field field : fields){ + String keyString; + try { + keyString = (String) field.get(null); + } catch (Exception e) { + continue; + } + List> fieldValidators = new ArrayList>(); + validation.put(keyString, fieldValidators); + for (Annotation annotation : field.getAnnotations()){ + if (annotation.annotationType().isAnnotationPresent(ValidityChecker.class)){ + Class> managedClass = ((ValidityChecker)annotation.annotationType().getAnnotation(ValidityChecker.class)).managed(); + FieldValidator validator; + try { + validator = managedClass.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + continue; + } + fieldValidators.add(validator); + } + } + } + } + + + public RawUsageRecord(){ + this.resourceSpecificProperties = new HashMap(); + this.validation = new HashMap>>(); + initializeValidation(); + this.resourceSpecificProperties.put(ID, UUID.randomUUID().toString()); + this.resourceSpecificProperties.put(RESOURCE_TYPE, this.getClass().getSimpleName()); + } + + public RawUsageRecord(Map properties) throws InvalidValueException { + this.validation = new HashMap>>(); + initializeValidation(); + setResourceSpecificProperties(properties); + } + + /** + * {@inheritDoc} + */ + @Override + public String getId() { + return (String) this.resourceSpecificProperties.get(ID); + } + + /** + * {@inheritDoc} + */ + @Override + public void setId(String id) throws InvalidValueException { + setResourceSpecificProperty(ID, id); + } + + /** + * {@inheritDoc} + */ + @Override + public String getCreatorId() { + return (String) this.resourceSpecificProperties.get(CREATOR_ID); + } + + /** + * {@inheritDoc} + */ + @Override + public void setCreatorId(String creatorId) throws InvalidValueException { + setResourceSpecificProperty(CREATOR_ID, creatorId); + } + + /** + * {@inheritDoc} + */ + @Override + public String getConsumerId() { + return (String) this.resourceSpecificProperties.get(CONSUMER_ID); + } + + /** + * {@inheritDoc} + */ + @Override + public void setConsumerId(String consumerId) throws InvalidValueException { + setResourceSpecificProperty(CONSUMER_ID, consumerId); + } + + protected Calendar timestampStringToCalendar(long millis){ + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(millis); + return calendar; + } + + /** + * {@inheritDoc} + */ + @Override + public Calendar getCreationTime() { + long millis = (Long) this.resourceSpecificProperties.get(CREATION_TIME); + return timestampStringToCalendar(millis); + } + + /** + * {@inheritDoc} + */ + @Override + public void setCreationTime(Calendar creationTime) throws InvalidValueException { + setResourceSpecificProperty(CREATION_TIME, creationTime.getTimeInMillis()); + } + + /** + * {@inheritDoc} + */ + @Override + @Deprecated + public void setCreateTime(Date createTime) throws InvalidValueException { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(createTime); + setCreationTime(calendar); + } + + /** + * {@inheritDoc} + */ + @Override + public Calendar getStartTime() { + long millis = (Long) this.resourceSpecificProperties.get(START_TIME); + return timestampStringToCalendar(millis); + } + + /** + * {@inheritDoc} + */ + @Override + public void setStartTime(Calendar startTime) throws InvalidValueException { + setResourceSpecificProperty(START_TIME, startTime.getTimeInMillis()); + + } + + /** + * {@inheritDoc} + */ + @Override + @Deprecated + public void setStartTime(Date startTime) throws InvalidValueException { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(startTime); + setStartTime(calendar); + } + + /** + * {@inheritDoc} + */ + @Override + public Calendar getEndTime() { + long millis = (Long) this.resourceSpecificProperties.get(END_TIME); + return timestampStringToCalendar(millis); + } + + /** + * {@inheritDoc} + */ + @Override + public void setEndTime(Calendar endTime) throws InvalidValueException { + setResourceSpecificProperty(END_TIME, endTime.getTimeInMillis()); + } + + /** + * {@inheritDoc} + */ + @Override + @Deprecated + public void setEndTime(Date endTime) throws InvalidValueException { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(endTime); + setEndTime(calendar); + } + + + /** + * {@inheritDoc} + */ + @Override + public String getUsageRecordType() { + return (String) this.resourceSpecificProperties.get(RESOURCE_TYPE); + } + + /** + * {@inheritDoc} + */ + @Override + @Deprecated + public String getResourceType(){ + return getUsageRecordType(); + } + + /** + * {@inheritDoc} + */ + @Deprecated + public void setResourceType(String resourceType){} + + /** + * {@inheritDoc} + */ + @Override + public String getResourceScope() { + return (String) this.resourceSpecificProperties.get(RESOURCE_SCOPE); + } + + /** + * {@inheritDoc} + */ + @Override + public void setResourceScope(String scope) throws InvalidValueException { + setResourceSpecificProperty(RESOURCE_SCOPE, scope); + } + + /** + * {@inheritDoc} + */ + @Override + public String getResourceOwner() { + return (String) this.resourceSpecificProperties.get(RESOURCE_OWNER); + } + + /** + * {@inheritDoc} + */ + @Override + public void setResourceOwner(String owner) throws InvalidValueException { + setResourceSpecificProperty(RESOURCE_OWNER, owner); + } + + /** + * {@inheritDoc} + */ + @Override + public String getAggregatedId() { + return (String) this.resourceSpecificProperties.get(AGGREGATED_ID); + } + + /** + * {@inheritDoc} + */ + @Override + public void setAggregatedId(String aggregatedId) throws InvalidValueException { + setResourceSpecificProperty(AGGREGATED_ID, aggregatedId); + } + + /** + * {@inheritDoc} + */ + @Override + public Map getResourceSpecificProperties() { + return this.resourceSpecificProperties; + } + + /** + * {@inheritDoc} + */ + @Override + public void setResourceSpecificProperties(Map properties) throws InvalidValueException { + validate(properties); + this.resourceSpecificProperties = new HashMap(properties); + } + + /** + * {@inheritDoc} + */ + @Override + public Serializable getResourceSpecificProperty(String key) { + return this.resourceSpecificProperties.get(key); + } + + /** + * {@inheritDoc} + */ + @Override + public void setResourceSpecificProperty(String key, Serializable value) throws InvalidValueException { + validateField(key, value); + this.resourceSpecificProperties.put(key, value); + + } + + protected void validateField(String key, Serializable serializable) throws InvalidValueException { + List> fieldValidators = validation.get(key); + if(fieldValidators!=null){ + for(FieldValidator fieldValidator : fieldValidators){ + if(!fieldValidator.isValid(serializable)){ + throw new InvalidValueException(fieldValidator.getErrorSuffix()); + } + } + } + } + + protected void validate(Map properties) throws InvalidValueException{ + // TODO Change the behaviour. Get the list of validator and check the + // field in properties Map + for(String key : properties.keySet()){ + Serializable serializable = properties.get(key); + validateField(key, serializable); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void validate() throws InvalidValueException{ + validate(this.resourceSpecificProperties); + } + + @Override + public String toString(){ + /* + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("{"); + boolean first = true; + for(String key : this.resourceSpecificProperties.keySet()){ + if(!first){ + stringBuilder.append(", \""); + }else { + stringBuilder.append("\""); + first = false; + } + stringBuilder.append(key); + stringBuilder.append("\":\""); + stringBuilder.append(this.resourceSpecificProperties.get(key)); + stringBuilder.append("\""); + + } + stringBuilder.append("}"); + return stringBuilder.toString(); + */ + return resourceSpecificProperties.toString(); + } + + + /** + * {@inheritDoc} + */ + @Override + @Deprecated + public String getFullyQualifiedConsumerId() { + return getConsumerId(); + } + + + /** + * {@inheritDoc} + */ + @Override + @Deprecated + public void setFullyQualifiedConsumerId(String fqcid) { } + + /** + * Compare this UsageRecord instance with the one provided as argument + * @param usageRecord the Usage Record to compare + * @return 0 is and only if the UsageRecord provided as parameter + * contains all and ONLY the parameters contained in this instance. + * If the number of parameters differs, the methods return the difference + * between the number of parameter in this instance and the ones in the + * UsageRecord provided as parameter. + * If the size is the same but the UsageRecord provided as parameter does + * not contains all parameters in this instance, -1 is returned. + */ + @Override + public int compareTo(UsageRecord usageRecord) { + Set> thisSet = this.resourceSpecificProperties.entrySet(); + Set> usageRecordSet = usageRecord.getResourceSpecificProperties().entrySet(); + if(thisSet.size() != usageRecordSet.size()){ + return thisSet.size() - usageRecordSet.size(); + } + if(usageRecordSet.containsAll(thisSet)){ + return 0; + } + return 1; + } + +} diff --git a/src/main/java/org/gcube/accounting/datamodel/UsageRecord.java b/src/main/java/org/gcube/accounting/datamodel/UsageRecord.java new file mode 100644 index 0000000..22e65e1 --- /dev/null +++ b/src/main/java/org/gcube/accounting/datamodel/UsageRecord.java @@ -0,0 +1,228 @@ +package org.gcube.accounting.datamodel; + +import java.io.Serializable; +import java.util.Calendar; +import java.util.Date; +import java.util.Map; + +import org.gcube.accounting.exception.InvalidValueException; + +public interface UsageRecord extends Comparable{ + + /** + * Return the unique id for this {#UsageRecord} + * @return {#UsageRecord} Unique ID + */ + public String getId(); + + /** + * Set the unique id for this {#UsageRecord} + * @param id {#UsageRecord} Unique ID + * @throws InvalidValueException + */ + public void setId(String id) throws InvalidValueException; + + /** + * Return the identity of the entity creating this {#UsageRecord} + * @return Creator ID + */ + public String getCreatorId(); + + /** + * Set the identity of the entity creating this {#UsageRecord} + * @param creatorId Creator ID + * @throws InvalidValueException + */ + public void setCreatorId(String creatorId) throws InvalidValueException; + + + /** + * Return the identity of the entity that consumed the resource + * @return Consumer ID + */ + public String getConsumerId(); + + /** + * Set the identity of the entity that consumed the resource + * @param consumerId Consumer ID + * @throws InvalidValueException + */ + public void setConsumerId(String consumerId) throws InvalidValueException; + + /** + * Return the creation time for this {#UsageRecord} + * @return the creation time for this {#UsageRecord} + */ + public Calendar getCreationTime(); + + /** + * Set the creation time for this {#UsageRecord} + * @param creationTime creation time + * @throws InvalidValueException + */ + public void setCreationTime(Calendar creationTime) throws InvalidValueException; + + /** + * Use {{@link #setCreationTime(Calendar)}} instead + * @param createTime + * @throws InvalidValueException + */ + @Deprecated + public void setCreateTime(Date createTime) throws InvalidValueException; + + /** + * Return the left end of the time interval covered by this {#UsageRecord} + * @return Start Time + */ + public Calendar getStartTime(); + + /** + * Set the left end of the time interval covered by this {#UsageRecord} + * @param startTime Start Time + * @throws InvalidValueException + */ + public void setStartTime(Calendar startTime) throws InvalidValueException; + + /** + * Use {{@link #setStartTime(Calendar)}} instead + * @param createTime + * @throws InvalidValueException + */ + @Deprecated + public void setStartTime(Date startTime) throws InvalidValueException; + + /** + * Return the right end of the time interval covered by this {#UsageRecord} + * @return End Time + */ + public Calendar getEndTime(); + + /** + * Set the right end of the time interval covered by this {#UsageRecord} + * @param endTime End Time + * @throws InvalidValueException + */ + public void setEndTime(Calendar endTime) throws InvalidValueException; + + /** + * Use {{@link #setEndTime(Calendar)}} instead + * @param createTime + * @throws InvalidValueException + */ + @Deprecated + public void setEndTime(Date endTime) throws InvalidValueException; + + /** + * Return the type of the {#UsageRecord}. + * It is a alternative way of obj.getClass().getSimpleName() + * @return Resource Type + */ + public String getUsageRecordType(); + + /** + * Use {#getUsageRecordType} instead + * @param resourceType + * @return Usage Record Type + */ + @Deprecated + public String getResourceType(); + + /** + * This method is not valid due to Resource Type is derived by + * the Usage Record Implementation class. The method is deprecated and + * the implementations in known classes is a NoOperation. + * @param resourceType + */ + @Deprecated + public void setResourceType(String resourceType); + + /** + * Return the accounting scope of the {#UsageRecord} + * @return The Accounting scope of the {#UsageRecord} + */ + public String getResourceScope(); + + /** + * Set the accounting scope of the {#UsageRecord} + * @param scope The accounting scope of the {#UsageRecord} + * @throws InvalidValueException + */ + public void setResourceScope(String scope) throws InvalidValueException; + + /** + * Return the identity id of the accounting owner + * @return The identity id of the accounting owner + */ + public String getResourceOwner(); + + /** + * Set the identity id of the accounting owner + * @param ownerID The identity id of the accounting owner + * @throws InvalidValueException + */ + public void setResourceOwner(String ownerID) throws InvalidValueException; + + /** + * Return the id of the usage record aggregating this + * @return Aggregated Id + */ + public String getAggregatedId(); + + /** + * Set the id of the usage record aggregating this + * @param aggregatedId + * @throws InvalidValueException + */ + public void setAggregatedId(String aggregatedId) throws InvalidValueException; + + + /** + * Return all resource-specific properties + * @return + */ + public Map getResourceSpecificProperties(); + + /** + * Set all resource-specific properties, replacing existing ones + */ + public void setResourceSpecificProperties(Map resourceSpecificProperties) throws InvalidValueException; + + /** + * Return the value of the given resource-specific property + * @param key + * @return the value of the given resource-specific property + */ + public Serializable getResourceSpecificProperty(String key); + + /** + * Set the value of the given resource-specific property + * @param key + * @param value + */ + public void setResourceSpecificProperty(String key, Serializable value) throws InvalidValueException; + + /** + * The method is deprecated and the implementations in known classes + * return Consumer ID + * @return Consumer ID + */ + @Deprecated + public String getFullyQualifiedConsumerId(); + + + /** + * The method is deprecated and the implementations in known classes is + * a NoOperation. + * @param fqcid Fully Qualified Consumer Id + */ + @Deprecated + public void setFullyQualifiedConsumerId(String fqcid); + + + /** + * Validate the Resource Record + * @throws InvalidValueException + */ + public void validate() throws InvalidValueException; + +} \ No newline at end of file diff --git a/src/main/java/org/gcube/accounting/datamodel/implementations/JobUsageRecord.java b/src/main/java/org/gcube/accounting/datamodel/implementations/JobUsageRecord.java new file mode 100644 index 0000000..e77e97e --- /dev/null +++ b/src/main/java/org/gcube/accounting/datamodel/implementations/JobUsageRecord.java @@ -0,0 +1,41 @@ +/** + * + */ +package org.gcube.accounting.datamodel.implementations; + +import org.gcube.accounting.datamodel.RawUsageRecord; +import org.gcube.common.validator.annotations.NotEmpty; + +/** + * @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/ + * + */ +public class JobUsageRecord extends RawUsageRecord { + + /** + * Generated Serial Version UID + */ + private static final long serialVersionUID = -8648691183939346858L; + + @NotEmpty + public static final String JOB_ID = "jobId"; + @NotEmpty + public static final String JOB_QUALIFIER = "jobQualifier"; + @NotEmpty + public static final String JOB_NAME = "jobName"; + @NotEmpty + public static final String JOB_START = "jobStart"; + @NotEmpty + public static final String JOB_END = "jobEnd"; + @NotEmpty + public static final String JOB_STATUS = "jobStatus"; + @NotEmpty + public static final String VMS_USED = "vmsUsed"; + @NotEmpty + public static final String WALL_DURATION = "wallDuration"; + + public JobUsageRecord(){ + super(); + } + +} diff --git a/src/main/java/org/gcube/accounting/datamodel/implementations/PortletUsageRecord.java b/src/main/java/org/gcube/accounting/datamodel/implementations/PortletUsageRecord.java new file mode 100644 index 0000000..2d3ee31 --- /dev/null +++ b/src/main/java/org/gcube/accounting/datamodel/implementations/PortletUsageRecord.java @@ -0,0 +1,29 @@ +/** + * + */ +package org.gcube.accounting.datamodel.implementations; + +import org.gcube.accounting.datamodel.RawUsageRecord; +import org.gcube.common.validator.annotations.NotEmpty; + +/** + * @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/ + * + */ +public class PortletUsageRecord extends RawUsageRecord { + + /** + * Generated Serial Version UID + */ + private static final long serialVersionUID = 8339324883678974869L; + + @NotEmpty + public static final String USER = "user"; + @NotEmpty + public static final String OPERATION_ID = "operationId"; + + public PortletUsageRecord(){ + super(); + } + +} diff --git a/src/main/java/org/gcube/accounting/datamodel/implementations/ServiceUsageRecord.java b/src/main/java/org/gcube/accounting/datamodel/implementations/ServiceUsageRecord.java new file mode 100644 index 0000000..e6be7db --- /dev/null +++ b/src/main/java/org/gcube/accounting/datamodel/implementations/ServiceUsageRecord.java @@ -0,0 +1,42 @@ +/** + * + */ +package org.gcube.accounting.datamodel.implementations; + +import org.gcube.accounting.datamodel.RawUsageRecord; +import org.gcube.common.validator.annotations.NotEmpty; + +/** + * @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/ + * + */ +public class ServiceUsageRecord extends RawUsageRecord { + + /** + * Generated Serial Version UID + */ + private static final long serialVersionUID = -4214891294699473587L; + + @NotEmpty + public static final String CALLER_IP = "callerIP"; + @NotEmpty + public static final String CALLER_SCOPE = "callerScope"; + @NotEmpty + public static final String REF_HOST = "refHost"; + @NotEmpty + public static final String REF_VM = "refVM"; + @NotEmpty + public static final String DOMAIN = "domain"; + @NotEmpty + public static final String INVOCATION_COUNT = "invocationCount"; + @NotEmpty + public static final String AVERAGE_INVOCATION_COUNT = "averageInvocationTime"; + @NotEmpty + public static final String SERVICE_CLASS = "serviceClass"; + @NotEmpty + public static final String SERVICE_NAME = "serviceName"; + + public ServiceUsageRecord(){ + super(); + } +} diff --git a/src/main/java/org/gcube/accounting/datamodel/implementations/StorageStatusUsageRecord.java b/src/main/java/org/gcube/accounting/datamodel/implementations/StorageStatusUsageRecord.java new file mode 100644 index 0000000..96c8176 --- /dev/null +++ b/src/main/java/org/gcube/accounting/datamodel/implementations/StorageStatusUsageRecord.java @@ -0,0 +1,34 @@ +/** + * + */ +package org.gcube.accounting.datamodel.implementations; + +import org.gcube.accounting.datamodel.RawUsageRecord; +import org.gcube.common.validator.annotations.NotEmpty; + +/** + * @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/ + * + */ +public class StorageStatusUsageRecord extends RawUsageRecord { + + /** + * Generated Serial Version UID + */ + private static final long serialVersionUID = 2456314684466092685L; + + @NotEmpty + public static final String PROVIDER_ID = "providerId"; + @NotEmpty + public static final String QUALIFIER = "qualifier"; + @NotEmpty + public static final String DATA_TYPE = "dataType"; + @NotEmpty + public static final String DATA_VOLUME = "dataVolume"; + @NotEmpty + public static final String DATA_COUNT = "dataCount"; + + public StorageStatusUsageRecord(){ + super(); + } +} diff --git a/src/main/java/org/gcube/accounting/datamodel/implementations/StorageUsageRecord.java b/src/main/java/org/gcube/accounting/datamodel/implementations/StorageUsageRecord.java new file mode 100644 index 0000000..95e6dda --- /dev/null +++ b/src/main/java/org/gcube/accounting/datamodel/implementations/StorageUsageRecord.java @@ -0,0 +1,41 @@ +/** + * + */ +package org.gcube.accounting.datamodel.implementations; + +import org.gcube.accounting.datamodel.RawUsageRecord; +import org.gcube.common.validator.annotations.NotEmpty; + +/** + * @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/ + * + */ +public class StorageUsageRecord extends RawUsageRecord { + + /** + * Generated Serial Version UID + */ + private static final long serialVersionUID = 1381025822586583326L; + + @NotEmpty + public static final String PROVIDER_ID = "providerId"; + @NotEmpty + public static final String OBJECT_URI = "objectURI"; + @NotEmpty + public static final String OPERATION_TYPE = "operationType"; + @NotEmpty + public static final String QUALIFIER = "qualifier"; + @NotEmpty + public static final String DATA_TYPE = "dataType"; + @NotEmpty + public static final String DATA_VOLUME = "dataVolume"; + @NotEmpty + public static final String DATA_COUNT = "dataCount"; + @NotEmpty + public static final String CALLER_IP = "callerIP"; + + + public StorageUsageRecord(){ + super(); + } +} diff --git a/src/main/java/org/gcube/accounting/datamodel/implementations/TaskUsageRecord.java b/src/main/java/org/gcube/accounting/datamodel/implementations/TaskUsageRecord.java new file mode 100644 index 0000000..70300c1 --- /dev/null +++ b/src/main/java/org/gcube/accounting/datamodel/implementations/TaskUsageRecord.java @@ -0,0 +1,56 @@ +/** + * + */ +package org.gcube.accounting.datamodel.implementations; + +import org.gcube.accounting.datamodel.RawUsageRecord; +import org.gcube.common.validator.annotations.NotEmpty; + +/** + * @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/ + * + */ +public class TaskUsageRecord extends RawUsageRecord { + + /** + * Generated Serial Version UID + */ + private static final long serialVersionUID = -2208425042550641240L; + + @NotEmpty + public static final String JOB_ID = "jobId"; + @NotEmpty + public static final String REF_HOST = "refHost"; + @NotEmpty + public static final String REF_VM = "refVM"; + @NotEmpty + public static final String DOMAIN = "domain"; + @NotEmpty + public static final String USAGE_START = "usageStart"; + @NotEmpty + public static final String USAGE_END = "usageEnd"; + @NotEmpty + public static final String USAGE_PHASE = "usagePhase"; + @NotEmpty + public static final String INPUT_FILES_NUMBER = "inputFilesNumber"; + @NotEmpty + public static final String INPUT_FILES_SIZE = "inputFilesSize"; + @NotEmpty + public static final String OUTPUT_FILES_NUMBER = "outputFilesNumber"; + @NotEmpty + public static final String OUTPUT_FILES_SIZE = "outputFilesSize"; + @NotEmpty + public static final String OVERALL_NETWORK_IN = "overallNetworkIn"; + @NotEmpty + public static final String OVERALL_NETWORK_OUT = "overallNetworkOut"; + @NotEmpty + public static final String CORES = "cores"; + @NotEmpty + public static final String PROCESSORS = "processors"; + + + public TaskUsageRecord(){ + super(); + } + +} diff --git a/src/main/java/org/gcube/accounting/datamodel/validators/NotEmptyIfNotNull.java b/src/main/java/org/gcube/accounting/datamodel/validators/NotEmptyIfNotNull.java new file mode 100644 index 0000000..74228a9 --- /dev/null +++ b/src/main/java/org/gcube/accounting/datamodel/validators/NotEmptyIfNotNull.java @@ -0,0 +1,15 @@ +package org.gcube.accounting.datamodel.validators; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.gcube.common.validator.annotations.ValidityChecker; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@ValidityChecker(managed=NotEmptyIfNotNullValidator.class) +public @interface NotEmptyIfNotNull { + +} diff --git a/src/main/java/org/gcube/accounting/datamodel/validators/NotEmptyIfNotNullValidator.java b/src/main/java/org/gcube/accounting/datamodel/validators/NotEmptyIfNotNullValidator.java new file mode 100644 index 0000000..eb462c5 --- /dev/null +++ b/src/main/java/org/gcube/accounting/datamodel/validators/NotEmptyIfNotNullValidator.java @@ -0,0 +1,30 @@ +package org.gcube.accounting.datamodel.validators; + +import java.util.Map; +import org.gcube.common.validator.annotations.FieldValidator; +import org.gcube.common.validator.annotations.NotEmpty; + +public class NotEmptyIfNotNullValidator implements FieldValidator{ + + public Class annotation() { + return NotEmpty.class; + } + + public boolean isValid(Object toValidate) { + if (toValidate == null) return true; + if (toValidate.getClass().isArray() ){ + return ((Object[])toValidate).length>0; + }else if ( toValidate instanceof Iterable){ + return ((Iterable) toValidate).iterator().hasNext(); + } else if (toValidate instanceof Map){ + return ((Map) toValidate).size()>0; + } else if (toValidate instanceof String ){ + return !((String)toValidate).isEmpty(); + } else return true; + } + + public String getErrorSuffix() { + return "is empty"; + } + +} diff --git a/src/main/java/org/gcube/accounting/datamodel/validators/UsageRecordType.java b/src/main/java/org/gcube/accounting/datamodel/validators/UsageRecordType.java new file mode 100644 index 0000000..1b89308 --- /dev/null +++ b/src/main/java/org/gcube/accounting/datamodel/validators/UsageRecordType.java @@ -0,0 +1,15 @@ +package org.gcube.accounting.datamodel.validators; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.gcube.common.validator.annotations.ValidityChecker; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@ValidityChecker(managed=UsageRecordTypeValidator.class) +public @interface UsageRecordType { + +} diff --git a/src/main/java/org/gcube/accounting/datamodel/validators/UsageRecordTypeValidator.java b/src/main/java/org/gcube/accounting/datamodel/validators/UsageRecordTypeValidator.java new file mode 100644 index 0000000..271eb4c --- /dev/null +++ b/src/main/java/org/gcube/accounting/datamodel/validators/UsageRecordTypeValidator.java @@ -0,0 +1,23 @@ +package org.gcube.accounting.datamodel.validators; + +import org.gcube.accounting.datamodel.UsageRecord; +import org.gcube.common.validator.annotations.FieldValidator; + +public class UsageRecordTypeValidator implements FieldValidator{ + + public Class annotation() { + return UsageRecordType.class; + } + + public boolean isValid(Object toValidate) { + if (toValidate.getClass().isInstance(UsageRecord.class)) { + return true; + } + return false; + } + + public String getErrorSuffix() { + return String.format("not instace of %s", UsageRecord.class.getSimpleName()); + } + +} diff --git a/src/main/java/org/gcube/accounting/datamodel/validators/ValidTime.java b/src/main/java/org/gcube/accounting/datamodel/validators/ValidTime.java new file mode 100644 index 0000000..4f89d9e --- /dev/null +++ b/src/main/java/org/gcube/accounting/datamodel/validators/ValidTime.java @@ -0,0 +1,15 @@ +package org.gcube.accounting.datamodel.validators; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.gcube.common.validator.annotations.ValidityChecker; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@ValidityChecker(managed=ValidTimeValidator.class) +public @interface ValidTime { + +} diff --git a/src/main/java/org/gcube/accounting/datamodel/validators/ValidTimeValidator.java b/src/main/java/org/gcube/accounting/datamodel/validators/ValidTimeValidator.java new file mode 100644 index 0000000..0e7d980 --- /dev/null +++ b/src/main/java/org/gcube/accounting/datamodel/validators/ValidTimeValidator.java @@ -0,0 +1,23 @@ +package org.gcube.accounting.datamodel.validators; + +import org.gcube.accounting.datamodel.UsageRecord; +import org.gcube.common.validator.annotations.FieldValidator; + +public class ValidTimeValidator implements FieldValidator{ + + public Class annotation() { + return ValidTime.class; + } + + public boolean isValid(Object toValidate) { + if(toValidate instanceof Long){ + return true; + } + return false; + } + + public String getErrorSuffix() { + return String.format("not instace of %s", UsageRecord.class.getSimpleName()); + } + +} diff --git a/src/main/java/org/gcube/accounting/exception/InvalidValueException.java b/src/main/java/org/gcube/accounting/exception/InvalidValueException.java new file mode 100644 index 0000000..beecf9c --- /dev/null +++ b/src/main/java/org/gcube/accounting/exception/InvalidValueException.java @@ -0,0 +1,15 @@ +package org.gcube.accounting.exception; + +public class InvalidValueException extends Exception { + + private static final long serialVersionUID = 4403699127526286772L; + + public InvalidValueException() { + super(); + } + + public InvalidValueException(String message) { + super(message); + } + +} diff --git a/src/main/java/org/gcube/accounting/messaging/ResourceAccounting.java b/src/main/java/org/gcube/accounting/messaging/ResourceAccounting.java new file mode 100644 index 0000000..c59aa17 --- /dev/null +++ b/src/main/java/org/gcube/accounting/messaging/ResourceAccounting.java @@ -0,0 +1,26 @@ +package org.gcube.accounting.messaging; + +import org.gcube.accounting.datamodel.RawUsageRecord; +import org.gcube.accounting.persistence.Persistence; + +/** + * This class has been created for backward compatibility. + * Use {@link #Persistence} class instead + * @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/ + */ +@Deprecated +public class ResourceAccounting { + + protected Persistence persistence; + + @Deprecated + public ResourceAccounting() { + persistence = Persistence.getInstance(); + } + + @Deprecated + public void sendAccountingMessage(RawUsageRecord message){ + persistence.account(message); + } + +} diff --git a/src/main/java/org/gcube/accounting/messaging/ResourceAccountingFactory.java b/src/main/java/org/gcube/accounting/messaging/ResourceAccountingFactory.java new file mode 100644 index 0000000..81acf9e --- /dev/null +++ b/src/main/java/org/gcube/accounting/messaging/ResourceAccountingFactory.java @@ -0,0 +1,20 @@ +package org.gcube.accounting.messaging; + +/** + * This class has been created for backward compatibility. + * Use {@link #Persistence} class instead + * @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/ + */ +@Deprecated +public class ResourceAccountingFactory { + + private static ResourceAccounting resourceAccounting = null; + + @Deprecated + public static ResourceAccounting getResourceAccountingInstance() throws Exception { + if (resourceAccounting == null){ + resourceAccounting= new ResourceAccounting(); + } + return resourceAccounting; + } +} diff --git a/src/main/java/org/gcube/accounting/persistence/CouchDBPersistence.java b/src/main/java/org/gcube/accounting/persistence/CouchDBPersistence.java new file mode 100644 index 0000000..cef4b77 --- /dev/null +++ b/src/main/java/org/gcube/accounting/persistence/CouchDBPersistence.java @@ -0,0 +1,117 @@ +/** + * + */ +package org.gcube.accounting.persistence; + +import java.io.Serializable; +import java.util.Map; + +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.map.ObjectMapper; +import org.ektorp.CouchDbConnector; +import org.ektorp.CouchDbInstance; +import org.ektorp.ViewQuery; +import org.ektorp.ViewResult; +import org.ektorp.http.HttpClient; +import org.ektorp.http.StdHttpClient; +import org.ektorp.http.StdHttpClient.Builder; +import org.ektorp.impl.StdCouchDbConnector; +import org.ektorp.impl.StdCouchDbInstance; +import org.gcube.accounting.datamodel.RawUsageRecord; +import org.gcube.accounting.datamodel.UsageRecord; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/ + * + */ +public class CouchDBPersistence extends Persistence { + + private static final Logger logger = LoggerFactory.getLogger(CouchDBPersistence.class); + + protected CouchDbInstance couchDbInstance; + protected CouchDbConnector couchDbConnector; + + protected final static String HOST = "HOST"; + protected final static String DEFAULT_HOST = "localhost"; + protected final static String PORT = "PORT"; + protected final static String USERNAME = "USERNAME"; + protected final static String PASSWORD = "PASSWORD"; + + protected String host = "localhost"; + protected int port = 5984; + protected String username = ""; + protected String password = ""; + protected String dbName = "accounting"; + + protected CouchDBPersistence() throws Exception { + super(); + } + + protected HttpClient initHttpClient(String uri, int port, String username, String password){ + Builder builder = new StdHttpClient.Builder().host(uri).port(port); + if(username!=null && username.compareTo("")!=0 && + password!=null && password.compareTo("")!=0){ + builder.username(username).password(password); + } + HttpClient httpClient = builder.build(); + return httpClient; + } + + protected ViewResult query(ViewQuery query){ + ViewResult result = couchDbConnector.queryView(query); + return result; + } + + + @Override + public void close() throws Exception { + couchDbConnector.getConnection().shutdown(); + } + + /** + * {@inheritDoc} + */ + @Override + protected void prepareConnection() throws Exception { + logger.debug("Preparing Connection for {}", this.getClass().getSimpleName()); + HttpClient httpClient = initHttpClient(host, port, username, password); + couchDbInstance = new StdCouchDbInstance(httpClient); + couchDbConnector = new StdCouchDbConnector(dbName, couchDbInstance); + // TODO remove this + couchDbConnector.createDatabaseIfNotExists(); + } + + protected void createItem(JsonNode node, String id) throws Exception { + if(id!=null && id.compareTo("")!=0){ + couchDbConnector.create(id, node); + }else{ + couchDbConnector.create(node); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void reallyAccount(UsageRecord usageRecord) throws Exception { + JsonNode node = usageRecordToJsonNode(usageRecord); + createItem(node, usageRecord.getId()); + } + + public static JsonNode usageRecordToJsonNode(UsageRecord usageRecord) throws Exception { + ObjectMapper mapper = new ObjectMapper(); + JsonNode node = mapper.valueToTree(usageRecord.getResourceSpecificProperties()); + return node; + } + + public static UsageRecord jsonNodeToUsageRecord(JsonNode jsonNode) throws Exception { + ObjectMapper mapper = new ObjectMapper(); + @SuppressWarnings("unchecked") + Map result = mapper.convertValue(jsonNode, Map.class); + UsageRecord usageRecord = new RawUsageRecord(result); + return usageRecord; + } + +} diff --git a/src/main/java/org/gcube/accounting/persistence/FallbackPersistence.java b/src/main/java/org/gcube/accounting/persistence/FallbackPersistence.java new file mode 100644 index 0000000..7db444f --- /dev/null +++ b/src/main/java/org/gcube/accounting/persistence/FallbackPersistence.java @@ -0,0 +1,56 @@ +/** + * + */ +package org.gcube.accounting.persistence; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; + +import org.gcube.accounting.datamodel.UsageRecord; + +/** + * @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/ + */ +public class FallbackPersistence extends Persistence { + + private File accountingFallbackFile; + + protected FallbackPersistence(File accountingFallbackFile) { + super(); + this.accountingFallbackFile = accountingFallbackFile; + } + + /** + * {@inheritDoc} + */ + @Override + public void prepareConnection() { + // Nothing TO DO + } + + /** + * {@inheritDoc} + */ + @Override + protected void reallyAccount(UsageRecord usageRecord) throws Exception { + try(FileWriter fw = new FileWriter(accountingFallbackFile, true); + BufferedWriter bw = new BufferedWriter(fw); + PrintWriter out = new PrintWriter(bw)){ + out.println(usageRecord); + } catch( IOException e ){ + throw e; + } + } + + /** + * {@inheritDoc} + */ + @Override + public void close() throws Exception { + // Nothing TO DO + } + +} diff --git a/src/main/java/org/gcube/accounting/persistence/MongoDBPersistence.java b/src/main/java/org/gcube/accounting/persistence/MongoDBPersistence.java new file mode 100644 index 0000000..20928a5 --- /dev/null +++ b/src/main/java/org/gcube/accounting/persistence/MongoDBPersistence.java @@ -0,0 +1,17 @@ +/** + * + */ +package org.gcube.accounting.persistence; + +/** + * @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/ + * + */ +public class MongoDBPersistence { + + protected MongoDBPersistence(){ + super(); + } + + +} diff --git a/src/main/java/org/gcube/accounting/persistence/Persistence.java b/src/main/java/org/gcube/accounting/persistence/Persistence.java new file mode 100644 index 0000000..2438fb8 --- /dev/null +++ b/src/main/java/org/gcube/accounting/persistence/Persistence.java @@ -0,0 +1,188 @@ +/** + * + */ +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 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; + +} diff --git a/src/test/java/org/gcube/accounting/datamodel/RawUsageRecordTest.java b/src/test/java/org/gcube/accounting/datamodel/RawUsageRecordTest.java new file mode 100644 index 0000000..5829a2b --- /dev/null +++ b/src/test/java/org/gcube/accounting/datamodel/RawUsageRecordTest.java @@ -0,0 +1,61 @@ +/** + * + */ +package org.gcube.accounting.datamodel; + +import org.gcube.accounting.persistence.Persistence; +import org.junit.Assert; +import org.junit.Test; + +/** + * @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/ + * + */ +public class RawUsageRecordTest { + + @Test + public void testCompareToSameObject(){ + UsageRecord usageRecord = Persistence.createTestRawUsageRecord(); + UsageRecord ur = usageRecord; + Assert.assertEquals(0, usageRecord.compareTo(ur)); + Assert.assertEquals(0, ur.compareTo(usageRecord)); + } + + @Test + public void testCompareToEqualsObject() throws Exception { + UsageRecord usageRecord = Persistence.createTestRawUsageRecord(); + UsageRecord ur = new RawUsageRecord(usageRecord.getResourceSpecificProperties()); + Assert.assertEquals(0, usageRecord.compareTo(ur)); + Assert.assertEquals(0, ur.compareTo(usageRecord)); + } + + @Test + public void testCompareToComparedAddedProperty() throws Exception { + UsageRecord usageRecord = Persistence.createTestRawUsageRecord(); + UsageRecord ur = new RawUsageRecord(usageRecord.getResourceSpecificProperties()); + for(int i=1; i<31; i++){ + ur.setResourceSpecificProperty(Integer.toString(i), i); + Assert.assertEquals(-i, usageRecord.compareTo(ur)); + Assert.assertEquals(i, ur.compareTo(usageRecord)); + } + } + + @Test + public void testCompareToDifferentForAddedProperties() throws Exception { + UsageRecord usageRecord = Persistence.createTestRawUsageRecord(); + UsageRecord ur = new RawUsageRecord(usageRecord.getResourceSpecificProperties()); + usageRecord.setResourceSpecificProperty(Integer.toString(1), 2); + ur.setResourceSpecificProperty(Integer.toString(2), 2); + Assert.assertEquals(1, usageRecord.compareTo(ur)); + Assert.assertEquals(1, ur.compareTo(usageRecord)); + } + + @Test + public void testCompareToDifferentFromCreation() throws Exception { + UsageRecord usageRecord = Persistence.createTestRawUsageRecord(); + UsageRecord ur = Persistence.createTestRawUsageRecord(); + Assert.assertEquals(1, usageRecord.compareTo(ur)); + Assert.assertEquals(1, ur.compareTo(usageRecord)); + } + +} diff --git a/src/test/java/org/gcube/accounting/datamodel/persistence/CouchDBPersistenceTest.java b/src/test/java/org/gcube/accounting/datamodel/persistence/CouchDBPersistenceTest.java new file mode 100644 index 0000000..580f396 --- /dev/null +++ b/src/test/java/org/gcube/accounting/datamodel/persistence/CouchDBPersistenceTest.java @@ -0,0 +1,33 @@ +/** + * + */ +package org.gcube.accounting.datamodel.persistence; + +import org.codehaus.jackson.JsonNode; +import org.gcube.accounting.datamodel.UsageRecord; +import org.gcube.accounting.persistence.CouchDBPersistence; +import org.gcube.accounting.persistence.Persistence; +import org.junit.Assert; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/ + * + */ +public class CouchDBPersistenceTest { + + private static final Logger logger = LoggerFactory.getLogger(CouchDBPersistenceTest.class); + + @Test + public void TestJsonNodeUsageRecordConversions() throws Exception { + UsageRecord usageRecord = Persistence.createTestRawUsageRecord(); + logger.debug("UsageRecord : {}", usageRecord.toString()); + JsonNode node = CouchDBPersistence.usageRecordToJsonNode(usageRecord); + logger.debug("Node : {}", node.toString()); + UsageRecord ur = CouchDBPersistence.jsonNodeToUsageRecord(node); + Assert.assertEquals(0, usageRecord.compareTo(ur)); + Assert.assertEquals(0, ur.compareTo(usageRecord)); + } +} diff --git a/src/test/java/org/gcube/accounting/datamodel/persistence/PersistenceTest.java b/src/test/java/org/gcube/accounting/datamodel/persistence/PersistenceTest.java new file mode 100644 index 0000000..9b4a55e --- /dev/null +++ b/src/test/java/org/gcube/accounting/datamodel/persistence/PersistenceTest.java @@ -0,0 +1,48 @@ +/** + * + */ +package org.gcube.accounting.datamodel.persistence; + +import java.util.Calendar; +import java.util.GregorianCalendar; + +import org.gcube.accounting.datamodel.UsageRecord; +import org.gcube.accounting.persistence.Persistence; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/ + * + */ +public class PersistenceTest { + + private static final Logger logger = LoggerFactory.getLogger(PersistenceTest.class); + + @Test + public void test() throws Exception { + Persistence persistence = Persistence.getInstance(); + persistence.connect(); + } + + @Test + public void stressTest() throws Exception { + Persistence persistence = Persistence.getInstance(); + persistence.connect(); + int quantity = 3000; + Calendar startTestTime = new GregorianCalendar(); + for(int i=0; i< quantity; i++){ + UsageRecord usageRecord = Persistence.createTestRawUsageRecord(); + persistence.account(usageRecord); + } + Calendar stopTestTime = new GregorianCalendar(); + double startMillis = startTestTime.getTimeInMillis(); + double stopMillis = stopTestTime.getTimeInMillis(); + double duration = stopMillis - startMillis; + double average = (duration/quantity); + logger.debug("Duration (in millisec) : " + duration); + logger.debug("Average (in millisec) : " + average); + } + +} diff --git a/src/test/java/org/gcube/accounting/datamodel/validators/ValidTimeValidatorTest.java b/src/test/java/org/gcube/accounting/datamodel/validators/ValidTimeValidatorTest.java new file mode 100644 index 0000000..28368b0 --- /dev/null +++ b/src/test/java/org/gcube/accounting/datamodel/validators/ValidTimeValidatorTest.java @@ -0,0 +1,28 @@ +/** + * + */ +package org.gcube.accounting.datamodel.validators; + +import org.junit.Test; + +/** + * @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/ + * + */ +public class ValidTimeValidatorTest { + + @Test + public void testPrimitiveLong(){ + ValidTimeValidator validTimeValidator = new ValidTimeValidator(); + long myLong = 4; + validTimeValidator.isValid(myLong); + } + + @Test + public void testClassLong(){ + ValidTimeValidator validTimeValidator = new ValidTimeValidator(); + Long myLong = new Long(4); + validTimeValidator.isValid(myLong); + } + +}