2015-12-21 10:37:28 +01:00
/ * *
*
* /
package org.gcube.documentstore.records.implementation ;
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.HashMap ;
import java.util.HashSet ;
import java.util.List ;
import java.util.Map ;
import java.util.Map.Entry ;
import java.util.Set ;
2016-09-19 12:53:50 +02:00
import java.util.SortedSet ;
import java.util.TreeSet ;
2015-12-21 10:37:28 +01:00
import java.util.UUID ;
2020-07-10 18:24:56 +02:00
import org.gcube.com.fasterxml.jackson.annotation.JsonIgnore ;
2015-12-21 10:37:28 +01:00
import org.gcube.documentstore.exception.InvalidValueException ;
import org.gcube.documentstore.records.AggregatedRecord ;
import org.gcube.documentstore.records.Record ;
import org.gcube.documentstore.records.implementation.validations.annotations.NotEmpty ;
import org.gcube.documentstore.records.implementation.validations.annotations.ValidLong ;
2016-02-29 15:18:04 +01:00
import org.gcube.documentstore.records.implementation.validations.validators.ValidLongValidator ;
2015-12-21 10:37:28 +01:00
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
2017-05-11 16:44:04 +02:00
2015-12-21 10:37:28 +01:00
/ * *
2016-10-12 14:18:38 +02:00
* @author Luca Frosini ( ISTI - CNR )
2015-12-21 10:37:28 +01:00
* /
2017-09-05 16:58:47 +02:00
public abstract class AbstractRecord implements Record {
2015-12-21 10:37:28 +01:00
/ * *
* Generated Serial Version UID
* /
private static final long serialVersionUID = - 2060728578456796388L ;
private static Logger logger = LoggerFactory . getLogger ( AbstractRecord . class ) ;
2016-02-29 15:18:04 +01:00
@NotEmpty
2015-12-21 11:48:36 +01:00
protected static final String ID = Record . ID ;
2015-12-21 10:37:28 +01:00
2016-02-29 15:18:04 +01:00
@ValidLong
2015-12-21 11:48:36 +01:00
protected static final String CREATION_TIME = Record . CREATION_TIME ;
2015-12-21 10:37:28 +01:00
/** resource-specific properties */
2016-01-28 18:26:01 +01:00
protected Map < String , Serializable > resourceProperties ;
2015-12-21 10:37:28 +01:00
protected Map < String , List < FieldAction > > validation ;
2016-01-28 18:26:01 +01:00
protected Map < String , List < FieldAction > > computation ;
2015-12-21 10:37:28 +01:00
protected Set < String > requiredFields ;
protected Set < String > computedFields ;
protected Set < String > aggregatedFields ;
protected static Set < Field > getAllFields ( Class < ? > type ) {
2016-02-29 15:18:04 +01:00
Set < Field > fields = new HashSet < Field > ( ) ;
for ( Class < ? > c = type ; c ! = null ; c = c . getSuperclass ( ) ) {
fields . addAll ( Arrays . asList ( c . getDeclaredFields ( ) ) ) ;
fields . addAll ( Arrays . asList ( c . getFields ( ) ) ) ;
}
return fields ;
}
2015-12-21 10:37:28 +01:00
protected void initializeValidation ( ) {
Set < Field > fields = getAllFields ( this . getClass ( ) ) ;
for ( Field field : fields ) {
boolean defaultAccessibility = field . isAccessible ( ) ;
field . setAccessible ( true ) ;
String keyString ;
try {
keyString = ( String ) field . get ( null ) ;
} catch ( Exception e ) {
continue ;
}
2016-01-28 18:26:01 +01:00
2016-02-29 15:18:04 +01:00
if ( field . getAnnotations ( ) . length > 0 ) {
List < FieldAction > fieldValidators = validation . get ( keyString ) ;
if ( fieldValidators = = null ) {
fieldValidators = new ArrayList < FieldAction > ( ) ;
validation . put ( keyString , fieldValidators ) ;
2015-12-21 10:37:28 +01:00
}
2016-02-29 15:18:04 +01:00
List < FieldAction > fieldComputations = computation . get ( keyString ) ;
if ( fieldComputations = = null ) {
fieldComputations = new ArrayList < FieldAction > ( ) ;
computation . put ( keyString , fieldComputations ) ;
2015-12-21 10:37:28 +01:00
}
2016-02-29 15:18:04 +01:00
for ( Annotation annotation : field . getAnnotations ( ) ) {
Class < ? extends Annotation > annotationType = annotation . annotationType ( ) ;
if ( annotationType . isAnnotationPresent ( FieldDecorator . class ) ) {
Class < ? extends FieldAction > managedClass = ( ( FieldDecorator ) annotationType . getAnnotation ( FieldDecorator . class ) ) . action ( ) ;
FieldAction validator ;
try {
validator = managedClass . newInstance ( ) ;
} catch ( InstantiationException | IllegalAccessException e ) {
logger . error ( " {} {} " , keyString , annotation , e ) ;
continue ;
}
fieldValidators . add ( validator ) ;
}
if ( annotationType . isAssignableFrom ( RequiredField . class ) ) {
requiredFields . add ( keyString ) ;
}
if ( annotationType . isAssignableFrom ( AggregatedField . class ) ) {
aggregatedFields . add ( keyString ) ;
}
if ( annotationType . isAssignableFrom ( ComputedField . class ) ) {
computedFields . add ( keyString ) ;
Class < ? extends FieldAction > managedClass = ( ( ComputedField ) annotation ) . action ( ) ;
FieldAction computeAction ;
try {
computeAction = managedClass . newInstance ( ) ;
} catch ( InstantiationException | IllegalAccessException e ) {
logger . error ( " {} {} " , keyString , annotation , e ) ;
continue ;
}
fieldComputations . add ( computeAction ) ;
2016-01-28 18:26:01 +01:00
}
2015-12-21 10:37:28 +01:00
}
}
field . setAccessible ( defaultAccessibility ) ;
}
2016-09-28 10:59:18 +02:00
2015-12-21 10:37:28 +01:00
}
2016-09-19 12:53:50 +02:00
@Override
public SortedSet < String > getQuerableKeys ( )
throws Exception {
SortedSet < String > properties = new TreeSet < > (
this . getRequiredFields ( ) ) ;
properties . removeAll ( this . getAggregatedFields ( ) ) ;
properties . removeAll ( this . getComputedFields ( ) ) ;
properties . remove ( Record . ID ) ;
properties . remove ( Record . CREATION_TIME ) ;
return properties ;
}
2015-12-21 10:37:28 +01:00
protected void cleanExtraFields ( ) {
Set < String > neededFields = this . requiredFields ;
neededFields . addAll ( this . aggregatedFields ) ;
Set < String > keysToRemove = new HashSet < String > ( ) ;
Set < String > propertyKeys = this . resourceProperties . keySet ( ) ;
for ( String propertyName : propertyKeys ) {
if ( ! neededFields . contains ( propertyName ) ) {
keysToRemove . add ( propertyName ) ;
}
}
for ( String keyToRemove : keysToRemove ) {
this . resourceProperties . remove ( keyToRemove ) ;
}
}
/ * *
* Initialize variable
* /
2015-12-21 11:48:36 +01:00
protected void init ( ) {
2015-12-21 10:37:28 +01:00
this . validation = new HashMap < String , List < FieldAction > > ( ) ;
2016-01-28 18:26:01 +01:00
this . computation = new HashMap < String , List < FieldAction > > ( ) ;
2015-12-21 10:37:28 +01:00
this . requiredFields = new HashSet < String > ( ) ;
this . aggregatedFields = new HashSet < String > ( ) ;
this . computedFields = new HashSet < String > ( ) ;
2016-01-28 18:26:01 +01:00
this . resourceProperties = new HashMap < String , Serializable > ( ) ;
2015-12-21 10:37:28 +01:00
initializeValidation ( ) ;
}
2017-05-11 16:44:04 +02:00
2015-12-21 10:37:28 +01:00
public AbstractRecord ( ) {
init ( ) ;
this . resourceProperties . put ( ID , UUID . randomUUID ( ) . toString ( ) ) ;
Calendar calendar = Calendar . getInstance ( ) ;
this . resourceProperties . put ( CREATION_TIME , calendar . getTimeInMillis ( ) ) ;
}
2017-05-11 16:44:04 +02:00
2016-01-28 18:26:01 +01:00
public AbstractRecord ( Map < String , ? extends Serializable > properties ) throws InvalidValueException {
2015-12-21 10:37:28 +01:00
init ( ) ;
setResourceProperties ( properties ) ;
if ( this instanceof AggregatedRecord ) {
this . resourceProperties . put ( AggregatedRecord . AGGREGATED , true ) ;
cleanExtraFields ( ) ;
}
}
/ * *
* { @inheritDoc }
* /
@Override
2017-09-14 14:21:34 +02:00
@JsonIgnore
2015-12-21 10:37:28 +01:00
public Set < String > getRequiredFields ( ) {
2016-03-16 18:39:55 +01:00
return new HashSet < String > ( requiredFields ) ;
}
/ * *
* { @inheritDoc }
* /
@Override
2017-09-14 14:21:34 +02:00
@JsonIgnore
2016-03-16 18:39:55 +01:00
public Set < String > getComputedFields ( ) {
return new HashSet < String > ( computedFields ) ;
}
2017-09-14 14:21:34 +02:00
@JsonIgnore
2016-03-16 18:39:55 +01:00
public Set < String > getAggregatedFields ( ) {
return new HashSet < String > ( aggregatedFields ) ;
2015-12-21 10:37:28 +01:00
}
/ * *
* { @inheritDoc }
* /
@Override
public String getId ( ) {
return ( String ) this . resourceProperties . get ( ID ) ;
}
/ * *
* { @inheritDoc }
* /
@Override
public void setId ( String id ) throws InvalidValueException {
setResourceProperty ( ID , id ) ;
}
2016-01-13 15:34:05 +01:00
public static Calendar timestampToCalendar ( long millis ) {
2015-12-21 10:37:28 +01:00
Calendar calendar = Calendar . getInstance ( ) ;
calendar . setTimeInMillis ( millis ) ;
return calendar ;
}
/ * *
* { @inheritDoc }
* /
@Override
public Calendar getCreationTime ( ) {
2016-02-29 15:18:04 +01:00
Long millis = null ;
try {
millis = ( Long ) new ValidLongValidator ( ) . validate ( CREATION_TIME , this . resourceProperties . get ( CREATION_TIME ) , null ) ;
return timestampToCalendar ( millis ) ;
} catch ( InvalidValueException e ) {
return null ;
}
2015-12-21 10:37:28 +01:00
}
/ * *
* { @inheritDoc }
* /
@Override
public void setCreationTime ( Calendar creationTime ) throws InvalidValueException {
setResourceProperty ( CREATION_TIME , creationTime . getTimeInMillis ( ) ) ;
}
/ * *
* { @inheritDoc }
* /
@Override
2016-01-28 18:26:01 +01:00
public Map < String , Serializable > getResourceProperties ( ) {
return new HashMap < String , Serializable > ( this . resourceProperties ) ;
2015-12-21 10:37:28 +01:00
}
/ * *
* { @inheritDoc }
* /
@Override
2017-05-11 16:44:04 +02:00
public void setResourceProperties ( Map < String , ? extends Serializable > properties ) throws InvalidValueException {
Map < String , ? extends Serializable > validated = validateProperties ( properties ) ;
2016-01-28 18:26:01 +01:00
this . resourceProperties = new HashMap < String , Serializable > ( validated ) ;
2015-12-21 10:37:28 +01:00
}
/ * *
* { @inheritDoc }
* /
@Override
2016-01-28 18:26:01 +01:00
public Serializable getResourceProperty ( String key ) {
2015-12-21 10:37:28 +01:00
return this . resourceProperties . get ( key ) ;
}
2017-09-05 16:58:47 +02:00
@Override
public void removeResourceProperty ( String key ) {
this . resourceProperties . remove ( key ) ;
}
2015-12-21 10:37:28 +01:00
/ * *
* { @inheritDoc }
* /
2017-05-11 16:44:04 +02:00
@Override
2016-01-28 18:26:01 +01:00
public void setResourceProperty ( String key , Serializable value ) throws InvalidValueException {
Serializable checkedValue = validateField ( key , value ) ;
2015-12-21 10:37:28 +01:00
if ( checkedValue = = null ) {
this . resourceProperties . remove ( key ) ;
} else {
this . resourceProperties . put ( key , checkedValue ) ;
}
}
2017-05-11 16:44:04 +02:00
2015-12-21 10:37:28 +01:00
// AGGREGATION
/* --------------------------------------- */
2015-12-21 10:43:05 +01:00
/ * *
* Set the right end of the time interval covered by this Record
* @param endTime End Time
* @throws InvalidValueException
* /
protected void setEndTime ( Calendar endTime ) throws InvalidValueException {
setResourceProperty ( AggregatedRecord . END_TIME , endTime . getTimeInMillis ( ) ) ;
}
2017-09-14 14:21:34 +02:00
/ * *
* Return the right end of the time interval covered by this Record
* @return End Time
* /
protected long getEndTimeInMillis ( ) {
return ( Long ) this . resourceProperties . get ( AggregatedRecord . END_TIME ) ;
}
/ * *
* Return the right end of the time interval covered by this Record
* @return End Time
* /
protected Calendar getEndTimeAsCalendar ( ) {
long millis = getEndTimeInMillis ( ) ;
return timestampToCalendar ( millis ) ;
}
2017-09-18 15:09:20 +02:00
@JsonIgnore
2015-12-21 10:43:05 +01:00
protected int getOperationCount ( ) {
return ( Integer ) this . resourceProperties . get ( AggregatedRecord . OPERATION_COUNT ) ;
}
protected void setOperationCount ( int operationCount ) throws InvalidValueException {
setResourceProperty ( AggregatedRecord . OPERATION_COUNT , operationCount ) ;
}
2015-12-21 10:37:28 +01:00
/ * *
2015-12-21 10:43:05 +01:00
* Return the left end of the time interval covered by this Record
2015-12-21 10:37:28 +01:00
* @return Start Time
* /
protected long getStartTimeInMillis ( ) {
2015-12-21 10:43:05 +01:00
return ( Long ) this . resourceProperties . get ( AggregatedRecord . START_TIME ) ;
2015-12-21 10:37:28 +01:00
}
/ * *
2015-12-21 10:43:05 +01:00
* Return the left end of the time interval covered by this Record
2015-12-21 10:37:28 +01:00
* @return Start Time
* /
protected Calendar getStartTimeAsCalendar ( ) {
long millis = getStartTimeInMillis ( ) ;
2016-01-13 15:34:05 +01:00
return timestampToCalendar ( millis ) ;
2015-12-21 10:37:28 +01:00
}
/ * *
2015-12-21 10:43:05 +01:00
* Set the left end of the time interval covered by this Record
2015-12-21 10:37:28 +01:00
* @param startTime Start Time
* @throws InvalidValueException
* /
protected void setStartTime ( Calendar startTime ) throws InvalidValueException {
2015-12-21 10:43:05 +01:00
setResourceProperty ( AggregatedRecord . START_TIME , startTime . getTimeInMillis ( ) ) ;
2015-12-21 10:37:28 +01:00
}
2017-05-11 16:44:04 +02:00
/ * *
* Set the boolean aggregate by this Record
2017-10-27 16:47:21 +02:00
* @param aggregate
2017-05-11 16:44:04 +02:00
* @throws InvalidValueException
* /
2017-09-14 14:21:34 +02:00
protected void setAggregated ( Boolean aggregate ) throws InvalidValueException {
2017-05-11 16:44:04 +02:00
setResourceProperty ( AggregatedRecord . AGGREGATED , aggregate ) ;
}
2017-10-27 16:47:21 +02:00
2017-09-18 15:09:20 +02:00
@JsonIgnore
2017-09-14 14:21:34 +02:00
protected Boolean isAggregated ( ) {
Boolean bool = ( Boolean ) this . resourceProperties . get ( AggregatedRecord . AGGREGATED ) ;
if ( bool = = null ) {
return false ;
}
return bool ;
2015-12-21 10:37:28 +01:00
}
2017-05-11 16:44:04 +02:00
2016-01-28 18:26:01 +01:00
protected Serializable validateField ( String key , Serializable value ) throws InvalidValueException {
2015-12-21 10:37:28 +01:00
if ( key = = null ) {
throw new InvalidValueException ( " The key of property to set cannot be null " ) ;
}
2016-01-28 18:26:01 +01:00
Serializable checkedValue = value ;
2015-12-21 10:37:28 +01:00
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 will be ignored. " , key ) ;
}
try {
2016-09-28 10:59:18 +02:00
2015-12-21 10:37:28 +01:00
checkedValue = fieldValidator . validate ( key , checkedValue , this ) ;
2016-04-14 17:32:48 +02:00
if ( checkedValue = = null ) {
return null ;
}
2015-12-21 10:37:28 +01:00
} catch ( InvalidValueException e ) {
2021-05-25 11:07:14 +02:00
logger . error ( String . format ( " The provided value %s is NOT valid for field with key %s. " , checkedValue , key ) ) ;
2015-12-21 10:37:28 +01:00
throw e ;
}
}
}
return checkedValue ;
}
2016-01-28 18:26:01 +01:00
protected void computeField ( String key ) throws InvalidValueException {
if ( key = = null ) {
throw new InvalidValueException ( " The key of property to set cannot be null " ) ;
}
Serializable computedValue = null ;
List < FieldAction > fieldComputations = computation . get ( key ) ;
if ( fieldComputations ! = null ) {
for ( FieldAction fieldValidator : fieldComputations ) {
try {
computedValue = fieldValidator . validate ( key , null , this ) ;
this . resourceProperties . put ( key , computedValue ) ;
} catch ( InvalidValueException e ) {
logger . error ( String . format ( " Unable to calculate the field with key %s " , key ) ) ;
throw e ;
}
}
}
}
protected Map < String , ? extends Serializable > validateProperties ( Map < String , ? extends Serializable > properties ) throws InvalidValueException {
Map < String , Serializable > validated = new HashMap < String , Serializable > ( ) ;
2015-12-21 10:37:28 +01:00
for ( String key : properties . keySet ( ) ) {
2016-01-28 18:26:01 +01:00
Serializable value = properties . get ( key ) ;
2016-02-29 15:18:04 +01:00
Serializable checkedValue = validateField ( key , value ) ;
if ( checkedValue = = null ) {
validated . remove ( key ) ;
} else {
validated . put ( key , checkedValue ) ;
}
2017-05-11 16:44:04 +02:00
2015-12-21 10:37:28 +01:00
}
return validated ;
}
/ * *
* { @inheritDoc }
* /
@Override
public void validate ( ) throws InvalidValueException {
2016-01-28 18:26:01 +01:00
for ( String key : this . computedFields ) {
computeField ( key ) ;
}
2015-12-21 10:37:28 +01:00
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 " ;
2016-09-08 17:35:02 +02:00
logger . debug ( " ID doc:{} " , this . getId ( ) ) ;
2015-12-21 10:37:28 +01:00
throw new InvalidValueException ( String . format ( " The Record does not contain the following required propert%s %s " , pluralManagement , notPresentProperties . toString ( ) ) ) ;
}
}
@Override
public String toString ( ) {
return resourceProperties . toString ( ) ;
}
/ * *
* Compare this Record instance with the one provided as argument
* @param record the Record to compare
2015-12-21 10:43:05 +01:00
* @return 0 is and only if the Record provided as parameter
2015-12-21 10:37:28 +01:00
* 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
2015-12-21 10:43:05 +01:00
* Record provided as parameter .
2015-12-21 10:37:28 +01:00
* If the size is the same but the Record provided as parameter does
* not contains all parameters in this instance , - 1 is returned .
* /
@Override
public int compareTo ( Record record ) {
2016-01-28 18:26:01 +01:00
Set < Entry < String , Serializable > > thisSet = this . resourceProperties . entrySet ( ) ;
Set < Entry < String , Serializable > > recordSet = record . getResourceProperties ( ) . entrySet ( ) ;
2015-12-21 10:43:05 +01:00
if ( thisSet . size ( ) ! = recordSet . size ( ) ) {
return thisSet . size ( ) - recordSet . size ( ) ;
2015-12-21 10:37:28 +01:00
}
2015-12-21 10:43:05 +01:00
if ( recordSet . containsAll ( thisSet ) ) {
2015-12-21 10:37:28 +01:00
return 0 ;
}
return 1 ;
}
}