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

com.nerdvision.agent.AgentImpl Maven / Gradle / Ivy

package com.nerdvision.agent;

import com.nerdvision.agent.api.INVError;
import com.nerdvision.agent.api.INerdVision;
import com.nerdvision.agent.api.INerdVisionHook;
import com.nerdvision.agent.api.decorators.IDecorator;
import com.nerdvision.agent.api.logging.ILogger;
import com.nerdvision.agent.api.reflection.IReflection;
import com.nerdvision.agent.inst.InstrumentationBreakpointService;
import com.nerdvision.agent.inst.PluginTransformer;
import com.nerdvision.agent.snapshot.Callback;
import com.nerdvision.agent.snapshot.EventSnapshot;
import com.nerdvision.agent.snapshot.NVError;
import java.lang.instrument.Instrumentation;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.nerdvision.agent.ClientReg.backoff;

public class AgentImpl
{
    public static final String PRODUCT_NAME = "nerd.vision";

    private static volatile boolean shutdown = false;
    private static Logger logger;
    static IBreakpointService breakpointService;
    private static Thread thread;
    static ClientReg clientReg;
    private static HTTPClient httpClient;
    private static Settings settings;
    static ClientConfig clientConfig;


    public static void startup( final JarFile jarFile, final Instrumentation inst, final Map args )
    {
        final Attributes manifest = getManifest( jarFile );

        settings = Settings.build( args );

        logger = configureLogging( settings );
        announceClient( manifest, logger );

        clientConfig = new ClientConfig();

        final String uuid = UUID.randomUUID().toString();
        httpClient = new HTTPClient( uuid, settings.getSettingAs( "api.key", String.class ), clientConfig );

        clientReg = new ClientReg( settings, manifest, jarFile.getName(), uuid, httpClient );

        final Boolean lambdaSetting = settings.getSettingAs( "lambda", Boolean.class );
        final Boolean lambda = lambdaSetting == null ? Boolean.FALSE : lambdaSetting;

        breakpointService = new InstrumentationBreakpointService( inst, settings );

        // TODO: 16.07.21 Can we scan the jarFile for plugins ? see FR MixinTransformer2
        PluginTransformer.init( inst, settings );

        Callback.init( settings, breakpointService, httpClient, clientConfig, lambda );

        final ThreadGroup threadGroup = new ThreadGroup( PRODUCT_NAME );
        thread = createThread( threadGroup, lambda, clientReg, settings, breakpointService, logger );
        thread.setDaemon( true );
        thread.setName( PRODUCT_NAME );
        thread.start();

        @SuppressWarnings("Convert2Lambda")
        final Thread hook = new Thread( threadGroup, new Runnable()
        {
            @Override
            public void run()
            {
                shutdown = true;
                thread.interrupt();
            }
        } );
        hook.setName( "nerd.vision shutdown" );
        Runtime.getRuntime().addShutdownHook( hook );

        if( lambda )
        {
            try
            {
                thread.join();
            }
            catch( InterruptedException e )
            {
                e.printStackTrace();
            }
        }

    }


    static Thread createThread( final ThreadGroup threadGroup,
                                final Boolean lambda,
                                final ClientReg clientReg,
                                final Settings settings,
                                final IBreakpointService breakpointService,
                                final Logger logger )
    {
        if( !lambda )
        {
            final long backoffMax = settings.getSettingAs( "grpc.backoff.max", long.class );
            final double maxMultiplier = settings.getSettingAs( "grpc.backoff.multiplier", double.class );
            //noinspection Convert2Lambda
            return new Thread( threadGroup, new Runnable()
            {
                @Override
                public void run()
                {
                    final ClientReg.Response session = clientReg.getSessionWithBreakpoints();
                    logger.debug( "Session id: {}", session.getSession() );

                    AgentImpl.clientConfig.setSessionId( session.getSession() );
                    AgentImpl.clientConfig.setTags( session.getTags() );
                    AgentImpl.clientConfig.update( session.getConfig() );

                    int cnt = 0;
                    while( !shutdown )
                    {
                        try( final GrpcService grpcService = new GrpcService( settings, AgentImpl.breakpointService, AgentImpl.clientConfig ); )
                        {
                            grpcService.connect( session.getSession() );
                            grpcService.await();
                            if( !grpcService.hasFailed() )
                            {
                                cnt = 0; // reset cnt as we have connected successfully
                            }
                        }
                        catch( Throwable t )
                        {
                            t.printStackTrace();
                        }
                        finally
                        {
                            if( !shutdown )
                            {
                                long delay = backoff( cnt++, backoffMax, maxMultiplier );
                                logger.debug( "Attempting to reconnect GRPC waiting {}", delay );
                                if( delay > 0 )
                                {
                                    synchronized( this )
                                    {
                                        try
                                        {
                                            wait( delay * 1000 );
                                        }
                                        catch( InterruptedException e )
                                        {
                                            logger.debug( "Interrupted during wait." );
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }, "nerd.vision - GRPC" );
        }
        else
        {
            //noinspection Convert2Lambda
            return new Thread( threadGroup, new Runnable()
            {
                @Override
                public void run()
                {
                    try
                    {
                        final ClientReg.Response session = clientReg.getSessionWithBreakpoints();

                        AgentImpl.clientConfig.setSessionId( session.getSession() );
                        AgentImpl.clientConfig.setTags( session.getTags() );
                        AgentImpl.clientConfig.update( session.getConfig() );

                        breakpointService.processBreakpoints( session.getBreakpoints() );
                    }
                    catch( Throwable t )
                    {
                        t.printStackTrace();
                    }
                }
            }, "nerd.vision - Service Thread" );
        }
    }


    public static Logger configureLogging( final Settings settings )
    {
        final java.util.logging.Logger logger = java.util.logging.Logger.getLogger( "com.nerdvision" );
        logger.setUseParentHandlers( false );
        final ConsoleHandler handler = new ConsoleHandler();
        logger.addHandler( handler );

        final Level settingAs = settings.getSettingAs( "logging.level", Level.class );
        handler.setLevel( settingAs );
        logger.setLevel( settingAs );
        return LoggerFactory.getLogger( AgentImpl.class );
    }


    public static Object loadNerdVisionAPI()
    {
        if( clientReg == null )
        {
            return null;
        }
        return new INerdVisionHook()
        {
            @Override
            public INerdVision nerdVision()
            {
                return new INerdVision()
                {
                    @Override
                    public void updateBreakpoints()
                    {
                        try
                        {
                            final ClientReg.Response sessionWithBreakpoints = clientReg.getSessionWithBreakpoints();
                            breakpointService.processBreakpoints( sessionWithBreakpoints.getBreakpoints() );
                        }
                        catch( Exception e )
                        {
                            e.printStackTrace();
                        }
                    }


                    @Override
                    public String registerDecorator( final IDecorator decorator )
                    {
                        return clientConfig.registerDecorator( decorator );
                    }


                    @Override
                    public void deregisterDecorator( final String id )
                    {
                        clientConfig.deregisterDecorator( id );
                    }


                    @Override
                    public void close() throws Exception
                    {
                        shutdown = true;
                        thread.interrupt();
                    }


                    @Override
                    public void captureException( final Throwable t )
                    {
                        final INVError invError = NVError.fromThrowable( t, clientConfig.getTags() );
                        httpClient.postJsonAsync( String.format( "%s", settings.getErrorUrl() ),
                                JsonUtils.toJson( EventSnapshot.processErrorAsMap( invError ) ),
                                new HTTPClient.SinkBodyHandler() );
                    }


                    @Override
                    public IVersion version()
                    {
                        final Map productData = clientReg.getProductData();

                        //noinspection Convert2Lambda
                        return new IVersion()
                        {
                            @Override
                            public String version()
                            {
                                return String.valueOf( productData.get( "version" ) );
                            }
                        };
                    }
                };

            }


            @Override
            public ILogger logger()
            {
                return new ILogger()
                {
                    @Override
                    public void info( final String message, final Object... args )
                    {
                        logger.info( message, args );
                    }


                    @Override
                    public void debug( final String message, final Object... args )
                    {
                        logger.debug( message, args );
                    }


                    @Override
                    public void error( final String message, final Object... args )
                    {
                        logger.error( message, args );
                    }
                };
            }


            @Override
            public IReflection reflection()
            {
                return Utils.getReflection();
            }
        };
    }


    private static Attributes getManifest( final JarFile jarFile )
    {
        try
        {
            return jarFile.getManifest().getMainAttributes();
        }
        catch( Exception e )
        {
            return new Attributes( 0 );
        }
    }


    private static void announceClient( final Attributes manifest, final Logger logger )
    {
        announce( logger, "--------------------------------------------------------------------------------------" );
        announce( logger,
                "nerdvision (" + manifest.getValue( "Version" ) + ") - Copyright (C) Intergral GmbH. All Rights Reserved" );

        announceManifestValue( logger, manifest, "Version" );
        announceManifestValue( logger, manifest, "Git-Commit-Id" );
        announceManifestValue( logger, manifest, "Git-Commit-Time" );
        announceManifestValue( logger, manifest, "Git-Branch" );

        announce( logger,
                "OS              : " + System.getProperty( "os.name" ) + " [" + System.getProperty( "os.version" ) + "] "
                        + System.getProperty( "os.arch" ) );
        announce( logger,
                "Java            : " + System.getProperty( "java.version" ) + " [" + System.getProperty( "java.vm.version" ) +
                        "] " + System.getProperty( "java.vm.vendor" ) );
        announce( logger, "Start Time      : " + new Date() );

        announce( logger, "--------------------------------------------------------------------------------------" );
    }


    private static void announceManifestValue( final Logger logger, final Attributes manifest, final String key )
    {
        final String s = String.format( "%-16s: %s", key, manifest.getValue( key ) );
        announce( logger, s );
    }


    private static void announce( final Logger logger, final String message )
    {
        logger.debug( "{}: {}", PRODUCT_NAME, message );
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy