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 ;
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 ) ;
@RequiredField @NotEmpty
public static final String ID = " id " ;
@RequiredField @NotEmpty
public static final String CONSUMER_ID = " consumerId " ;
@RequiredField @ValidLong
public static final String CREATION_TIME = " creationTime " ;
2015-06-23 17:47:38 +02:00
@RequiredField @NotEmpty
protected static final String USAGE_RECORD_TYPE = " usageRecordType " ;
@RequiredField @NotEmpty
public static final String SCOPE = " scope " ;
@RequiredField @ValidOperationResult
public static final String OPERATION_RESULT = " operationResult " ;
2015-06-10 17:36:50 +02:00
@AggregatedField @ValidLong
protected static final String START_TIME = " startTime " ;
@AggregatedField @ValidLong
protected static final String END_TIME = " endTime " ;
2015-06-24 15:24:57 +02:00
2015-06-10 17:36:50 +02:00
@AggregatedField @NotEmptyIfNotNull
protected static final String AGGREGATED = " aggregated " ;
2015-06-23 17:47:38 +02:00
/ *
2015-06-10 17:36:50 +02:00
@AggregatedField @NotEmptyIfNotNull
protected static final String AGGREGATED_USAGE_RECORD_ID = " aggregatedUsageRecordId " ;
2015-06-23 17:47:38 +02:00
* /
2015-06-10 17:36:50 +02:00
/** resource-specific properties */
protected Map < String , Serializable > resourceProperties ;
protected Map < String , List < FieldAction > > validation ;
protected Set < String > requiredFields ;
protected Set < String > aggregatedFields ;
protected Set < String > computedFields ;
protected void initializeValidation ( ) {
logger . debug ( " Initializing Field Validators " ) ;
List < Field > fields = Arrays . asList ( this . getClass ( ) . getDeclaredFields ( ) ) ;
for ( Field field : fields ) {
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-06-23 17:47:38 +02:00
2015-06-10 17:36:50 +02:00
}
/ * *
* Initialize variable
* /
private void init ( ) {
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-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-10 17:36:50 +02:00
long millis = ( Long ) this . resourceProperties . get ( START_TIME ) ;
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-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-10 17:36:50 +02:00
long millis = ( Long ) this . resourceProperties . get ( END_TIME ) ;
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-15 11:28:19 +02:00
/ *
2015-06-10 17:36:50 +02:00
protected String getUsageRecordType ( ) {
//return (String) this.resourceSpecificProperties.get(RESOURCE_TYPE);
return this . getClass ( ) . getSimpleName ( ) ;
}
2015-06-15 11:28:19 +02:00
* /
2015-06-10 17:36:50 +02:00
/ * *
* { @inheritDoc }
* /
@Override
2015-06-23 17:47:38 +02:00
public String getScope ( ) {
return ( String ) this . resourceProperties . get ( SCOPE ) ;
2015-06-10 17:36:50 +02:00
}
/ * *
* { @inheritDoc }
* /
@Override
2015-06-23 17:47:38 +02:00
public void setScope ( String scope ) throws InvalidValueException {
setResourceProperty ( SCOPE , scope ) ;
2015-06-10 17:36:50 +02:00
}
2015-06-23 17:47:38 +02:00
/ * *
2015-06-10 17:36:50 +02:00
* { @inheritDoc }
2015-06-23 17:47:38 +02:00
* /
2015-06-10 17:36:50 +02:00
@Override
public String getAggregatedUsageRecordId ( ) {
return ( String ) this . resourceProperties . get ( AGGREGATED_USAGE_RECORD_ID ) ;
}
2015-06-23 17:47:38 +02:00
/ * *
2015-06-10 17:36:50 +02:00
* { @inheritDoc }
2015-06-23 17:47:38 +02:00
* /
2015-06-10 17:36:50 +02:00
@Override
public void setAggregatedUsageRecordId ( String aggregatedId ) throws InvalidValueException {
setResourceProperty ( AGGREGATED_USAGE_RECORD_ID , aggregatedId ) ;
}
2015-06-23 17:47:38 +02:00
* /
2015-06-10 17:36:50 +02:00
/ * *
* { @inheritDoc }
* /
@Override
public Map < String , Serializable > getResourceProperties ( ) {
2015-06-15 11:28:19 +02:00
return new HashMap < String , Serializable > ( this . resourceProperties ) ;
2015-06-10 17:36:50 +02:00
}
/ * *
* { @inheritDoc }
* /
@Override
public void setResourceProperties ( Map < String , Serializable > properties ) throws InvalidValueException {
validateProperties ( properties ) ;
this . resourceProperties = new HashMap < String , Serializable > ( properties ) ;
}
/ * *
* { @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 ) ;
}
}
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. " ) ;
}
checkedValue = fieldValidator . validate ( key , checkedValue , this ) ;
}
}
return checkedValue ;
}
protected void validateProperties ( Map < String , Serializable > properties ) throws InvalidValueException {
for ( String key : properties . keySet ( ) ) {
Serializable serializable = properties . get ( key ) ;
validateField ( key , serializable ) ;
}
}
/ * *
* { @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 ;
Class < ? extends UsageRecord > utilityClass = org . gcube . accounting . datamodel . implementations . JobUsageRecord . class ;
if ( aggregated ) {
utilityClass = org . gcube . accounting . datamodel . implementations . aggregated . JobUsageRecord . class ;
}
String classCanonicalName = utilityClass . getCanonicalName ( ) ;
classCanonicalName . replace ( utilityClass . getSimpleName ( ) , usageRecordName ) ;
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 ;
}
}