Observer.java

// Copyright (c) ZeroC, Inc.

package com.zeroc.IceMX;

import com.zeroc.Ice.MetricsMap;

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

/**
 * Observer implementation for metrics collection.
 *
 * @param <T> the metrics type
 */
public class Observer<T extends Metrics> extends StopWatch
    implements com.zeroc.Ice.Instrumentation.Observer {
    /**
     * Interface for updating metrics objects.
     *
     * @param <T> the metrics type
     */
    public interface MetricsUpdate<T> {
        /**
         * Updates the metrics object.
         *
         * @param m the metrics object to update
         */
        void update(T m);
    }

    @Override
    public void attach() {
        if (!isStarted()) {
            start();
        }
    }

    @Override
    public void detach() {
        long lifetime = _previousDelay + stop();
        for (MetricsMap<T>.Entry e : _objects) {
            e.detach(lifetime);
        }
    }

    @Override
    public void failed(String exceptionName) {
        for (MetricsMap<T>.Entry e : _objects) {
            e.failed(exceptionName);
        }
    }

    /**
     * Executes the specified update function on all metrics objects.
     *
     * @param u the update function to execute
     */
    public void forEach(MetricsUpdate<T> u) {
        for (MetricsMap<T>.Entry e : _objects) {
            e.execute(u);
        }
    }

    /**
     * Initializes this observer with the specified parameters.
     *
     * @param helper the metrics helper
     * @param objects the list of metrics map entries
     * @param previous the previous observer, or null
     */
    public void init(
            MetricsHelper<T> helper,
            List<MetricsMap<T>.Entry> objects,
            Observer<T> previous) {
        _objects = objects;

        if (previous == null) {
            return;
        }

        _previousDelay = previous._previousDelay + previous.delay();

        //
        // Detach entries from previous observer which are no longer
        // attached to this new observer.
        //
        for (MetricsMap<T>.Entry p : previous._objects) {
            if (!_objects.contains(p)) {
                p.detach(_previousDelay);
            }
        }
    }

    /**
     * Gets a sub-observer for the specified metrics type.
     *
     * @param <S> the sub-metrics type
     * @param <ObserverImpl> the observer implementation type
     * @param mapName the name of the metrics map
     * @param helper the metrics helper for the sub-metrics type
     * @param mcl the metrics class
     * @param ocl the observer class
     * @return the sub-observer, or null if no matching entries are found
     */
    public <S extends Metrics, ObserverImpl extends Observer<S>> ObserverImpl getObserver(
            String mapName, MetricsHelper<S> helper, Class<S> mcl, Class<ObserverImpl> ocl) {
        List<MetricsMap<S>.Entry> metricsObjects = null;
        for (MetricsMap<T>.Entry entry : _objects) {
            MetricsMap<S>.Entry e = entry.getMatching(mapName, helper, mcl);
            if (e != null) {
                if (metricsObjects == null) {
                    metricsObjects = new ArrayList<>(_objects.size());
                }
                metricsObjects.add(e);
            }
        }

        if (metricsObjects == null) {
            return null;
        }

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

    /**
     * Gets the metrics map entry for the specified map.
     *
     * @param map the metrics map to find
     * @return the matching entry, or null if not found
     */
    public MetricsMap<T>.Entry getEntry(MetricsMap<?> map) {
        for (MetricsMap<T>.Entry e : _objects) {
            if (e.getMap() == map) {
                return e;
            }
        }
        return null;
    }

    private List<MetricsMap<T>.Entry> _objects;
    private long _previousDelay;
}