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

org.neo4j.kernel.monitoring.Monitors Maven / Gradle / Ivy

/*
 * Copyright (c) 2002-2015 "Neo Technology,"
 * Network Engine for Objects in Lund AB [http://neotechnology.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package org.neo4j.kernel.monitoring;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.neo4j.function.Predicate;
import org.neo4j.function.Predicates;
import org.neo4j.helpers.collection.Iterables;

import static org.neo4j.helpers.collection.Iterables.append;
import static org.neo4j.helpers.collection.Iterables.toArray;

/**
 * This can be used to create monitor instances using a Dynamic Proxy, which when invoked can delegate to any number of
 * listeners. Listeners typically also implement the monitor interface, but it's possible to use a reflective style
 * to either do generic listeners, or avoid the performance penalty of Method.invoke().
 *
 * The creation of monitors and registration of listeners may happen in any order. Listeners can be registered before
 * creating the actual monitor, and vice versa.
 *
 * Typically only the top level component that creates Monitors should have a reference to it. When creating subcomponents
 * that uses monitors they should get instances of the monitor interface in the constructor, or if they need to create them
 * on demand then pass in a {@link org.neo4j.function.Factory} for that monitor instead. This allows tests to not have to use
 * Monitors, and instead can pass in mocks or similar.
 *
 * The other type of component that would have direct references to the Monitors instance are those that actually implement
 * listening functionality, and must call addMonitorListener.
 *
 * Monitors is monitorable itself, through the {@link org.neo4j.kernel.monitoring.Monitors.Monitor} monitor interface.
 *
 * This class, and the proxy objects it produces, are thread-safe.
 */
public class Monitors
{
    public interface Monitor
    {
        void monitorCreated( Class monitorClass, String... tags );

        void monitorListenerException( Throwable throwable );

        class Adapter implements Monitor
        {
            @Override
            public void monitorCreated( Class monitorClass, String... tags )
            {
            }

            @Override
            public void monitorListenerException( Throwable throwable )
            {
            }
        }
    }

    // Concurrency: Mutation of these data structures is always guarded by the monitor lock on this Monitors instance,
    // while look-ups and reads are performed concurrently. The methodMonitorListerners lists (the map values) are
    // read concurrently by the proxies, while changing the listener set always produce new lists that atomically
    // replace the ones already in the methodMonitorListeners map.
    private final Map> methodMonitorListeners = new ConcurrentHashMap<>();
    private final List> monitoredInterfaces = new ArrayList<>();
    private final Map, MonitorListenerInvocationHandler> monitorListeners = new ConcurrentHashMap<>();
    private final Monitor monitorsMonitor;

    public Monitors()
    {
        monitorsMonitor = newMonitor( Monitor.class );
    }

    public synchronized  T newMonitor( Class monitorClass, Class owningClass, String... tags )
    {
        Iterable tagIer = append( owningClass.getName(), Iterables.iterable( tags ) );
        String[] tagArray = toArray( String.class, tagIer );
        return newMonitor( monitorClass, tagArray );
    }

    public synchronized  T newMonitor( Class monitorClass, String... tags )
    {
        if ( !monitoredInterfaces.contains( monitorClass ) )
        {
            monitoredInterfaces.add( monitorClass );

            for ( Method method : monitorClass.getMethods() )
            {
                recalculateMethodListeners( method );
            }
        }

        ClassLoader classLoader = monitorClass.getClassLoader();
        MonitorInvocationHandler monitorInvocationHandler = new MonitorInvocationHandler( tags );
        try
        {
            return monitorClass.cast( Proxy.newProxyInstance(
                    classLoader, new Class[]{monitorClass}, monitorInvocationHandler ) );
        }
        finally
        {
            if ( monitorsMonitor != null )
            {
                monitorsMonitor.monitorCreated( monitorClass, tags );
            }
        }
    }

    public synchronized void addMonitorListener( final Object monitorListener, String... tags )
    {
        MonitorListenerInvocationHandler monitorListenerInvocationHandler =
                tags.length == 0 ? new UntaggedMonitorListenerInvocationHandler( monitorListener )
                                 : new TaggedMonitorListenerInvocationHandler( monitorListener, tags );

        for ( Class monitorInterface : getInterfacesOf( monitorListener.getClass() ) )
        {
            for ( final Method method : monitorInterface.getMethods() )
            {
                monitorListeners.put(
                        Predicates.equalTo( method ),
                        monitorListenerInvocationHandler );

                recalculateMethodListeners( method );
            }
        }
    }

    public synchronized void removeMonitorListener( Object monitorListener )
    {
        Iterator, MonitorListenerInvocationHandler>> iter =
                monitorListeners.entrySet().iterator();

        while ( iter.hasNext() )
        {
            Map.Entry, MonitorListenerInvocationHandler> handlerEntry = iter.next();
            if ( handlerEntry.getValue() instanceof UntaggedMonitorListenerInvocationHandler )
            {
                UntaggedMonitorListenerInvocationHandler handler =
                        (UntaggedMonitorListenerInvocationHandler) handlerEntry.getValue();

                if ( handler.getMonitorListener() == monitorListener )
                {
                    iter.remove();
                }
            }
        }

        recalculateAllMethodListeners();
    }

    public synchronized void addMonitorListener(
            MonitorListenerInvocationHandler invocationHandler, Predicate methodSpecification )
    {
        monitorListeners.put( methodSpecification, invocationHandler );

        recalculateAllMethodListeners();
    }

    public synchronized void removeMonitorListener( MonitorListenerInvocationHandler invocationHandler )
    {
        Iterator, MonitorListenerInvocationHandler>> iter =
                monitorListeners.entrySet().iterator();

        while ( iter.hasNext() )
        {
            Map.Entry, MonitorListenerInvocationHandler> handlerEntry = iter.next();
            if ( handlerEntry.getValue() == invocationHandler )
            {
                iter.remove();
                recalculateAllMethodListeners();
                return;
            }
        }
    }

    private void recalculateMethodListeners( Method method )
    {
        List listeners = new ArrayList<>();
        for ( Map.Entry, MonitorListenerInvocationHandler> handlerEntry : monitorListeners.entrySet() )
        {
            if ( handlerEntry.getKey().test( method ) )
            {
                listeners.add( handlerEntry.getValue() );
            }
        }
        methodMonitorListeners.put( method, listeners );
    }

    private void recalculateAllMethodListeners()
    {
        for ( Method method : methodMonitorListeners.keySet() )
        {
            recalculateMethodListeners( method );
        }
    }

    private Iterable> getInterfacesOf( Class aClass )
    {
        List> interfaces = new ArrayList<>();
        while ( aClass != null )
        {
            Collections.addAll( interfaces, aClass.getInterfaces() );
            aClass = aClass.getSuperclass();
        }
        return interfaces;
    }

    private static class UntaggedMonitorListenerInvocationHandler implements MonitorListenerInvocationHandler
    {
        private final Object monitorListener;

        public UntaggedMonitorListenerInvocationHandler( Object monitorListener )
        {
            this.monitorListener = monitorListener;
        }

        public Object getMonitorListener()
        {
            return monitorListener;
        }

        @Override
        public void invoke( Object proxy, Method method, Object[] args, String... tags )
                throws Throwable
        {
            method.invoke( monitorListener, args );
        }
    }

    private static class TaggedMonitorListenerInvocationHandler
            extends UntaggedMonitorListenerInvocationHandler
    {
        private final String[] tags;

        public TaggedMonitorListenerInvocationHandler( Object monitorListener, String... tags )
        {
            super( monitorListener );
            this.tags = tags;
        }

        @Override
        public void invoke( Object proxy, Method method, Object[] args, String... tags ) throws Throwable
        {
            required:
            for ( String requiredTag : this.tags )
            {
                for ( String tag : tags )
                {
                    if ( requiredTag.equals( tag ) )
                    {
                        continue required;
                    }
                }
                return; // Not all required tags present
            }

            super.invoke( proxy, method, args, tags );
        }
    }

    private class MonitorInvocationHandler implements InvocationHandler
    {
        private String[] tags;

        public MonitorInvocationHandler( String... tags )
        {
            this.tags = tags;
        }

        @Override
        public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable
        {
            invokeMonitorListeners( proxy, method, args );
            return null;
        }

        private void invokeMonitorListeners( Object proxy, Method method, Object[] args )
        {
            List handlers = methodMonitorListeners.get( method );

            if ( handlers != null )
            {
                for ( MonitorListenerInvocationHandler monitorListenerInvocationHandler : handlers )
                {
                    try
                    {
                        monitorListenerInvocationHandler.invoke( proxy, method, args, tags );
                    }
                    catch ( Throwable e )
                    {
                        if ( !method.getDeclaringClass().equals( Monitor.class ) )
                        {
                            monitorsMonitor.monitorListenerException( e );
                        }
                    }
                }
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy