Skip to content

Commit

Permalink
Merge pull request #729 from ChandraAddala/json-pojo-validate
Browse files Browse the repository at this point in the history
Json Validation and Mapping - part 1
  • Loading branch information
ChandraAddala committed Aug 31, 2016
2 parents dd5e24f + bb202dc commit 8316651
Show file tree
Hide file tree
Showing 16 changed files with 988 additions and 197 deletions.
18 changes: 18 additions & 0 deletions blueflood-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,24 @@
<version>${jodatime.version}</version>
</dependency>

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.4.Final</version>
</dependency>

<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>2.2.4</version>
</dependency>

<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.4</version>
</dependency>

<!-- testing dependencies -->
<dependency>
<groupId>junit</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.rackspacecloud.blueflood.inputs.constraints;

import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import javax.validation.constraints.Min;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;


/**
*
* The annotated element has to be in the appropriate range relative to the current time.
* Applicable for values of type long which represent time in epoch. The range of the
* element is defined as below.
*
* (currentTime - maxPast) <= element <= (currentTime + maxFuture)
*
*/
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = { EpochRangeValidator.class })
@ReportAsSingleViolation
public @interface EpochRange {

String message() default "{com.rackspacecloud.blueflood.inputs.constraints.EpochRange.message}";

Class<?>[] groups() default { };

Class<? extends Payload>[] payload() default { };

/**
* @return the element cannot be older than (current time - maxPast) milli seconds in the past
*/
EpochRangeLimits maxPast();

/**
* @return the element cannot be earlier tha (current time + maxFuture) milli seconds in the future
*/
EpochRangeLimits maxFuture();

/**
* Defines several {@link Min} annotations on the same element.
*
* @see Min
*/
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@interface List {

EpochRange[] value();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.rackspacecloud.blueflood.inputs.constraints;

import com.rackspacecloud.blueflood.service.Configuration;
import com.rackspacecloud.blueflood.service.CoreConfig;

/**
* Constants to set limits for allowed epoch ranges.
*/
public enum EpochRangeLimits {

BEFORE_CURRENT_TIME_MS(Configuration.getInstance().getLongProperty(CoreConfig.BEFORE_CURRENT_COLLECTIONTIME_MS)),
AFTER_CURRENT_TIME_MS(Configuration.getInstance().getLongProperty(CoreConfig.AFTER_CURRENT_COLLECTIONTIME_MS));

private long value;

EpochRangeLimits(long value) {
this.value = value;
}

public long getValue() {
return value;
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.rackspacecloud.blueflood.inputs.constraints;

import com.rackspacecloud.blueflood.utils.Clock;
import com.rackspacecloud.blueflood.utils.DefaultClockImpl;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class EpochRangeValidator implements ConstraintValidator<EpochRange, Long> {

private final Clock clock = new DefaultClockImpl();

private long maxPast;
private long maxFuture;

@Override
public void initialize(EpochRange constraintAnnotation) {
this.maxPast = constraintAnnotation.maxPast().getValue();
this.maxFuture = constraintAnnotation.maxFuture().getValue();
}

@Override
public boolean isValid(Long value, ConstraintValidatorContext context) {

long currentTime = clock.now().getMillis();

if ( value < currentTime - maxPast ) {
// collectionTime is too far in the past
return false;
} else if ( value > currentTime + maxFuture ) {
// collectionTime is too far in the future
return false;
}

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,31 @@
*/
package com.rackspacecloud.blueflood.inputs.formats;

import com.rackspacecloud.blueflood.service.Configuration;
import com.rackspacecloud.blueflood.service.CoreConfig;
import com.rackspacecloud.blueflood.inputs.constraints.EpochRange;
import com.rackspacecloud.blueflood.inputs.constraints.EpochRangeLimits;
import org.hibernate.validator.constraints.NotEmpty;
import org.hibernate.validator.constraints.Range;

import java.util.List;
import javax.validation.constraints.NotNull;

// Jackson compatible class. Jackson uses reflection to call these methods and so they have to match JSON keys.
public class JSONMetric {

final long BEFORE_CURRENT_COLLECTIONTIME_MS = Configuration.getInstance().getLongProperty( CoreConfig.BEFORE_CURRENT_COLLECTIONTIME_MS );
final long AFTER_CURRENT_COLLECTIONTIME_MS = Configuration.getInstance().getLongProperty( CoreConfig.AFTER_CURRENT_COLLECTIONTIME_MS );
final static String ERROR_MESSAGE = "xxx " + EpochRangeLimits.BEFORE_CURRENT_TIME_MS.getValue();

@NotEmpty
private String metricName;

private Object metricValue;

@EpochRange(maxPast = EpochRangeLimits.BEFORE_CURRENT_TIME_MS,
maxFuture = EpochRangeLimits.AFTER_CURRENT_TIME_MS,
message = "Out of bounds. Cannot be more than ${maxPast.getValue()} milliseconds into the past. Cannot be more than ${maxPast.getValue()} milliseconds into the future")
private long collectionTime;

@Range(min=1, max=Integer.MAX_VALUE)
private int ttlInSeconds;

private String unit;

public String getMetricName() {
Expand Down Expand Up @@ -72,19 +82,4 @@ public void setTtlInSeconds(int ttlInSeconds) {
this.ttlInSeconds = ttlInSeconds;
}

public List<String> getValidationErrors() {
List<String> errors = new java.util.ArrayList<String>();

long currentTime = System.currentTimeMillis();
if ( collectionTime < currentTime - BEFORE_CURRENT_COLLECTIONTIME_MS ) {
// collectionTime is too far in the past
errors.add( "'" + metricName + "': 'collectionTime' '" + collectionTime + "' is more than '" + BEFORE_CURRENT_COLLECTIONTIME_MS + "' milliseconds into the past." );
} else if ( collectionTime > currentTime + AFTER_CURRENT_COLLECTIONTIME_MS ) {
// collectionTime is too far in the future
errors.add( "'" + metricName + "': 'collectionTime' '" + collectionTime + "' is more than '" + AFTER_CURRENT_COLLECTIONTIME_MS + "' milliseconds into the future." );
}

return errors;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,14 @@
*/
package com.rackspacecloud.blueflood.inputs.formats;

import org.apache.commons.lang.StringUtils;

import java.util.List;
import org.hibernate.validator.constraints.NotEmpty;

public class JSONMetricScoped extends JSONMetric {

@NotEmpty
private String tenantId;

public String getTenantId() { return tenantId; }

public void setTenantId(String tenantId) { this.tenantId = tenantId; }

@Override
public List<String> getValidationErrors() {

List<String> errors = super.getValidationErrors();

// validate tenantid
if (StringUtils.isBlank(tenantId)) {
errors.add ( "'" + getMetricName() + "': No tenantId is provided for the metric." );
}

return errors;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.rackspacecloud.blueflood.inputs.formats;

import com.rackspacecloud.blueflood.io.Instrumentation;
import com.rackspacecloud.blueflood.outputs.formats.ErrorResponse;
import com.rackspacecloud.blueflood.service.Configuration;
import com.rackspacecloud.blueflood.service.CoreConfig;
import com.rackspacecloud.blueflood.types.Locator;
Expand All @@ -35,45 +36,31 @@ public class JSONMetricsContainer {
private static final long SHORT_DELAY = Configuration.getInstance().getLongProperty(CoreConfig.SHORT_DELAY_METRICS_ROLLUP_DELAY_MILLIS);

private final String tenantId;
private final List<JSONMetric> jsonMetrics;
private final List<Metric> metrics = new ArrayList<Metric>();

private final List<Metric> validMetrics;
private final List<ErrorResponse.ErrorData> validationErrors;
private final List<Metric> delayedMetrics = new ArrayList<Metric>();
private final List<String> errors = new ArrayList<String>();

public JSONMetricsContainer(String tenantId, List<JSONMetric> metrics) {
public JSONMetricsContainer(String tenantId, List<JSONMetric> validJsonMetrics, List<ErrorResponse.ErrorData> validationErrors) {
this.tenantId = tenantId;
this.jsonMetrics = metrics;
processJson();
}

public List<String> getValidationErrors() {
return errors;
this.validMetrics = processJson(validJsonMetrics);
this.validationErrors = validationErrors;
}

public List<Metric> getValidMetrics() {
return metrics;
return validMetrics;
}

public List<JSONMetric> getJsonMetrics() {
return jsonMetrics;
public List<ErrorResponse.ErrorData> getValidationErrors() {
return validationErrors;
}

private List<Metric> processJson() {
private List<Metric> processJson(List<JSONMetric> jsonMetrics) {

if (jsonMetrics == null || jsonMetrics.isEmpty()) {
return null;
}
List<Metric> metrics = new ArrayList<Metric>();

for (JSONMetric jsonMetric : jsonMetrics) {

// validate metric and retrieve error message if failed
List<String> metricValidationErrors = jsonMetric.getValidationErrors();
if ( !metricValidationErrors.isEmpty() ) {
// has metric has validation errors, do not convert metric and add to errors list and go to next metric
errors.addAll(metricValidationErrors);
continue;
}

if (jsonMetric.getMetricValue() == null) {
// skip null value
continue;
Expand All @@ -90,6 +77,7 @@ private List<Metric> processJson() {

final Metric metric = new Metric(locator, jsonMetric.getMetricValue(), jsonMetric.getCollectionTime(),
new TimeValue(jsonMetric.getTtlInSeconds(), TimeUnit.SECONDS), jsonMetric.getUnit());

long delay = new DateTime().getMillis() - metric.getCollectionTime();

if (delay > TRACKER_DELAYED_METRICS_MILLIS) {
Expand Down Expand Up @@ -118,4 +106,4 @@ public List<Metric> getDelayedMetrics() {
return delayedMetrics;
}

}
}
Loading

0 comments on commit 8316651

Please sign in to comment.