CommunicatorObserverI.java

// Copyright (c) ZeroC, Inc.

package com.zeroc.Ice;

import com.zeroc.Ice.Instrumentation.CommunicatorObserver;
import com.zeroc.Ice.Instrumentation.ConnectionObserver;
import com.zeroc.Ice.Instrumentation.ConnectionState;
import com.zeroc.Ice.Instrumentation.DispatchObserver;
import com.zeroc.Ice.Instrumentation.InvocationObserver;
import com.zeroc.Ice.Instrumentation.Observer;
import com.zeroc.Ice.Instrumentation.ObserverUpdater;
import com.zeroc.Ice.Instrumentation.ThreadObserver;
import com.zeroc.Ice.Instrumentation.ThreadState;
import com.zeroc.IceMX.CollocatedMetrics;
import com.zeroc.IceMX.ConnectionMetrics;
import com.zeroc.IceMX.DispatchMetrics;
import com.zeroc.IceMX.InvocationMetrics;
import com.zeroc.IceMX.Metrics;
import com.zeroc.IceMX.MetricsHelper;
import com.zeroc.IceMX.ObserverFactoryWithDelegate;
import com.zeroc.IceMX.ObserverWithDelegateI;
import com.zeroc.IceMX.RemoteMetrics;
import com.zeroc.IceMX.ThreadMetrics;

import java.util.Map;

/**
 * @hidden Public because it's used by IceMX (via reflection).
 */
public class CommunicatorObserverI implements CommunicatorObserver {
    static void addEndpointAttributes(MetricsHelper.AttributeResolver r, Class<?> cl)
        throws Exception {
        r.add("endpoint", cl.getDeclaredMethod("getEndpoint"));

        Class<?> cli = EndpointInfo.class;
        r.add(
            "endpointType",
            cl.getDeclaredMethod("getEndpointInfo"),
            cli.getDeclaredMethod("type"));
        r.add(
            "endpointIsDatagram",
            cl.getDeclaredMethod("getEndpointInfo"),
            cli.getDeclaredMethod("datagram"));
        r.add(
            "endpointIsSecure",
            cl.getDeclaredMethod("getEndpointInfo"),
            cli.getDeclaredMethod("secure"));
        r.add(
            "endpointCompress",
            cl.getDeclaredMethod("getEndpointInfo"),
            cli.getDeclaredField("compress"));

        cli = IPEndpointInfo.class;
        r.add(
            "endpointHost",
            cl.getDeclaredMethod("getEndpointInfo"),
            cli.getDeclaredField("host"));
        r.add(
            "endpointPort",
            cl.getDeclaredMethod("getEndpointInfo"),
            cli.getDeclaredField("port"));
    }

    static void addConnectionAttributes(MetricsHelper.AttributeResolver r, Class<?> cl)
        throws Exception {
        Class<?> cli = ConnectionInfo.class;
        r.add(
            "incoming",
            cl.getDeclaredMethod("getConnectionInfo"),
            cli.getDeclaredField("incoming"));
        r.add(
            "adapterName",
            cl.getDeclaredMethod("getConnectionInfo"),
            cli.getDeclaredField("adapterName"));
        r.add(
            "connectionId",
            cl.getDeclaredMethod("getConnectionInfo"),
            cli.getDeclaredField("connectionId"));

        cli = IPConnectionInfo.class;
        r.add(
            "localHost",
            cl.getDeclaredMethod("getConnectionInfo"),
            cli.getDeclaredField("localAddress"));
        r.add(
            "localPort",
            cl.getDeclaredMethod("getConnectionInfo"),
            cli.getDeclaredField("localPort"));
        r.add(
            "remoteHost",
            cl.getDeclaredMethod("getConnectionInfo"),
            cli.getDeclaredField("remoteAddress"));
        r.add(
            "remotePort",
            cl.getDeclaredMethod("getConnectionInfo"),
            cli.getDeclaredField("remotePort"));

        cli = UDPConnectionInfo.class;
        r.add(
            "mcastHost",
            cl.getDeclaredMethod("getConnectionInfo"),
            cli.getDeclaredField("mcastAddress"));
        r.add(
            "mcastPort",
            cl.getDeclaredMethod("getConnectionInfo"),
            cli.getDeclaredField("mcastPort"));

        addEndpointAttributes(r, cl);
    }

    public static class ConnectionHelper extends MetricsHelper<ConnectionMetrics> {
        private static AttributeResolver _attributes =
            new AttributeResolver() {
                {
                    try {
                        add("parent", ConnectionHelper.class.getDeclaredMethod("getParent"));
                        add("id", ConnectionHelper.class.getDeclaredMethod("getId"));
                        add("state", ConnectionHelper.class.getDeclaredMethod("getState"));
                        addConnectionAttributes(this, ConnectionHelper.class);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        assert false;
                    }
                }
            };

        ConnectionHelper(ConnectionInfo con, Endpoint endpt, ConnectionState state) {
            super(_attributes);
            _connectionInfo = con;
            _endpoint = endpt;
            _state = state;
        }

        public String getId() {
            if (_id == null) {
                StringBuilder os = new StringBuilder();
                IPConnectionInfo info = getIPConnectionInfo();
                if (info != null) {
                    os.append(info.localAddress).append(':').append(info.localPort);
                    os.append(" -> ");
                    os.append(info.remoteAddress).append(':').append(info.remotePort);
                } else {
                    os.append("connection-").append(_connectionInfo);
                }
                if (!_connectionInfo.connectionId.isEmpty()) {
                    os.append(" [").append(_connectionInfo.connectionId).append("]");
                }
                _id = os.toString();
            }
            return _id;
        }

        public String getState() {
            switch (_state) {
                case ConnectionStateValidating:
                    return "validating";
                case ConnectionStateHolding:
                    return "holding";
                case ConnectionStateActive:
                    return "active";
                case ConnectionStateClosing:
                    return "closing";
                case ConnectionStateClosed:
                    return "closed";
                default:
                    assert false;
                    return "";
            }
        }

        public String getParent() {
            if (_connectionInfo.adapterName != null && !_connectionInfo.adapterName.isEmpty()) {
                return _connectionInfo.adapterName;
            } else {
                return "Communicator";
            }
        }

        public ConnectionInfo getConnectionInfo() {
            return _connectionInfo;
        }

        public Endpoint getEndpoint() {
            return _endpoint;
        }

        public EndpointInfo getEndpointInfo() {
            if (_endpointInfo == null) {
                _endpointInfo = _endpoint.getInfo();
            }
            return _endpointInfo;
        }

        private IPConnectionInfo getIPConnectionInfo() {
            for (ConnectionInfo p = _connectionInfo; p != null; p = p.underlying) {
                if (p instanceof IPConnectionInfo) {
                    return (IPConnectionInfo) p;
                }
            }
            return null;
        }

        private final ConnectionInfo _connectionInfo;
        private final Endpoint _endpoint;
        private final ConnectionState _state;
        private String _id;
        private EndpointInfo _endpointInfo;
    }

    public static final class DispatchHelper extends MetricsHelper<DispatchMetrics> {
        private static final AttributeResolver _attributes =
            new AttributeResolver() {
                {
                    try {
                        Class<?> cl = DispatchHelper.class;
                        add("parent", cl.getDeclaredMethod("getParent"));
                        add("id", cl.getDeclaredMethod("getId"));

                        addConnectionAttributes(this, cl);

                        Class<?> clc = Current.class;
                        add(
                            "operation",
                            cl.getDeclaredMethod("getCurrent"),
                            clc.getDeclaredField("operation"));
                        add("identity", cl.getDeclaredMethod("getIdentity"));
                        add(
                            "facet",
                            cl.getDeclaredMethod("getCurrent"),
                            clc.getDeclaredField("facet"));
                        add(
                            "requestId",
                            cl.getDeclaredMethod("getCurrent"),
                            clc.getDeclaredField("requestId"));
                        add("mode", cl.getDeclaredMethod("getMode"));
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        assert false;
                    }
                }
            };

        DispatchHelper(Current current, int size) {
            super(_attributes);
            _current = current;
            _size = size;
        }

        @Override
        public void initMetrics(DispatchMetrics v) {
            v.size += _size;
        }

        @Override
        protected String defaultResolve(String attribute) {
            if (attribute.indexOf("context.", 0) == 0) {
                String v = _current.ctx.get(attribute.substring(8));
                if (v != null) {
                    return v;
                }
            }
            throw new IllegalArgumentException(attribute);
        }

        public String getMode() {
            return _current.requestId == 0 ? "oneway" : "twoway";
        }

        public String getId() {
            if (_id == null) {
                StringBuilder os = new StringBuilder();
                if (_current.id.category != null && !_current.id.category.isEmpty()) {
                    os.append(_current.id.category).append('/');
                }
                os.append(_current.id.name).append(" [").append(_current.operation).append(']');
                _id = os.toString();
            }
            return _id;
        }

        public int getRequestId() {
            return _current.requestId;
        }

        public String getParent() {
            return _current.adapter.getName();
        }

        public ConnectionInfo getConnectionInfo() {
            if (_current.con != null) {
                return _current.con.getInfo();
            }
            return null;
        }

        public Endpoint getEndpoint() {
            if (_current.con != null) {
                return _current.con.getEndpoint();
            }
            return null;
        }

        public Connection getConnection() {
            return _current.con;
        }

        public EndpointInfo getEndpointInfo() {
            if (_current.con != null && _endpointInfo == null) {
                _endpointInfo = _current.con.getEndpoint().getInfo();
            }
            return _endpointInfo;
        }

        public Current getCurrent() {
            return _current;
        }

        public String getIdentity() {
            return _current.adapter.getCommunicator().identityToString(_current.id);
        }

        private final Current _current;
        private final int _size;
        private String _id;
        private EndpointInfo _endpointInfo;
    }

    public static final class InvocationHelper extends MetricsHelper<InvocationMetrics> {
        private static final AttributeResolver _attributes =
            new AttributeResolver() {
                {
                    try {
                        Class<?> cl = InvocationHelper.class;
                        add("parent", cl.getDeclaredMethod("getParent"));
                        add("id", cl.getDeclaredMethod("getId"));

                        add("operation", cl.getDeclaredMethod("getOperation"));
                        add("identity", cl.getDeclaredMethod("getIdentity"));

                        Class<?> cli = ObjectPrx.class;
                        add(
                            "facet",
                            cl.getDeclaredMethod("getProxy"),
                            cli.getDeclaredMethod("ice_getFacet"));
                        add("encoding", cl.getDeclaredMethod("getEncodingVersion"));
                        add("mode", cl.getDeclaredMethod("getMode"));
                        add("proxy", cl.getDeclaredMethod("getProxy"));
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        assert false;
                    }
                }
            };

        InvocationHelper(ObjectPrx proxy, String op, Map<String, String> ctx) {
            super(_attributes);
            _proxy = proxy;
            _operation = op;
            _context = ctx;
        }

        @Override
        protected String defaultResolve(String attribute) {
            if (attribute.indexOf("context.", 0) == 0) {
                String v = _context.get(attribute.substring(8));
                if (v != null) {
                    return v;
                }
            }
            throw new IllegalArgumentException(attribute);
        }

        public String getMode() {
            if (_proxy == null) {
                throw new IllegalArgumentException("mode");
            }

            if (_proxy.ice_isTwoway()) {
                return "twoway";
            } else if (_proxy.ice_isOneway()) {
                return "oneway";
            } else if (_proxy.ice_isBatchOneway()) {
                return "batch-oneway";
            } else if (_proxy.ice_isDatagram()) {
                return "datagram";
            } else if (_proxy.ice_isBatchDatagram()) {
                return "batch-datagram";
            } else {
                throw new IllegalArgumentException("mode");
            }
        }

        public String getId() {
            if (_id == null) {
                if (_proxy != null) {
                    StringBuilder os = new StringBuilder();
                    try {
                        os.append(_proxy.ice_endpoints(emptyEndpoints))
                            .append(" [")
                            .append(_operation)
                            .append(']');
                    } catch (Exception ex) {
                        // Either a fixed proxy or the communicator is destroyed.
                        os.append(_proxy.ice_getCommunicator()
                            .identityToString(_proxy.ice_getIdentity()));
                        os.append(" [").append(_operation).append(']');
                    }
                    _id = os.toString();
                } else {
                    _id = _operation;
                }
            }
            return _id;
        }

        public String getParent() {
            return "Communicator";
        }

        public ObjectPrx getProxy() {
            return _proxy;
        }

        public String getIdentity() {
            if (_proxy != null) {
                return _proxy.ice_getCommunicator().identityToString(_proxy.ice_getIdentity());
            } else {
                return "";
            }
        }

        public String getOperation() {
            return _operation;
        }

        public String getEncodingVersion() {
            return Util.encodingVersionToString(_proxy.ice_getEncodingVersion());
        }

        private final ObjectPrx _proxy;
        private final String _operation;
        private final Map<String, String> _context;
        private String _id;

        private static final Endpoint[] emptyEndpoints = new Endpoint[0];
    }

    public static final class ThreadHelper extends MetricsHelper<ThreadMetrics> {
        private static final AttributeResolver _attributes =
            new AttributeResolver() {
                {
                    try {
                        add("parent", ThreadHelper.class.getDeclaredField("_parent"));
                        add("id", ThreadHelper.class.getDeclaredField("_id"));
                    } catch (Exception ex) {
                        assert false;
                    }
                }
            };

        ThreadHelper(String parent, String id, ThreadState state) {
            super(_attributes);
            _parent = parent;
            _id = id;
            _state = state;
        }

        @Override
        public void initMetrics(ThreadMetrics v) {
            switch (_state) {
                case ThreadStateInUseForIO:
                    ++v.inUseForIO;
                    break;
                case ThreadStateInUseForUser:
                    ++v.inUseForUser;
                    break;
                case ThreadStateInUseForOther:
                    ++v.inUseForOther;
                    break;
                default:
                    break;
            }
        }

        public final String _parent;
        public final String _id;
        private final ThreadState _state;
    }

    public static final class EndpointHelper extends MetricsHelper<Metrics> {
        private static final AttributeResolver _attributes =
            new AttributeResolver() {
                {
                    try {
                        add("parent", EndpointHelper.class.getDeclaredMethod("getParent"));
                        add("id", EndpointHelper.class.getDeclaredMethod("getId"));
                        addEndpointAttributes(this, EndpointHelper.class);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        assert false;
                    }
                }
            };

        EndpointHelper(Endpoint endpt, String id) {
            super(_attributes);
            _endpoint = endpt;
            _id = id;
        }

        EndpointHelper(Endpoint endpt) {
            super(_attributes);
            _endpoint = endpt;
        }

        public EndpointInfo getEndpointInfo() {
            if (_endpointInfo == null) {
                _endpointInfo = _endpoint.getInfo();
            }
            return _endpointInfo;
        }

        public String getParent() {
            return "Communicator";
        }

        public String getId() {
            if (_id == null) {
                _id = _endpoint.toString();
            }
            return _id;
        }

        public String getEndpoint() {
            return _endpoint.toString();
        }

        private final Endpoint _endpoint;
        private String _id;
        private EndpointInfo _endpointInfo;
    }

    public CommunicatorObserverI(InitializationData initData) {
        _metrics = new MetricsAdminI(initData.properties, initData.logger);
        _delegate = initData.observer;

        _connections =
            new ObserverFactoryWithDelegate<
                ConnectionMetrics, ConnectionObserverI, ConnectionObserver>(
                _metrics, "Connection", ConnectionMetrics.class);
        _dispatch =
            new ObserverFactoryWithDelegate<
                DispatchMetrics, DispatchObserverI, DispatchObserver>(
                _metrics, "Dispatch", DispatchMetrics.class);
        _invocations =
            new ObserverFactoryWithDelegate<
                InvocationMetrics, InvocationObserverI, InvocationObserver>(
                _metrics, "Invocation", InvocationMetrics.class);
        _threads =
            new ObserverFactoryWithDelegate<ThreadMetrics, ThreadObserverI, ThreadObserver>(
                _metrics, "Thread", ThreadMetrics.class);
        _connects =
            new ObserverFactoryWithDelegate<Metrics, ObserverWithDelegateI, Observer>(
                _metrics, "ConnectionEstablishment", Metrics.class);
        _endpointLookups =
            new ObserverFactoryWithDelegate<Metrics, ObserverWithDelegateI, Observer>(
                _metrics, "EndpointLookup", Metrics.class);

        try {
            _invocations.registerSubMap(
                "Remote",
                RemoteMetrics.class,
                InvocationMetrics.class.getDeclaredField("remotes"));
            _invocations.registerSubMap(
                "Collocated",
                CollocatedMetrics.class,
                InvocationMetrics.class.getDeclaredField("collocated"));
        } catch (Exception ex) {
            assert false;
        }
    }

    @Override
    public Observer getConnectionEstablishmentObserver(Endpoint endpt, String connector) {
        if (_connects.isEnabled()) {
            try {
                Observer delegate = null;
                if (_delegate != null) {
                    delegate = _delegate.getConnectionEstablishmentObserver(endpt, connector);
                }
                return _connects.getObserver(
                    new EndpointHelper(endpt, connector),
                    ObserverWithDelegateI.class,
                    delegate);
            } catch (Exception ex) {
                _metrics.getLogger()
                    .error(
                        "unexpected exception trying to obtain observer:\n"
                            + Ex.toString(ex));
            }
        }
        return null;
    }

    @Override
    public Observer getEndpointLookupObserver(Endpoint endpt) {
        if (_endpointLookups.isEnabled()) {
            try {
                Observer delegate = null;
                if (_delegate != null) {
                    delegate = _delegate.getEndpointLookupObserver(endpt);
                }
                return _endpointLookups.getObserver(
                    new EndpointHelper(endpt), ObserverWithDelegateI.class, delegate);
            } catch (Exception ex) {
                _metrics.getLogger()
                    .error(
                        "unexpected exception trying to obtain observer:\n"
                            + Ex.toString(ex));
            }
        }
        return null;
    }

    @Override
    public ConnectionObserver getConnectionObserver(
            ConnectionInfo c, Endpoint e, ConnectionState s, ConnectionObserver observer) {
        if (_connections.isEnabled()) {
            try {
                ConnectionObserver delegate = null;
                ConnectionObserverI o =
                    observer instanceof ConnectionObserverI
                        ? (ConnectionObserverI) observer
                        : null;
                if (_delegate != null) {
                    delegate =
                        _delegate.getConnectionObserver(
                            c, e, s, o != null ? o.getDelegate() : observer);
                }
                return _connections.getObserver(
                    new ConnectionHelper(c, e, s), o, ConnectionObserverI.class, delegate);
            } catch (Exception ex) {
                _metrics.getLogger()
                    .error(
                        "unexpected exception trying to obtain observer:\n"
                            + Ex.toString(ex));
            }
        }
        return null;
    }

    @Override
    public ThreadObserver getThreadObserver(
            String parent, String id, ThreadState s, ThreadObserver observer) {
        if (_threads.isEnabled()) {
            try {
                ThreadObserver delegate = null;
                ThreadObserverI o =
                    observer instanceof ThreadObserverI ? (ThreadObserverI) observer : null;
                if (_delegate != null) {
                    delegate =
                        _delegate.getThreadObserver(
                            parent, id, s, o != null ? o.getDelegate() : observer);
                }
                return _threads.getObserver(
                    new ThreadHelper(parent, id, s), o, ThreadObserverI.class, delegate);
            } catch (Exception ex) {
                _metrics.getLogger()
                    .error(
                        "unexpected exception trying to obtain observer:\n"
                            + Ex.toString(ex));
            }
        }
        return null;
    }

    @Override
    public InvocationObserver getInvocationObserver(
            ObjectPrx prx, String operation, Map<String, String> ctx) {
        if (_invocations.isEnabled()) {
            try {
                InvocationObserver delegate = null;
                if (_delegate != null) {
                    delegate = _delegate.getInvocationObserver(prx, operation, ctx);
                }
                return _invocations.getObserver(
                    new InvocationHelper(prx, operation, ctx),
                    InvocationObserverI.class,
                    delegate);
            } catch (Exception ex) {
                _metrics.getLogger()
                    .error(
                        "unexpected exception trying to obtain observer:\n"
                            + Ex.toString(ex));
            }
        }
        return null;
    }

    @Override
    public DispatchObserver getDispatchObserver(Current c, int size) {
        if (_dispatch.isEnabled()) {
            try {
                DispatchObserver delegate = null;
                if (_delegate != null) {
                    delegate = _delegate.getDispatchObserver(c, size);
                }
                return _dispatch.getObserver(
                    new DispatchHelper(c, size), DispatchObserverI.class, delegate);
            } catch (Exception ex) {
                _metrics.getLogger()
                    .error(
                        "unexpected exception trying to obtain observer:\n"
                            + Ex.toString(ex));
            }
        }
        return null;
    }

    @Override
    public void setObserverUpdater(final ObserverUpdater updater) {
        if (updater == null) {
            _connections.setUpdater(null);
            _threads.setUpdater(null);
        } else {
            _connections.setUpdater(
                () -> {
                    updater.updateConnectionObservers();
                });
            _threads.setUpdater(
                () -> {
                    updater.updateThreadObservers();
                });
        }

        if (_delegate != null) {
            _delegate.setObserverUpdater(updater);
        }
    }

    public MetricsAdminI getFacet() {
        return _metrics;
    }

    private final MetricsAdminI _metrics;
    private final CommunicatorObserver _delegate;
    private final ObserverFactoryWithDelegate<
        ConnectionMetrics, ConnectionObserverI, ConnectionObserver>
        _connections;
    private final ObserverFactoryWithDelegate<DispatchMetrics, DispatchObserverI, DispatchObserver>
        _dispatch;
    private final ObserverFactoryWithDelegate<
        InvocationMetrics, InvocationObserverI, InvocationObserver>
        _invocations;
    private final ObserverFactoryWithDelegate<ThreadMetrics, ThreadObserverI, ThreadObserver>
        _threads;
    private final ObserverFactoryWithDelegate<Metrics, ObserverWithDelegateI, Observer> _connects;
    private final ObserverFactoryWithDelegate<Metrics, ObserverWithDelegateI, Observer>
        _endpointLookups;
}