package rocks.inspectit.shared.all.communication.data;
import java.sql.Timestamp;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.PrePersist;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.math.NumberUtils;
import rocks.inspectit.shared.all.cmr.model.JmxDefinitionDataIdent;
import rocks.inspectit.shared.all.cmr.model.PlatformIdent;
import rocks.inspectit.shared.all.cmr.model.SensorTypeIdent;
import rocks.inspectit.shared.all.communication.IAggregatedData;
import rocks.inspectit.shared.all.communication.SystemSensorData;
/**
* This class is needed to store the values of a single attribute.
*
* @author Alfred Krauss
* @author Marius Oehler
*
*/
@Entity
public class JmxSensorValueData extends SystemSensorData implements IAggregatedData<JmxSensorValueData> {
/**
* Maximum {@link #value} length.
*/
private static final int MAX_VALUE_LENGTH = 10000;
/**
* The serial version uid for this class.
*/
private static final long serialVersionUID = 1064800467325690317L;
/**
* The ID of the DefinitionData.
*/
private long jmxSensorDefinitionDataIdentId;
/**
* Values of the attribute to a given time.
*/
@Column(length = MAX_VALUE_LENGTH)
private String value;
/**
* The count of aggregated values, represented by this object.
*/
private int aggregationCount = 0;
/**
* The minimum value of the aggregated objects.
*/
private double minValue = Double.MAX_VALUE;
/**
* The maximum value of the aggregated objects.
*/
private double maxValue = Double.MIN_VALUE;
/**
* The sum of all values of the aggregated objects.
*/
private double totalValue = 0;
/**
* Empty constructor needed for Hibernate.
*/
public JmxSensorValueData() {
}
/**
* Constructor.
*
* @param jmxDefinitionDataIdentId
* the id of the related {@link JmxDefinitionDataIdent} of this
* {@link JmxSensorValueData}
* @param value
* the value
* @param timestamp
* the timestamp when this value was captured
* @param platformIdent
* the id of the related {@link PlatformIdent}
* @param sensorTypeIdent
* the id of the related {@link SensorTypeIdent}
*/
public JmxSensorValueData(long jmxDefinitionDataIdentId, String value, Timestamp timestamp, long platformIdent, long sensorTypeIdent) {
setJmxSensorDefinitionDataIdentId(jmxDefinitionDataIdentId);
setValue(value);
super.setTimeStamp(timestamp);
super.setPlatformIdent(platformIdent);
super.setSensorTypeIdent(sensorTypeIdent);
super.setId(jmxDefinitionDataIdentId);
}
/**
* Copy constructor. Copies all values (except the aggregation values) of the given
* {@link JmxSensorValueData} object into the newly created.
*
* @param origin
* object to clone
*/
public JmxSensorValueData(JmxSensorValueData origin) {
setId(origin.getId());
setPlatformIdent(origin.getPlatformIdent());
setTimeStamp(new Timestamp(origin.getTimeStamp().getTime()));
setSensorTypeIdent(origin.getSensorTypeIdent());
setJmxSensorDefinitionDataIdentId(origin.jmxSensorDefinitionDataIdentId);
setValue(origin.value);
}
/**
* @return The jmxSensorDefinitionDataIdentifier ID
*/
public long getJmxSensorDefinitionDataIdentId() {
return jmxSensorDefinitionDataIdentId;
}
/**
* @param jmxSensorDefinitionDataIdentId
* The jmxSensorDefinitionDataIdent ID to set.
*/
public void setJmxSensorDefinitionDataIdentId(long jmxSensorDefinitionDataIdentId) {
this.jmxSensorDefinitionDataIdentId = jmxSensorDefinitionDataIdentId;
}
/**
* Gets the {@link #value}.
*
* @return {@link #value}
*/
public String getValue() {
return value;
}
/**
* Sets the {@link #value}.
*
* @param value
* New value for {@link #value}.
*/
public void setValue(String value) {
this.value = value;
if ((aggregationCount <= 0) && isBooleanOrNumeric()) {
double currentValue = getValueAsDouble();
aggregationCount = 1;
minValue = currentValue;
maxValue = currentValue;
totalValue = currentValue;
}
}
/**
* Calculates and returns the average value of the aggregated objects.
*
* @return the average value
*/
public double getAverageValue() {
if (aggregationCount <= 0) {
return getValueAsDouble();
}
return totalValue / aggregationCount;
}
/**
* {@inheritDoc}
*/
@Override
public JmxSensorValueData getData() {
return this;
}
/**
* Gets {@link #aggregationCount}.
*
* @return {@link #aggregationCount}
*/
public int getAggregationCount() {
return aggregationCount;
}
/**
* Gets {@link #minValue}.
*
* @return {@link #minValue}
*/
public double getMinValue() {
if (aggregationCount <= 0) {
return getValueAsDouble();
}
return minValue;
}
/**
* Gets {@link #maxValue}.
*
* @return {@link #maxValue}
*/
public double getMaxValue() {
if (aggregationCount <= 0) {
return getValueAsDouble();
}
return maxValue;
}
/**
* Gets {@link #totalValue}.
*
* @return {@link #totalValue}
*/
public double getTotalValue() {
return totalValue;
}
/**
* Returns the value as a {@link Double} value. The returned value will be <code>0</code> or
* <code>1</code> if the {@link #value} is a boolean value. If {@link #value} can not be
* converted into a number, a {@link NumberFormatException} is thrown.
*
* @return {@link #value} as {@link Double}
*/
public double getValueAsDouble() {
if (NumberUtils.isNumber(value)) {
return NumberUtils.createDouble(value);
}
if (isBooleanValue(value)) {
return BooleanUtils.toBoolean(value) ? 1 : 0;
}
throw new NumberFormatException();
}
/**
* Checks if the value of this object is a boolean or numeric value.
*
* @return <code>true</code> if the value is a boolean or number, otherwise <code>false</code>
*/
public boolean isBooleanOrNumeric() {
return isBooleanValue(value) || NumberUtils.isNumber(value);
}
/**
* Determines whether the given string contains a boolean value (false, true, yes, no).
*
* @param value
* the value to examine
* @return whether the given value is a boolean value
*/
private boolean isBooleanValue(String value) {
value = value.toLowerCase();
return "false".equals(value) || "true".equals(value) || "yes".equals(value) || "no".equals(value);
}
/**
* {@inheritDoc}
*/
@Override
public void aggregate(JmxSensorValueData data) {
if (!data.isBooleanOrNumeric()) {
throw new RuntimeException("The given JMX data can not be aggregated.");
}
aggregationCount++;
double valueToAggregate = data.getValueAsDouble();
minValue = Math.min(minValue, valueToAggregate);
maxValue = Math.max(maxValue, valueToAggregate);
totalValue += valueToAggregate;
}
/**
* Pre-persist to ensure size of the {@link #value} is not more than {@value #MAX_VALUE_LENGTH}.
*/
@PrePersist
protected void prePersist() {
if ((null != value) && (value.length() > MAX_VALUE_LENGTH)) {
value = value.substring(0, MAX_VALUE_LENGTH);
}
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "JmxSensorValueData [jmxSensorDefinitionDataIdent=" + jmxSensorDefinitionDataIdentId + ", value=" + value + ", getId()=" + getId() + ", getPlatformIdent()=" + getPlatformIdent()
+ ", getSensorTypeIdent()=" + getSensorTypeIdent() + ", getTimeStamp()=" + getTimeStamp() + "]";
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = (prime * result) + aggregationCount;
result = (prime * result) + (int) (jmxSensorDefinitionDataIdentId ^ (jmxSensorDefinitionDataIdentId >>> 32));
long temp;
temp = Double.doubleToLongBits(maxValue);
result = (prime * result) + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(minValue);
result = (prime * result) + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(totalValue);
result = (prime * result) + (int) (temp ^ (temp >>> 32));
result = (prime * result) + ((value == null) ? 0 : value.hashCode());
return result;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
JmxSensorValueData other = (JmxSensorValueData) obj;
if (aggregationCount != other.aggregationCount) {
return false;
}
if (jmxSensorDefinitionDataIdentId != other.jmxSensorDefinitionDataIdentId) {
return false;
}
if (Double.doubleToLongBits(maxValue) != Double.doubleToLongBits(other.maxValue)) {
return false;
}
if (Double.doubleToLongBits(minValue) != Double.doubleToLongBits(other.minValue)) {
return false;
}
if (Double.doubleToLongBits(totalValue) != Double.doubleToLongBits(other.totalValue)) {
return false;
}
if (value == null) {
if (other.value != null) {
return false;
}
} else if (!value.equals(other.value)) {
return false;
}
return true;
}
}