All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.influxdb.client.internal.MeasurementMapper Maven / Gradle / Ivy

/*
 * The MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.influxdb.client.internal;

import java.lang.reflect.Field;
import java.time.Instant;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nonnull;

import com.influxdb.annotations.Column;
import com.influxdb.annotations.Measurement;
import com.influxdb.client.domain.WritePrecision;
import com.influxdb.client.write.Point;
import com.influxdb.exceptions.InfluxException;
import com.influxdb.utils.Arguments;

/**
 * @author Jakub Bednar (bednar@github) (15/10/2018 13:04)
 */
public final class MeasurementMapper {

    private static final Logger LOG = Logger.getLogger(MeasurementMapper.class.getName());

    private static final ConcurrentMap> CLASS_FIELD_CACHE
            = new ConcurrentHashMap<>();

    @Nonnull
     Point toPoint(@Nonnull final M measurement, @Nonnull final WritePrecision precision) throws InfluxException {

        Arguments.checkNotNull(measurement, "measurement");

        Class measurementType = measurement.getClass();
        cacheMeasurementClass(measurementType);

        Point point = Point.measurement(getMeasurementName(measurement, measurementType));

        for (Map.Entry entry : CLASS_FIELD_CACHE.get(measurementType.getName()).entrySet()) {
            String name = entry.getKey();
            Field field = entry.getValue();
            Column column = field.getAnnotation(Column.class);
            if (column.measurement()) {
                continue;
            }

            Object value = getObject(measurement, field);

            if (value == null) {
                Object[] params = {field.getName(), measurement};
                LOG.log(Level.FINEST, "Field {0} of {1} has null value", params);
                continue;
            }

            Class fieldType = field.getType();
            if (column.tag()) {
                point.addTag(name, value.toString());
            } else if (column.timestamp()) {
                Instant instant = (Instant) value;
                point.time(instant, precision);
            } else if (isNumber(fieldType)) {
                point.addField(name, (Number) value);
            } else if (Boolean.class.isAssignableFrom(fieldType) || boolean.class.isAssignableFrom(fieldType)) {
                point.addField(name, (Boolean) value);
            } else if (String.class.isAssignableFrom(fieldType)) {
                point.addField(name, (String) value);
            } else {
                point.addField(name, value.toString());
            }
        }

        LOG.log(Level.FINEST, "Mapped measurement: {0} to Point: {1}", new Object[]{measurement, point});

        return point;
    }

    @Nonnull
    private  String getMeasurementName(@Nonnull final M measurement, @Nonnull final Class measurementType) {

        // from @Measurement annotation for class
        Measurement measurementAnnotation = measurementType.getAnnotation(Measurement.class);
        if (measurementAnnotation != null) {
            return measurementAnnotation.name();
        }

        // from Field with @Column(measurement = true)
        Field measurementField = CLASS_FIELD_CACHE.get(measurementType.getName())
                .values()
                .stream()
                .filter(field -> field.getAnnotation(Column.class).measurement())
                .findFirst()
                .orElse(null);

        if (measurementField == null) {
            String message = String
                    .format("Unable to determine Measurement for '%s'. Does it have a @Measurement annotation or "
                            + "field with @Column(measurement = true) annotation?", measurementType);

            throw new InfluxException(message);
        }

        return getObject(measurement, measurementField).toString();
    }

    private  Object getObject(@Nonnull final M measurement, @Nonnull final Field field) {
        Object value;
        try {
            field.setAccessible(true);
            value = field.get(measurement);
        } catch (IllegalAccessException e) {

            throw new InfluxException(e);
        }
        return value;
    }

    private boolean isNumber(@Nonnull final Class fieldType) {
        return Number.class.isAssignableFrom(fieldType)
                || double.class.isAssignableFrom(fieldType)
                || long.class.isAssignableFrom(fieldType)
                || float.class.isAssignableFrom(fieldType)
                || int.class.isAssignableFrom(fieldType);
    }

    private void cacheMeasurementClass(@Nonnull final Class... measurementTypes) {

        for (Class measurementType : measurementTypes) {
            if (CLASS_FIELD_CACHE.containsKey(measurementType.getName())) {
                continue;
            }
            ConcurrentMap map = new ConcurrentHashMap<>();
            Class currentMeasurementType = measurementType;
            while (currentMeasurementType != null) {
                for (Field field : currentMeasurementType.getDeclaredFields()) {
                    Column colAnnotation = field.getAnnotation(Column.class);
                    if (colAnnotation != null) {
                        String name = colAnnotation.name();
                        if (name.isEmpty()) {
                            name = field.getName();
                        }
                        map.put(name, field);
                    }
                }

                currentMeasurementType = currentMeasurementType.getSuperclass();
            }

            CLASS_FIELD_CACHE.putIfAbsent(measurementType.getName(), map);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy