2015-06-10 17:36:50 +02:00
/ * *
*
* /
package org.gcube.accounting.datamodel ;
import java.io.Serializable ;
import java.lang.annotation.Annotation ;
import java.lang.reflect.Constructor ;
import java.lang.reflect.Field ;
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.Calendar ;
import java.util.HashMap ;
import java.util.HashSet ;
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.decorators.AggregatedField ;
import org.gcube.accounting.datamodel.decorators.ComputedField ;
import org.gcube.accounting.datamodel.decorators.FieldAction ;
import org.gcube.accounting.datamodel.decorators.FieldDecorator ;
import org.gcube.accounting.datamodel.decorators.RequiredField ;
import org.gcube.accounting.datamodel.validations.annotations.NotEmpty ;
import org.gcube.accounting.datamodel.validations.annotations.NotEmptyIfNotNull ;
2015-06-26 15:50:39 +02:00
import org.gcube.accounting.datamodel.validations.annotations.ValidInteger ;
2015-06-10 17:36:50 +02:00
import org.gcube.accounting.datamodel.validations.annotations.ValidLong ;
import org.gcube.accounting.datamodel.validations.annotations.ValidOperationResult ;
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 BasicUsageRecord implements UsageRecord , Serializable {
/ * *
* Generated Serial Version UID
* /
private static final long serialVersionUID = - 2060728578456796388L ;
private static Logger logger = LoggerFactory . getLogger ( BasicUsageRecord . class ) ;
2015-06-25 15:58:26 +02:00
/ * *
2015-06-25 17:41:20 +02:00
* KEY for : The unique identifier for the UsageRecord .
* The ID is automatically Created . Set the ID only if you really know what
* you are going to do .
2015-06-25 15:58:26 +02:00
* /
2015-06-10 17:36:50 +02:00
@RequiredField @NotEmpty
2015-06-25 17:41:20 +02:00
protected static final String ID = " id " ;
2015-06-25 15:58:26 +02:00
/ * *
2015-06-25 17:41:20 +02:00
* KEY for : The user ( or the Consumer Identity , that in the S2S
* communication is another service ) .
2015-06-25 15:58:26 +02:00
* /
2015-06-10 17:36:50 +02:00
@RequiredField @NotEmpty
public static final String CONSUMER_ID = " consumerId " ;
2015-06-25 15:58:26 +02:00
/ * *
2015-06-25 17:41:20 +02:00
* KEY for : The instant when the UR was created . The value will be recorded
* in UTC milliseconds from the epoch .
2015-06-25 15:58:26 +02:00
* /
2015-06-10 17:36:50 +02:00
@RequiredField @ValidLong
public static final String CREATION_TIME = " creationTime " ;
2015-06-25 15:58:26 +02:00
2015-06-25 17:41:20 +02:00
/ * *
* Internal USE ONLY .
* KEY for : The Class Name of the represented { # Usage Record }
* /
2015-06-23 17:47:38 +02:00
@RequiredField @NotEmpty
protected static final String USAGE_RECORD_TYPE = " usageRecordType " ;
2015-06-25 17:41:20 +02:00
/ * *
* KEY for : The accounting scope
* /
2015-06-23 17:47:38 +02:00
@RequiredField @NotEmpty
public static final String SCOPE = " scope " ;
2015-06-25 16:41:13 +02:00
2015-06-25 17:41:20 +02:00
/ * *
* KEY for : The Operation Result of the accounted operation .
* The value is expressed as
* { @link # org . gcube . accounting . datamodel . UsageRecord . OperationResult }
* /
2015-06-23 17:47:38 +02:00
@RequiredField @ValidOperationResult
public static final String OPERATION_RESULT = " operationResult " ;
2015-06-24 17:51:46 +02:00
2015-06-23 17:47:38 +02:00
2015-06-24 17:51:46 +02:00
/** resource-specific properties */
protected Map < String , Serializable > resourceProperties ;
protected Map < String , List < FieldAction > > validation ;
protected Set < String > requiredFields ;
2015-07-09 15:34:31 +02:00
2015-07-01 16:40:49 +02:00
/ * *
2015-07-09 15:34:31 +02:00
* { @inheritDoc }
2015-07-01 16:40:49 +02:00
* /
2015-07-09 15:34:31 +02:00
@Override
2015-07-01 16:40:49 +02:00
public Set < String > getRequiredFields ( ) {
return requiredFields ;
}
2015-06-24 17:51:46 +02:00
protected Set < String > computedFields ;
2015-06-10 17:36:50 +02:00
2015-06-25 17:41:20 +02:00
/ * *
* KEY for : Used in aggregated record . Represent the left end of the time
* interval covered by this { # Usage Record }
* The value will be recorded in UTC milliseconds from the epoch .
* /
2015-06-10 17:36:50 +02:00
@AggregatedField @ValidLong
protected static final String START_TIME = " startTime " ;
2015-06-25 17:41:20 +02:00
/ * *
* KEY for : Used in aggregated record . Represent the right end of the time
* interval covered by this { # Usage Record }
* The value will be recorded in UTC milliseconds from the epoch .
* /
2015-06-10 17:36:50 +02:00
@AggregatedField @ValidLong
protected static final String END_TIME = " endTime " ;
2015-06-25 17:41:20 +02:00
/ * *
* Internal USE ONLY .
* KEY for : Indicate that the record is an aggregation
* /
2015-06-10 17:36:50 +02:00
@AggregatedField @NotEmptyIfNotNull
protected static final String AGGREGATED = " aggregated " ;
2015-07-17 10:54:14 +02:00
2015-06-26 15:50:39 +02:00
/ * *
* KEY for : Indicate The Number of Aggregated Operation
* /
@AggregatedField @ValidInteger
protected static final String OPERATION_COUNT = " operationCount " ;
2015-06-10 17:36:50 +02:00
protected Set < String > aggregatedFields ;
2015-06-24 17:51:46 +02:00
2015-07-02 11:00:10 +02:00
protected static Set < Field > getAllFields ( Class < ? > type ) {
Set < Field > fields = new HashSet < Field > ( ) ;
for ( Class < ? > c = type ; c ! = null ; c = c . getSuperclass ( ) )
fields . addAll ( Arrays . asList ( c . getDeclaredFields ( ) ) ) ;
return fields ;
}
2015-06-10 17:36:50 +02:00
protected void initializeValidation ( ) {
2015-07-13 12:55:57 +02:00
//logger.trace("Initializing Field Validators");
2015-07-02 11:00:10 +02:00
Set < Field > fields = getAllFields ( this . getClass ( ) ) ;
2015-07-01 18:09:42 +02:00
2015-06-10 17:36:50 +02:00
for ( Field field : fields ) {
2015-07-02 11:20:36 +02:00
boolean defaultAccessibility = field . isAccessible ( ) ;
2015-07-02 11:00:10 +02:00
field . setAccessible ( true ) ;
2015-06-10 17:36:50 +02:00
String keyString ;
try {
keyString = ( String ) field . get ( null ) ;
} catch ( Exception e ) {
continue ;
}
List < FieldAction > fieldValidators = new ArrayList < FieldAction > ( ) ;
validation . put ( keyString , fieldValidators ) ;
for ( Annotation annotation : field . getAnnotations ( ) ) {
if ( annotation . annotationType ( ) . isAnnotationPresent ( FieldDecorator . class ) ) {
Class < ? extends FieldAction > managedClass = ( ( FieldDecorator ) annotation . annotationType ( ) . getAnnotation ( FieldDecorator . class ) ) . managed ( ) ;
FieldAction validator ;
try {
validator = managedClass . newInstance ( ) ;
} catch ( InstantiationException | IllegalAccessException e ) {
continue ;
}
fieldValidators . add ( validator ) ;
}
if ( annotation . annotationType ( ) . isAssignableFrom ( RequiredField . class ) ) {
requiredFields . add ( keyString ) ;
}
if ( annotation . annotationType ( ) . isAssignableFrom ( AggregatedField . class ) ) {
aggregatedFields . add ( keyString ) ;
}
if ( annotation . annotationType ( ) . isAssignableFrom ( ComputedField . class ) ) {
computedFields . add ( keyString ) ;
}
}
2015-07-02 11:20:36 +02:00
field . setAccessible ( defaultAccessibility ) ;
2015-06-10 17:36:50 +02:00
}
2015-07-13 12:55:57 +02:00
/ *
2015-07-02 11:00:10 +02:00
logger . trace ( " Required Fields {} " , requiredFields ) ;
logger . trace ( " Aggregated Fields {} " , aggregatedFields ) ;
logger . trace ( " Computed Fields {} " , computedFields ) ;
2015-07-13 12:55:57 +02:00
* /
2015-06-10 17:36:50 +02:00
}
/ * *
* Initialize variable
* /
2015-06-26 15:50:39 +02:00
private void init ( ) {
2015-06-10 17:36:50 +02:00
this . validation = new HashMap < String , List < FieldAction > > ( ) ;
this . requiredFields = new HashSet < String > ( ) ;
2015-06-15 17:01:56 +02:00
this . aggregatedFields = new HashSet < String > ( ) ;
this . computedFields = new HashSet < String > ( ) ;
2015-06-10 17:36:50 +02:00
initializeValidation ( ) ;
}
public BasicUsageRecord ( ) {
init ( ) ;
this . resourceProperties = new HashMap < String , Serializable > ( ) ;
this . resourceProperties . put ( ID , UUID . randomUUID ( ) . toString ( ) ) ;
this . resourceProperties . put ( USAGE_RECORD_TYPE , this . getClass ( ) . getSimpleName ( ) ) ;
Calendar calendar = Calendar . getInstance ( ) ;
this . resourceProperties . put ( CREATION_TIME , calendar . getTimeInMillis ( ) ) ;
}
public BasicUsageRecord ( Map < String , Serializable > properties ) throws InvalidValueException {
init ( ) ;
setResourceProperties ( properties ) ;
}
/ * *
* { @inheritDoc }
* /
@Override
public String getId ( ) {
return ( String ) this . resourceProperties . get ( ID ) ;
}
/ * *
* { @inheritDoc }
* /
@Override
public void setId ( String id ) throws InvalidValueException {
setResourceProperty ( ID , id ) ;
}
/ * *
* { @inheritDoc }
* /
@Override
public String getConsumerId ( ) {
return ( String ) this . resourceProperties . get ( CONSUMER_ID ) ;
}
/ * *
* { @inheritDoc }
* /
@Override
public void setConsumerId ( String consumerId ) throws InvalidValueException {
setResourceProperty ( 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 . resourceProperties . get ( CREATION_TIME ) ;
return timestampStringToCalendar ( millis ) ;
}
/ * *
* { @inheritDoc }
* /
@Override
public void setCreationTime ( Calendar creationTime ) throws InvalidValueException {
setResourceProperty ( CREATION_TIME , creationTime . getTimeInMillis ( ) ) ;
}
2015-06-26 15:50:39 +02:00
/ * *
* { @inheritDoc }
* /
@Override
public String getScope ( ) {
return ( String ) this . resourceProperties . get ( SCOPE ) ;
}
/ * *
* { @inheritDoc }
* /
@Override
public void setScope ( String scope ) throws InvalidValueException {
setResourceProperty ( SCOPE , scope ) ;
}
/ * *
* { @inheritDoc }
* /
@Override
public Map < String , Serializable > getResourceProperties ( ) {
return new HashMap < String , Serializable > ( this . resourceProperties ) ;
}
/ * *
* { @inheritDoc }
* /
@Override
public void setResourceProperties ( Map < String , Serializable > properties ) throws InvalidValueException {
2015-07-08 11:15:54 +02:00
Map < String , Serializable > validated = validateProperties ( properties ) ;
this . resourceProperties = new HashMap < String , Serializable > ( validated ) ;
2015-06-26 15:50:39 +02:00
}
/ * *
* { @inheritDoc }
* /
@Override
public Serializable getResourceProperty ( String key ) {
return this . resourceProperties . get ( key ) ;
}
/ * *
* { @inheritDoc }
* /
@Override
public void setResourceProperty ( String key , Serializable value ) throws InvalidValueException {
Serializable checkedValue = validateField ( key , value ) ;
if ( checkedValue = = null ) {
this . resourceProperties . remove ( key ) ;
} else {
this . resourceProperties . put ( key , checkedValue ) ;
}
}
// AGGREGATION
/* --------------------------------------- */
2015-06-24 17:51:46 +02:00
/ * *
* Return the left end of the time interval covered by this { # UsageRecord }
* @return Start Time
* /
protected long getStartTimeInMillis ( ) {
return ( Long ) this . resourceProperties . get ( START_TIME ) ;
}
2015-06-10 17:36:50 +02:00
/ * *
2015-06-15 11:28:19 +02:00
* Return the left end of the time interval covered by this { # UsageRecord }
* @return Start Time
2015-06-10 17:36:50 +02:00
* /
2015-06-15 11:28:19 +02:00
protected Calendar getStartTimeAsCalendar ( ) {
2015-06-24 17:51:46 +02:00
long millis = getStartTimeInMillis ( ) ;
2015-06-10 17:36:50 +02:00
return timestampStringToCalendar ( millis ) ;
}
2015-06-15 11:28:19 +02:00
2015-06-10 17:36:50 +02:00
/ * *
* Set the left end of the time interval covered by this { # UsageRecord }
* @param startTime Start Time
* @throws InvalidValueException
* /
protected void setStartTime ( Calendar startTime ) throws InvalidValueException {
setResourceProperty ( START_TIME , startTime . getTimeInMillis ( ) ) ;
}
2015-06-24 17:51:46 +02:00
/ * *
* Return the right end of the time interval covered by this { # UsageRecord }
* @return End Time
* /
protected long getEndTimeInMillis ( ) {
return ( Long ) this . resourceProperties . get ( END_TIME ) ;
}
2015-06-10 17:36:50 +02:00
/ * *
2015-06-15 11:28:19 +02:00
* Return the right end of the time interval covered by this { # UsageRecord }
* @return End Time
2015-06-10 17:36:50 +02:00
* /
2015-06-15 11:28:19 +02:00
protected Calendar getEndTimeAsCalendar ( ) {
2015-06-24 17:51:46 +02:00
long millis = getEndTimeInMillis ( ) ;
2015-06-10 17:36:50 +02:00
return timestampStringToCalendar ( millis ) ;
}
/ * *
* Set the right end of the time interval covered by this { # UsageRecord }
* @param endTime End Time
* @throws InvalidValueException
* /
protected void setEndTime ( Calendar endTime ) throws InvalidValueException {
setResourceProperty ( END_TIME , endTime . getTimeInMillis ( ) ) ;
}
2015-06-26 15:50:39 +02:00
protected int getOperationCount ( ) {
return ( Integer ) this . resourceProperties . get ( OPERATION_COUNT ) ;
2015-06-10 17:36:50 +02:00
}
2015-06-26 15:50:39 +02:00
protected void setOperationCount ( int operationCount ) throws InvalidValueException {
setResourceProperty ( OPERATION_COUNT , operationCount ) ;
2015-06-10 17:36:50 +02:00
}
2015-06-26 15:50:39 +02:00
/* --------------------------------------- */
2015-06-10 17:36:50 +02:00
protected Serializable validateField ( String key , Serializable serializable ) throws InvalidValueException {
if ( key = = null ) {
throw new InvalidValueException ( " The key of property to set cannot be null " ) ;
}
Serializable checkedValue = serializable ;
List < FieldAction > fieldValidators = validation . get ( key ) ;
if ( fieldValidators ! = null ) {
for ( FieldAction fieldValidator : fieldValidators ) {
if ( aggregatedFields . contains ( key ) ) {
// TODO
}
if ( computedFields . contains ( key ) ) {
logger . debug ( " {} is a computed field. To be calculated all the required fields to calcutalate it MUST be set. "
+ " In any case the provided value is ignored. " ) ;
}
2015-07-13 15:12:21 +02:00
try {
checkedValue = fieldValidator . validate ( key , checkedValue , this ) ;
} catch ( InvalidValueException e ) {
logger . error ( String . format ( " The provided value %s is NOT valid for field with key %s. " , checkedValue . toString ( ) , key ) ) ;
throw e ;
}
2015-06-10 17:36:50 +02:00
}
}
return checkedValue ;
}
2015-07-08 11:15:54 +02:00
protected Map < String , Serializable > validateProperties ( Map < String , Serializable > properties ) throws InvalidValueException {
Map < String , Serializable > validated = new HashMap < String , Serializable > ( ) ;
2015-06-10 17:36:50 +02:00
for ( String key : properties . keySet ( ) ) {
Serializable serializable = properties . get ( key ) ;
2015-07-08 11:15:54 +02:00
validated . put ( key , validateField ( key , serializable ) ) ;
2015-06-10 17:36:50 +02:00
}
2015-07-08 11:15:54 +02:00
return validated ;
2015-06-10 17:36:50 +02:00
}
/ * *
* { @inheritDoc }
* /
@Override
public void validate ( ) throws InvalidValueException {
validateProperties ( this . resourceProperties ) ;
Set < String > notPresentProperties = new HashSet < String > ( ) ;
for ( String key : this . requiredFields ) {
if ( ! this . resourceProperties . containsKey ( key ) ) {
notPresentProperties . add ( key ) ;
}
}
if ( ! notPresentProperties . isEmpty ( ) ) {
String pluralManagement = notPresentProperties . size ( ) = = 1 ? " y " : " ies " ;
throw new InvalidValueException ( String . format ( " The Usage Record does not contain the following required propert%s %s " , pluralManagement , notPresentProperties . toString ( ) ) ) ;
}
}
@Override
public String toString ( ) {
return resourceProperties . toString ( ) ;
}
/ * *
* { @inheritDoc }
* /
@Override
public OperationResult getOperationResult ( ) {
return OperationResult . valueOf ( ( String ) this . resourceProperties . get ( OPERATION_RESULT ) ) ;
}
/ * *
* { @inheritDoc }
* @throws InvalidValueException
* /
@Override
public void setOperationResult ( OperationResult operationResult ) throws InvalidValueException {
setResourceProperty ( OPERATION_RESULT , operationResult ) ;
}
/ * *
* 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 < Entry < String , Serializable > > thisSet = this . resourceProperties . entrySet ( ) ;
Set < Entry < String , Serializable > > usageRecordSet = usageRecord . getResourceProperties ( ) . entrySet ( ) ;
if ( thisSet . size ( ) ! = usageRecordSet . size ( ) ) {
return thisSet . size ( ) - usageRecordSet . size ( ) ;
}
if ( usageRecordSet . containsAll ( thisSet ) ) {
return 0 ;
}
return 1 ;
}
@SuppressWarnings ( " unchecked " )
protected static Class < ? extends UsageRecord > getClass ( String usageRecordName , boolean aggregated ) throws ClassNotFoundException {
Class < ? extends UsageRecord > clz = null ;
2015-07-01 12:50:46 +02:00
Class < ? extends UsageRecord > utilityClass = org . gcube . accounting . datamodel . usagerecords . JobUsageRecord . class ;
2015-06-10 17:36:50 +02:00
if ( aggregated ) {
2015-07-01 11:02:45 +02:00
utilityClass = org . gcube . accounting . aggregation . JobUsageRecord . class ;
2015-06-10 17:36:50 +02:00
}
String classCanonicalName = utilityClass . getCanonicalName ( ) ;
2015-07-02 14:32:49 +02:00
classCanonicalName = classCanonicalName . replace ( utilityClass . getSimpleName ( ) , usageRecordName ) ;
2015-06-10 17:36:50 +02:00
try {
clz = ( Class < ? extends UsageRecord > ) Class . forName ( classCanonicalName ) ;
} catch ( ClassNotFoundException e ) {
logger . error ( " Unable to retrieve class {} " , classCanonicalName ) ;
throw e ;
}
return clz ;
}
/ * *
* This method use the resourceType value contained in the Map to instance
* the right UsageRecord class and return it . If the type implementation
* does not exist or the validation of one or more field validation fails
* an exception is thrown
* @param usageRecordMap
* @return the instance of the UsageRecord class .
* @throws Exception if fails
* /
public static UsageRecord getUsageRecord ( Map < String , Serializable > usageRecordMap ) throws Exception {
String className = ( String ) usageRecordMap . get ( USAGE_RECORD_TYPE ) ;
boolean aggregated = false ;
try {
aggregated = ( Boolean ) usageRecordMap . get ( AGGREGATED ) ;
} catch ( Exception e ) { }
Class < ? extends UsageRecord > clz = getClass ( className , aggregated ) ;
logger . debug ( " Trying to instantiate {} " , clz . getClass ( ) . getSimpleName ( ) ) ;
@SuppressWarnings ( " rawtypes " )
Class [ ] usageRecordArgTypes = { Map . class } ;
Constructor < ? extends UsageRecord > usageRecordConstructor = clz . getDeclaredConstructor ( usageRecordArgTypes ) ;
Object [ ] usageRecordArguments = { usageRecordMap } ;
UsageRecord usageRecord = usageRecordConstructor . newInstance ( usageRecordArguments ) ;
logger . debug ( " Created Usage Record : {} " , usageRecord ) ;
return usageRecord ;
}
}