Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes issue: InfluxDBResultMapper doesnt call POJO setter #676

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 131 additions & 53 deletions src/main/java/org/influxdb/impl/InfluxDBResultMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
package org.influxdb.impl;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
Expand Down Expand Up @@ -51,6 +53,9 @@ public class InfluxDBResultMapper {
private static final
ConcurrentMap<String, ConcurrentMap<String, Field>> CLASS_FIELD_CACHE = new ConcurrentHashMap<>();

private static final
ConcurrentMap<String, ConcurrentMap<String, Method>> CLASS_SETTERS_CACHE = new ConcurrentHashMap<>();

private static final int FRACTION_MIN_WIDTH = 0;
private static final int FRACTION_MAX_WIDTH = 9;
private static final boolean ADD_DECIMAL_POINT = true;
Expand Down Expand Up @@ -217,11 +222,25 @@ void cacheMeasurementClass(final Class<?>... classVarAgrs) {
influxColumnAndFieldMap = initialMap;
}

ConcurrentMap<String, Method> classFieldSetters = new ConcurrentHashMap<>();
ConcurrentMap<String, Method> fieldSetters = CLASS_SETTERS_CACHE.putIfAbsent(clazz.getName(), classFieldSetters);
if (fieldSetters == null) {
fieldSetters = classFieldSetters;
}

Class<?> c = clazz;
while (c != null) {
for (Field field : c.getDeclaredFields()) {
Column colAnnotation = field.getAnnotation(Column.class);
if (colAnnotation != null) {
String fieldName = field.getName();
String setterName = "set".concat(fieldName.substring(0, 1).toUpperCase().concat(fieldName.substring(1)));
try {
Method setter = c.getDeclaredMethod(setterName, field.getType());
fieldSetters.put(colAnnotation.name(), setter);
} catch (NoSuchMethodException e) {
//sj_todo ignore? maybe print a warning that no setter found?
}
influxColumnAndFieldMap.put(colAnnotation.name(), field);
}
}
Expand Down Expand Up @@ -254,16 +273,18 @@ <T> List<T> parseSeriesAs(final QueryResult.Series series, final Class<T> clazz,
final TimeUnit precision) {
int columnSize = series.getColumns().size();
ConcurrentMap<String, Field> colNameAndFieldMap = CLASS_FIELD_CACHE.get(clazz.getName());
ConcurrentMap<String, Method> fieldSettersMap = CLASS_SETTERS_CACHE.get(clazz.getName());
try {
T object = null;
for (List<Object> row : series.getValues()) {
for (int i = 0; i < columnSize; i++) {
Method fieldSetter = fieldSettersMap.get(series.getColumns().get(i));
Field correspondingField = colNameAndFieldMap.get(series.getColumns().get(i)/*InfluxDB columnName*/);
if (correspondingField != null) {
if (correspondingField != null || fieldSetter != null) {
if (object == null) {
object = clazz.newInstance();
}
setFieldValue(object, correspondingField, row.get(i), precision);
setFieldValue(object, correspondingField, fieldSetter, row.get(i), precision);
}
}
// When the "GROUP BY" clause is used, "tags" are returned as Map<String,String> and
Expand All @@ -273,9 +294,10 @@ <T> List<T> parseSeriesAs(final QueryResult.Series series, final Class<T> clazz,
if (series.getTags() != null && !series.getTags().isEmpty()) {
for (Entry<String, String> entry : series.getTags().entrySet()) {
Field correspondingField = colNameAndFieldMap.get(entry.getKey()/*InfluxDB columnName*/);
if (correspondingField != null) {
Method fieldSetter = fieldSettersMap.get(entry.getKey());
if (correspondingField != null || fieldSetter != null) {
// I don't think it is possible to reach here without a valid "object"
setFieldValue(object, correspondingField, entry.getValue(), precision);
setFieldValue(object, correspondingField, fieldSetter, entry.getValue(), precision);
}
}
}
Expand All @@ -284,7 +306,7 @@ <T> List<T> parseSeriesAs(final QueryResult.Series series, final Class<T> clazz,
object = null;
}
}
} catch (InstantiationException | IllegalAccessException e) {
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new InfluxDBMapperException(e);
}
return result;
Expand All @@ -302,8 +324,9 @@ <T> List<T> parseSeriesAs(final QueryResult.Series series, final Class<T> clazz,
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
<T> void setFieldValue(final T object, final Field field, final Object value, final TimeUnit precision)
throws IllegalArgumentException, IllegalAccessException {
<T> void setFieldValue(final T object, final Field field, final Method fieldSetter, final Object value,
final TimeUnit precision)
throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
if (value == null) {
return;
}
Expand All @@ -312,9 +335,12 @@ <T> void setFieldValue(final T object, final Field field, final Object value, fi
if (!field.isAccessible()) {
field.setAccessible(true);
}
if (fieldValueModified(fieldType, field, object, value, precision)
|| fieldValueForPrimitivesModified(fieldType, field, object, value)
|| fieldValueForPrimitiveWrappersModified(fieldType, field, object, value)) {
if (assignInstant(fieldType, field, fieldSetter, object, value, precision)
|| assignString(fieldType, field, fieldSetter, object, value)
|| assignDouble(fieldType, field, fieldSetter, object, value)
|| assignInteger(fieldType, field, fieldSetter, object, value)
|| assignLong(fieldType, field, fieldSetter, object, value)
|| assignBoolean(fieldType, field, fieldSetter, object, value)) {
return;
}
String msg = "Class '%s' field '%s' is from an unsupported type '%s'.";
Expand All @@ -328,13 +354,10 @@ <T> void setFieldValue(final T object, final Field field, final Object value, fi
}
}

<T> boolean fieldValueModified(final Class<?> fieldType, final Field field, final T object, final Object value,
final TimeUnit precision)
throws IllegalArgumentException, IllegalAccessException {
if (String.class.isAssignableFrom(fieldType)) {
field.set(object, String.valueOf(value));
return true;
}
<T> boolean assignInstant(final Class<?> fieldType, final Field field, final Method fieldSetter, final T object,
final Object value, final TimeUnit precision) throws InvocationTargetException,
IllegalAccessException {
boolean isInstantAssigned = false;
if (Instant.class.isAssignableFrom(fieldType)) {
Instant instant;
if (value instanceof String) {
Expand All @@ -348,52 +371,107 @@ <T> boolean fieldValueModified(final Class<?> fieldType, final Field field, fina
} else {
throw new InfluxDBMapperException("Unsupported type " + field.getClass() + " for field " + field.getName());
}
field.set(object, instant);
return true;
if (fieldSetter != null) {
fieldSetter.invoke(object, instant);
isInstantAssigned = true;
} else {
field.set(object, instant);
isInstantAssigned = true;
}
}
return false;
return isInstantAssigned;
}

<T> boolean fieldValueForPrimitivesModified(final Class<?> fieldType, final Field field, final T object,
final Object value) throws IllegalArgumentException, IllegalAccessException {
if (double.class.isAssignableFrom(fieldType)) {
field.setDouble(object, ((Double) value).doubleValue());
return true;
}
if (long.class.isAssignableFrom(fieldType)) {
field.setLong(object, ((Double) value).longValue());
return true;
}
if (int.class.isAssignableFrom(fieldType)) {
field.setInt(object, ((Double) value).intValue());
return true;
}
if (boolean.class.isAssignableFrom(fieldType)) {
field.setBoolean(object, Boolean.valueOf(String.valueOf(value)).booleanValue());
return true;
<T> boolean assignString(final Class<?> fieldType, final Field field, final Method fieldSetter, final T object,
final Object value) throws InvocationTargetException, IllegalAccessException {
boolean isStringAssigned = false;
if (String.class.isAssignableFrom(fieldType)) {
final String stringValue = String.valueOf(value);
if (fieldSetter != null) {
fieldSetter.invoke(object, stringValue);
isStringAssigned = true;
} else {
field.set(object, stringValue);
isStringAssigned = true;
}
}
return false;
return isStringAssigned;
}

<T> boolean fieldValueForPrimitiveWrappersModified(final Class<?> fieldType, final Field field, final T object,
final Object value) throws IllegalArgumentException, IllegalAccessException {
if (Double.class.isAssignableFrom(fieldType)) {
field.set(object, value);
return true;
<T> boolean assignDouble(final Class<?> fieldType, final Field field, final Method fieldSetter, final T object,
final Object value) throws InvocationTargetException, IllegalAccessException {
boolean isDoubleAssigned = false;
if (double.class.isAssignableFrom(fieldType) || Double.class.isAssignableFrom(fieldType)) {
if (fieldSetter != null) {
fieldSetter.invoke(object, value);
isDoubleAssigned = true;
} else if (double.class.isAssignableFrom(fieldType)) {
final double doubleValue = (Double) value;
field.setDouble(object, doubleValue);
isDoubleAssigned = true;
} else if (Double.class.isAssignableFrom(fieldType)) {
field.set(object, value);
isDoubleAssigned = true;
}
}
if (Long.class.isAssignableFrom(fieldType)) {
field.set(object, Long.valueOf(((Double) value).longValue()));
return true;
return isDoubleAssigned;
}

<T> boolean assignLong(final Class<?> fieldType, final Field field, final Method fieldSetter, final T object,
final Object value) throws InvocationTargetException, IllegalAccessException {
boolean isLongAssigned = false;
if (long.class.isAssignableFrom(fieldType) || Long.class.isAssignableFrom(fieldType)) {
final long longValue = ((Double) value).longValue();
if (fieldSetter != null) {
fieldSetter.invoke(object, longValue);
isLongAssigned = true;
} else if (long.class.isAssignableFrom(fieldType)) {
field.setLong(object, longValue);
isLongAssigned = true;
} else if (Long.class.isAssignableFrom(fieldType)) {
field.set(object, longValue);
isLongAssigned = true;
}
}
if (Integer.class.isAssignableFrom(fieldType)) {
field.set(object, Integer.valueOf(((Double) value).intValue()));
return true;
return isLongAssigned;
}

<T> boolean assignInteger(final Class<?> fieldType, final Field field, final Method fieldSetter, final T object,
final Object value) throws InvocationTargetException, IllegalAccessException {
boolean isIntegerAssigned = false;
if (int.class.isAssignableFrom(fieldType) || Integer.class.isAssignableFrom(fieldType)) {
final int intValue = ((Double) value).intValue();
if (fieldSetter != null) {
fieldSetter.invoke(object, intValue);
isIntegerAssigned = true;
} else if (int.class.isAssignableFrom(fieldType)) {
field.setInt(object, intValue);
isIntegerAssigned = true;
} else if (Integer.class.isAssignableFrom(fieldType)) {
field.set(object, intValue);
isIntegerAssigned = true;
}
}
if (Boolean.class.isAssignableFrom(fieldType)) {
field.set(object, Boolean.valueOf(String.valueOf(value)));
return true;
return isIntegerAssigned;
}

<T> boolean assignBoolean(final Class<?> fieldType, final Field field, final Method fieldSetter, final T object,
final Object value) throws InvocationTargetException, IllegalAccessException {
boolean isBooleanAssigned = false;
if (boolean.class.isAssignableFrom(fieldType) || Boolean.class.isAssignableFrom(fieldType)) {
final boolean boolValue = Boolean.parseBoolean(String.valueOf(value));
if (fieldSetter != null) {
fieldSetter.invoke(object, boolValue);
isBooleanAssigned = true;
} else if (boolean.class.isAssignableFrom(fieldType)) {
field.setBoolean(object, boolValue);
isBooleanAssigned = true;
} else if (Boolean.class.isAssignableFrom(fieldType)) {
field.set(object, boolValue);
isBooleanAssigned = true;
}
}
return false;
return isBooleanAssigned;
}

private Long toMillis(final long value, final TimeUnit precision) {
Expand Down