ObserverFactory.java

// Copyright (c) ZeroC, Inc.

package com.zeroc.IceMX;

import com.zeroc.Ice.MetricsAdminI;
import com.zeroc.Ice.MetricsMap;

import java.util.ArrayList;
import java.util.List;

/**
 * Factory for creating observer instances for metrics collection.
 *
 * @param <T> the metrics type
 * @param <O> the observer type
 */
public class ObserverFactory<T extends Metrics, O extends Observer<T>> {
    /**
     * Constructs an ObserverFactory.
     *
     * @param metrics the metrics admin instance
     * @param name the name of the metrics map
     * @param cl the metrics class
     */
    public ObserverFactory(MetricsAdminI metrics, String name, Class<T> cl) {
        _metrics = metrics;
        _name = name;
        _class = cl;
        _metrics.registerMap(
            name,
            _class,
            () -> {
                update();
            });
    }

    /**
     * Destroys this observer factory and unregisters the metrics map.
     */
    public void destroy() {
        if (_metrics != null) {
            _metrics.unregisterMap(_name);
        }
    }

    /**
     * Gets an observer instance for the specified helper.
     *
     * @param helper the metrics helper
     * @param cl the observer class
     * @return the observer instance, or null if no metrics maps are enabled
     */
    public O getObserver(MetricsHelper<T> helper, Class<O> cl) {
        return getObserver(helper, null, cl);
    }

    /**
     * Gets an observer instance for the specified helper and previous observer.
     *
     * @param helper the metrics helper
     * @param observer the previous observer, or null
     * @param cl the observer class
     * @return the observer instance, or null if no metrics maps are enabled
     */
    @SuppressWarnings("unchecked")
    public synchronized O getObserver(MetricsHelper<T> helper, Object observer, Class<O> cl) {
        O old = null;
        try {
            old = (O) observer;
        } catch (ClassCastException ex) {}
        List<MetricsMap<T>.Entry> metricsObjects = null;
        for (MetricsMap<T> m : _maps) {
            MetricsMap<T>.Entry e = m.getMatching(helper, old != null ? old.getEntry(m) : null);
            if (e != null) {
                if (metricsObjects == null) {
                    metricsObjects = new ArrayList<>(_maps.size());
                }
                metricsObjects.add(e);
            }
        }

        if (metricsObjects == null) {
            if (old != null) {
                old.detach();
            }
            return null;
        }

        O obsv;
        try {
            obsv = cl.getDeclaredConstructor().newInstance();
        } catch (Exception ex) {
            assert false;
            return null;
        }
        obsv.init(helper, metricsObjects, old);
        return obsv;
    }

    /**
     * Registers a sub-map for this observer factory.
     *
     * @param <S> the sub-metrics type
     * @param subMap the name of the sub-map
     * @param cl the sub-metrics class
     * @param field the field to associate with the sub-map
     */
    public <S extends Metrics> void registerSubMap(
            String subMap, Class<S> cl, java.lang.reflect.Field field) {
        _metrics.registerSubMap(_name, subMap, cl, field);
    }

    /**
     * Checks whether this observer factory is enabled.
     *
     * @return true if enabled (has active metrics maps), false otherwise
     */
    public boolean isEnabled() {
        return _enabled;
    }

    /**
     * Updates the metrics maps and enabled state.
     */
    public void update() {
        Runnable updater;
        synchronized (this) {
            _maps.clear();
            for (MetricsMap<T> m : _metrics.getMaps(_name, _class)) {
                _maps.add(m);
            }
            _enabled = !_maps.isEmpty();
            updater = _updater;
        }

        if (updater != null) {
            updater.run();
        }
    }

    /**
     * Sets an updater to be called when the factory is updated.
     *
     * @param updater the updater to call, or null
     */
    public synchronized void setUpdater(Runnable updater) {
        _updater = updater;
    }

    private final MetricsAdminI _metrics;
    private final String _name;
    private final Class<T> _class;
    private final List<MetricsMap<T>> _maps = new ArrayList<>();
    private volatile boolean _enabled;
    private Runnable _updater;
}