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

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

There is a newer version: 3.0.2
Show newest version
package com.nerdvision.agent;

import com.nerdvision.agent.api.IBreakpoint;
import com.nerdvision.agent.snapshot.WatchValue;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TimeZone;
import java.util.jar.Attributes;
import java.util.regex.Pattern;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ClientReg
{
    private static final Logger LOGGER = LoggerFactory.getLogger( ClientReg.class );

    private final Pattern envKeyFilter;
    private final Pattern javaFilter;
    private final int maxStringLength;
    private final String apikey;
    private final String licenseUrl;
    private final Long backoffMax;
    private final double maxMultiplier;
    private final Settings settings;
    private final Attributes manifest;
    private final String jarPath;
    private final String uuid;
    private final HTTPClient httpClient;
    private final String nvStartMode;

    private String sessionId;


    public ClientReg( final Settings settings,
                      final Attributes manifest,
                      final String jarPath,
                      final String uuid,
                      final HTTPClient httpClient )
    {
        envKeyFilter = settings.getSettingAs( "env.regex", Pattern.class );
        javaFilter = settings.getSettingAs( "java.regex", Pattern.class );
        maxStringLength = settings.getSettingAs( "env.max.str.length", int.class );
        apikey = settings.getSettingAs( "api.key", String.class );
        licenseUrl = settings.getLicenseUrl();
        backoffMax = settings.getSettingAs( "client.registration.backoff.max", long.class );
        maxMultiplier = settings.getSettingAs( "client.registration.backoff.multiplier", double.class );
        nvStartMode = settings.getSettingAs( "nv.start_method", String.class );
        this.settings = settings;
        this.manifest = manifest;
        this.jarPath = jarPath;
        this.uuid = uuid;
        this.httpClient = httpClient;
    }


    public Response getSessionWithBreakpoints()
    {
        final Map stringObjectMap = loadSessionData();
        setSession( stringObjectMap );

        final List breakpoints = parseBreakpoints( (List) stringObjectMap.get( "breakpoints" ) );
        return new Response( this.sessionId, breakpoints );
    }


    private List parseBreakpoints( final List breakpoints )
    {
        final ArrayList list = new ArrayList<>();
        if( breakpoints == null )
        {
            return list;
        }
        for( Object breakpoint : breakpoints )
        {
            list.add( parseBreakpoint( (Map) breakpoint ) );
        }
        return list;
    }


    private IBreakpoint parseBreakpoint( final Map breakpoint )
    {
        return new GrpcBreakpoint(
                asString( breakpoint, "breakpoint_id" ),
                asString( breakpoint, "rel_path" ),
                asLong( breakpoint, "line_no" ),
                asInt( breakpoint, "fire_count" ),
                asString( breakpoint, "src_type" ),
                asString( breakpoint, "workspace_id" ),
                asString( breakpoint, "condition" ),
                asWatchers( breakpoint, "watchers" ),
                asString( breakpoint, "type" ),
                asString( breakpoint, "log_msg" ),
                asMap( breakpoint, "args" )
        );
    }


    private String asString( final Map map, final String key )
    {
        if( map.containsKey( key ) )
        {
            return String.valueOf( map.get( key ) );
        }
        return null;
    }


    private long asLong( final Map map, final String key )
    {
        if( map.containsKey( key ) )
        {
            return Settings.coerc( String.valueOf( map.get( key ) ), Long.class );
        }
        return -1;
    }


    private int asInt( final Map map, final String key )
    {
        if( map.containsKey( key ) )
        {
            return Settings.coerc( String.valueOf( map.get( key ) ), int.class );
        }
        return -1;
    }


    private Map asMap( final Map map, final String key )
    {
        if( map.containsKey( key ) )
        {
            return (Map) map.get( key );
        }
        return new HashMap<>();
    }


    private List asWatchers( final Map map, final String key )
    {
        final List watchValues = new ArrayList<>();
        if( map.containsKey( key ) )
        {
            final Map watchList = (Map) map.get( key );
            for( Map.Entry entry : watchList.entrySet() )
            {
                watchValues.add( new WatchValue( entry.getKey(), entry.getValue() ) );
            }
        }
        return watchValues;
    }


    private void setSession( final Map stringObjectMap )
    {
        final String session = String.valueOf( stringObjectMap.get( "session" ) );
        this.sessionId = session;
        this.httpClient.setSession( session );
    }


    public String getSession()
    {
        final Map stringObjectMap = loadSessionData();
        setSession( stringObjectMap );
        return this.sessionId;
    }


    private Map loadSessionData()
    {
        final long start = System.currentTimeMillis();
        Map response = null;
        int cnt = 0;
        while( response == null )
        {
            cnt++;
            final Map data = buildClientRegDocument( start );

            LOGGER.debug( "Sending client registration to ({}): {}", this.licenseUrl, data );
            final byte[] bytes = JsonUtils.toJson( data );
            final JSONObject res = httpClient.postJson( licenseUrl, bytes, new HTTPClient.JsonBodyHandler() );
            if( res != null )
            {
                response = res.toMap();
            }
            else
            {
                long delay = backoff( cnt, backoffMax, maxMultiplier );
                if( delay > 0 )
                {
                    synchronized( this )
                    {
                        try
                        {
                            wait( delay * 1000 );
                        }
                        catch( InterruptedException e )
                        {
                            LOGGER.debug( "Interrupted during wait." );
                        }
                    }
                }
            }
        }
        // should not be possible.
        return response;
    }


    public Map buildClientRegDocument( final long start )
    {
        final Map data = new HashMap<>();
        data.put( "uid", this.uuid );
        data.put( "api_key", apikey );
        data.put( "instance", new HashMap()
        {{
            put( "name", settings.getSettingAs( "name", String.class ) );
            put( "start_ts", String.valueOf( start ) );
            put( "start_mode", nvStartMode );
        }} );
        data.put( "network", getNetworkData() );
        data.put( "language", getLanguageData() );
        data.put( "env", getEnvData() );
        data.put( "os", getOSData() );
        data.put( "product", getProductData() );
        data.put( "tags", settings.getSettingAs( "tags", Map.class ) );
        return data;
    }


    public Map getProductData()
    {
        final Map data = new HashMap<>();
        data.put( "build", manifest.getValue( "Implementation-Build" ) );
        data.put( "version", manifest.getValue( "Version" ) );

        data.put( "major_version", manifest.getValue( "Version-Major" ) );
        data.put( "minor_version", manifest.getValue( "Version-Minor" ) );
        data.put( "micro_version", manifest.getValue( "Version-Micro" ) );

        data.put( "name", "nerdvision Java - Agent" );
        data.put( "path", jarPath );

        return data;
    }


    public Map getOSData()
    {
        final Map osMap = new HashMap<>();

        osMap.put( "name", System.getProperty( "os.name" ) );
        osMap.put( "version", System.getProperty( "os.version" ) );
        osMap.put( "arch", System.getProperty( "os.arch" ) );

        osMap.put( "lang", Locale.getDefault().getLanguage() );
        osMap.put( "locale", Locale.getDefault().getCountry() );
        osMap.put( "timezone", TimeZone.getDefault().getID() );

        osMap.put( "time", System.currentTimeMillis() );
        osMap.put( "start_ts", System.currentTimeMillis() );

        return osMap;
    }


    public Map getNetworkData()
    {
        final Map data = new HashMap<>();
        try
        {
            final InetAddress local = InetAddress.getLocalHost();
            data.put( "hostname", local.getHostName() );

            final String hostAddress = local.getHostAddress();
            data.put( "address", hostAddress );
        }
        catch( final UnknownHostException ex )
        {
            LOGGER.debug( "Exception getting host name." );
        }
        return data;
    }


    private Map getLanguageData()
    {
        final Map data = new HashMap<>();
        data.put( "type", "java" );
        data.put( "version", System.getProperty( "java.runtime.version" ) );
        data.put( "name", System.getProperty( "java.runtime.name" ) );

        final Map properties = new HashMap<>();
        data.put( "properties", properties );

        final Properties props = System.getProperties();
        final Set keys = props.stringPropertyNames();
        for( final String key : keys )
        {
            final String value = props.getProperty( key );

            if( key.startsWith( "java." ) || key.startsWith( "sun." ) )
            {
                final String k = trimString( key.replace( '.', '_' ) );
                properties.put( k, value );
            }
        }

        try
        {
            final List args = new ArrayList<>();
            final RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
            for( final String arg : runtimeMXBean.getInputArguments() )
            {
                if( javaFilter == null || !javaFilter.matcher( arg ).matches() )
                {
                    args.add( arg );
                }
            }

            data.put( "args", args );
        }
        catch( Throwable ignored )
        {
        }

        return data;
    }


    public static long backoff( final int attempt, final long maxDelayInSeconds, final double multiplier )
    {
        final double delayInSec = (Math.pow( 2.0, attempt ) - 1.0) * .5;
        return Math.round( Math.min( delayInSec * multiplier, maxDelayInSeconds ) );
    }


    public Map getEnvData()
    {
        final Map rtn = new HashMap<>();
        try
        {
            final Map env = System.getenv();
            for( final String key : env.keySet() )
            {
                boolean filter = false;
                if( envKeyFilter != null && envKeyFilter.matcher( key ).matches() )
                {
                    filter = true;
                }
                if( !filter )
                {
                    final String value = trimString( env.get( key ) );
                    rtn.put( key, value );
                }
            }
        }
        catch( SecurityException se )
        {
            LOGGER.debug( "Cannot process ENV", se );
        }
        return rtn;
    }


    private String trimString( final String s )
    {
        if( s.length() > this.maxStringLength )
        {
            return s.substring( 0, this.maxStringLength );
        }
        return s;
    }


    public static class Response
    {
        private final String session;
        private final List breakpoints;


        public Response( final String session, final List breakpoints )
        {
            this.session = session;
            this.breakpoints = breakpoints;
        }


        public String getSession()
        {
            return session;
        }


        public List getBreakpoints()
        {
            return breakpoints;
        }
    }
}