com.hotwire.sid.QuovaIP_GeoServerAdapter.java Source code

Java tutorial

Introduction

Here is the source code for com.hotwire.sid.QuovaIP_GeoServerAdapter.java

Source

package com.hotwire.sid;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
//import org.slf4j.*;

import org.apache.commons.lang.StringUtils;

import com.quova.common.IPInfoField;
import com.quova.common.QuovaReturnCode;
import com.quova.geodirectoryapi.GeoDirectoryAPI;
import com.quova.geodirectoryapi.IPInfo;
import com.quova.geodirectoryapi.QuovaResponse;
import com.quova.util.QuovaException;

/**
 * Adapter used to connect to the Quova GeoServer
 *
 * @author Mike McClay <mmcclay@hotwire.com />
 * @since 9.04
 */
public class QuovaIP_GeoServerAdapter {

    // ///////////////// ATTRIBUTES /////////////////////////

    /**
     * Logger
     */
    private static final Logger log = Logger.getLogger(QuovaIP_GeoServerAdapter.class.getName());

    /**
     * Holds the value of the confidence factor threshold that must be exceeded
     * to trust Quova IP Geo data.
     */
    private int cityConfidenceFactorThreshold = 0;

    /**
     * IPAddress to use when querying Quova, if it is specified in the property
     * hotwire.eis.geo.quova.ipAddressOverride If this value is not empty, then
     * the adapter will use this ip for all queries to quova ignoring the
     * ipAddress passed to the adapter's lookup methods
     * <p/>
     * THIS IS FOR NON-PROD ENVIRONMENTS ONLY - IT SHOULD NOT BE USED IN
     * PRODUCTION
     */
    private String ipAddressOverride;

    /**
     * Holds the connection to a Quova GeoServer
     */
    private GeoDirectoryAPI geoDirectoryConnection;

    // protected transient HwProperties hwProps;
    protected transient Properties hwProps;

    // //////////////////////////////////////////////////////////////
    // METHODS

    /**
     * Method takes an ipAddress as input and retrieves the following
     * geographical information: countryCode, zipCode (if city confidence
     * threshold exceeded), NDMA Code, and Gmt Offset
     *
     * @param ipAddress
     * @return an IP_GeoResponse object
     * @throws HwIP_GeoException 
     */
    public IP_GeoResponse lookup(String ipAddress) throws HwIP_GeoException {
        IPInfo ipInfoRecord = getIP_InfoRecord(ipAddress);
        return getIP_GeoResponseFromIPInfoRecord(ipInfoRecord);
    }

    /**
     * Queries Quova with ipAddress and returns the IPInfo record that Quova
     * returns, null if the call raises an exception
     *
     * @param ipAddress
     * @return IPInfo record returned by Quova, null if the IP was not found or
     *         an exception was thrown
     */
    private IPInfo getIP_InfoRecord(String ipAddress) throws HwIP_GeoException {
        IPInfo ipInfoRecord = null;

        // if the ipAddressOverride property is not null, then use it to query
        // for geo information
        if (!StringUtils.isEmpty(ipAddressOverride)) {
            ipAddress = ipAddressOverride;
        }

        // throw exception if ipAddress is empty (Quova throws does not allow an
        // empty IP as an arg)
        if (StringUtils.isEmpty(ipAddress)) {
            log.warning("IP Address passed to Quova GeoServer was empty");
            throw new HwIP_GeoException(HotwireErrors.IPGEO_GEO_SERVER_INVALID_ARGUMENT);
        }

        if (geoDirectoryConnection != null) {
            QuovaResponse quovaResponse = null;
            long startTimeMillis = System.currentTimeMillis();
            try {
                // Query Quova adapter for geo information
                quovaResponse = geoDirectoryConnection.getIPInfo(ipAddress);

                // log elapsed query time
                if (log.isLoggable(Level.FINE)) {
                    log.fine("Successful Quova query for ip " + ipAddress + " took "
                            + (System.currentTimeMillis() - startTimeMillis) + " milliseconds");
                }
            } catch (QuovaException qe) {
                long errorQueryTime = System.currentTimeMillis() - startTimeMillis;
                boolean isRecoverableError = false;
                QuovaReturnCode quovaExceptionReturnCode = qe.getErrorCode();
                if (QuovaReturnCode.INVALID_INPUT.equals(quovaExceptionReturnCode)
                        || QuovaReturnCode.TIMEOUT.equals(quovaExceptionReturnCode)) {
                    // recoverable, log but don't null connection
                    isRecoverableError = true;
                } else {
                    if (QuovaReturnCode.POOL_FAILURE.equals(quovaExceptionReturnCode)) {
                        // recoverable, but pool tuning is needed to handle
                        // load, syslog but don't null connection
                        // if this happens it should be only for a short time,
                        // so it won't spam syslogs
                        isRecoverableError = true;
                    } else {
                        // unrecoverable error such as invalid license, server
                        // down etc
                        // null the connection so that the app does not keep
                        // querying the broken geo server
                        // syslog so that the server can be brought back up
                        geoDirectoryConnection = null;
                    }
                    // HwLogger.syslog(SyslogFunctionalAreas.AREA_QUOVA,
                    // "Exception querying Quova - exception code: " +
                    // quovaExceptionReturnCode);
                }
                String errorTypeMessage = isRecoverableError ? "Recoverable error" : "Unrecoverable error";
                log.log(Level.SEVERE,
                        errorTypeMessage + " encountered querying Quova - exception code: "
                                + quovaExceptionReturnCode + ". Query took " + errorQueryTime + " milliseconds.",
                        qe);
                throw new HwIP_GeoException(HotwireErrors.IPGEO_GEO_SERVER_NOT_AVAILABLE, qe);
            } catch (Throwable t) {
                // THIS SHOULD NEVER HAPPEN, however we catch it here so it does
                // not crash the server
                // null the connection so that the app does not query the broken
                // geo server
                geoDirectoryConnection = null;
                // HwLogger.syslog(SyslogFunctionalAreas.AREA_QUOVA,
                // "Runtime error encountered querying Geo Server");
                log.severe("Runtime error encountered querying Geo Server: " + t);
                throw new HwIP_GeoException(HotwireErrors.IPGEO_GEO_SERVER_RUNTIME_ERROR, new Exception(t));
            }

            // Quova supports batch ipLookups, since we only sent 1 ipAddress,
            // we want the record at index 0
            ipInfoRecord = quovaResponse.getIPInfo(0);

            // sanity check the return code
            if (!ipInfoRecord.getRetCode().equals(QuovaReturnCode.NOT_FOUND)
                    && !ipInfoRecord.getRetCode().equals(QuovaReturnCode.SUCCESS)) {
                // we received an info record with an invalid return code, log
                // it
                log.severe("Quova returned a response with an error return code: " + ipInfoRecord.getRetCode());
                throw new HwIP_GeoException(HotwireErrors.IPGEO_GEO_SERVER_INVALID_RESPONSE);
            }
        } else {
            log.warning("Quova Geo Server connection is null, geo server query will be skipped");
            throw new HwIP_GeoException(HotwireErrors.IPGEO_GEO_SERVER_NOT_AVAILABLE);
        }

        return ipInfoRecord;
    }

    /**
     * Helper method that takes an ipInfoRecord as input and populates an
     * IP_GeoResponse with the record It currently sets the countryCode,
     * gmtOffset, ndmaCode and zipCode if cityConfidenceFactor is >=
     * cityConfidenceFactorThreshold
     *
     * @param ipInfoRecord
     * @return IP_GeoResponse
     */
    private IP_GeoResponse getIP_GeoResponseFromIPInfoRecord(IPInfo ipInfoRecord) {
        IP_GeoResponse ipGeoResponse = new IP_GeoResponse(ipInfoRecord.getString(IPInfoField.IP_ADDRESS), 'Q');
        // set country code
        ipGeoResponse.setCountryCode(ipInfoRecord.getString(IPInfoField.COUNTRY));

        // parse GMT
        try {
            Float gmtOffset = ipInfoRecord.getFloat(IPInfoField.TIMEZONE);
            if (gmtOffset != null) {
                ipGeoResponse.setGmtOffset(gmtOffset.floatValue());
            }
        } catch (QuovaException qe) {
            // this is expected since gmtOffset sometimes comes back as
            // 'multizone' so ignore this
            if (log.isLoggable(Level.FINE)) {
                log.fine("Could not parse IPInfoField.TIMEZONE for ip: " + ipGeoResponse.getIP_Address() + ". "
                        + "String value of field is: " + ipInfoRecord.getString(IPInfoField.TIMEZONE));
            }
        }

        // parse NDMA code
        try {
            Integer ndmaCode = ipInfoRecord.getInt(IPInfoField.DMA);
            if (ndmaCode != null) {
                ipGeoResponse.setNDMA_Code(ndmaCode.intValue());
            }
        } catch (QuovaException qe) {
            log.log(Level.SEVERE, "Could not parse IPInfoField.DMA for ip: " + ipGeoResponse.getIP_Address() + ". "
                    + "String value of field is: " + ipInfoRecord.getString(IPInfoField.DMA), qe);
        }

        // evaluate the city confidence factor and if it is >=
        // cityConfidenceFactorThreshold, set the zip code
        Integer cityConfidenceFactor = ipInfoRecord.getCF(IPInfoField.CITY);
        if (cityConfidenceFactor != null && cityConfidenceFactor.intValue() >= cityConfidenceFactorThreshold) {
            ipGeoResponse.setZipCode(ipInfoRecord.getString(IPInfoField.ZIP));
            ipGeoResponse.setCityName(ipInfoRecord.getString(IPInfoField.CITY));
            ipGeoResponse.setStateName(ipInfoRecord.getString(IPInfoField.STATE));
        }

        // parse other numeric fields
        try {
            Float longitude = ipInfoRecord.getFloat(IPInfoField.LONGITUDE);
            if (longitude != null) {
                ipGeoResponse.setLongitude(longitude.floatValue());
            }

            Float latitude = ipInfoRecord.getFloat(IPInfoField.LATITUDE);
            if (latitude != null) {
                ipGeoResponse.setLatitude(latitude.floatValue());
            }
        } catch (QuovaException qe) {
            log.log(Level.SEVERE, "Could not parse latitude / longitude for ip: " + ipGeoResponse.getIP_Address()
                    + ". " + "String value of latitude is: " + ipInfoRecord.getString(IPInfoField.LATITUDE) + ". "
                    + "String value of longitude is: " + ipInfoRecord.getString(IPInfoField.LONGITUDE) + ".", qe);
        }
        return ipGeoResponse;
    }

    /**
     * Called to refresh properties and Geo Directory Server connection
     */
    public void configurationChanged() {
        if (log.isLoggable(Level.FINE)) {
            log.fine("Initializing QuovaIP_GeoServerAdapter");
        }

        String geoDirectoryServerConnectionPoolSize = null;
        String geoDirectoryServerClientTimeout = null;
        String geoDirectoryServerPrimaryServerIp = null;
        String geoDirectoryServerPrimaryServerPort = null;
        String geoDirectoryServerSecondaryServerIp = null;
        String geoDirectoryServerSecondaryServerPort = null;

        try {
            String hegq = "hotwire.eis.geo.quova.";

            // read attribute properties

            // read general Quova settings
            geoDirectoryServerConnectionPoolSize = hwProps
                    .getProperty(hegq + "geoDirectoryServerConnectionPoolSize");
            geoDirectoryServerClientTimeout = hwProps.getProperty(hegq + "geoDirectoryServerClientTimeoutInMillis");

            // read primary Quova server settings
            geoDirectoryServerPrimaryServerIp = hwProps.getProperty(hegq + "geoDirectoryServerPrimaryServerIp");
            geoDirectoryServerPrimaryServerPort = hwProps.getProperty(hegq + "geoDirectoryServerPrimaryServerPort");

            // read secondary Quova server settings, if any
            geoDirectoryServerSecondaryServerIp = hwProps.getProperty(hegq + "geoDirectoryServerSecondaryServerIp");
            geoDirectoryServerSecondaryServerPort = hwProps
                    .getProperty(hegq + "geoDirectoryServerSecondaryServerPort");

        } catch (Exception e) {
            String errorMessage = "Encountered runtime exception reading properties: " + e;
            // HwLogger.syslog(SyslogFunctionalAreas.AREA_QUOVA, errorMessage);
            log.log(Level.SEVERE, errorMessage, e);
            return;
        }

        // check that we have all of the required Quova settings
        if (StringUtils.isEmpty(geoDirectoryServerConnectionPoolSize)
                || StringUtils.isEmpty(geoDirectoryServerClientTimeout)
                || StringUtils.isEmpty(geoDirectoryServerPrimaryServerIp)
                || StringUtils.isEmpty(geoDirectoryServerPrimaryServerPort)) {

            // we dont have all the settings and therefore can't create the
            // Quova client, log this
            String errorMessage = "One or more Quova settings are not available, connection will not be made:"
                    + "\ngeoDirectoryServerConnectionPoolSize: " + geoDirectoryServerConnectionPoolSize
                    + "\ngeoDirectoryServerClientTimeoutInMillis: " + geoDirectoryServerClientTimeout
                    + "\ngeoDirectoryServerPrimaryServerIp: " + geoDirectoryServerPrimaryServerIp
                    + "\ngeoDirectoryServerPrimaryServerPort: " + geoDirectoryServerPrimaryServerPort;

            // Logger.log(Level.SEVERE,errorMessage);
            log.severe(errorMessage);
            return;
        }

        // create the geoDirectoryServerProperties object with the read
        // properties
        Properties geoDirectoryServerProperties = new Properties();
        geoDirectoryServerProperties.setProperty("CONNECTION_POOL_SIZE", geoDirectoryServerConnectionPoolSize);
        geoDirectoryServerProperties.setProperty("USER_TIMEOUT", geoDirectoryServerClientTimeout);
        geoDirectoryServerProperties.setProperty("PRIMARY_SERVER_IP", geoDirectoryServerPrimaryServerIp);
        geoDirectoryServerProperties.setProperty("PRIMARY_SERVER_PORT", geoDirectoryServerPrimaryServerPort);

        // configure a secondary server if both the secondary server ip and port
        // have been specified in the props
        if (!StringUtils.isEmpty(geoDirectoryServerSecondaryServerIp)
                && !StringUtils.isEmpty(geoDirectoryServerSecondaryServerPort)) {
            geoDirectoryServerProperties.setProperty("SEC_SERVER_IP", geoDirectoryServerSecondaryServerIp);
            geoDirectoryServerProperties.put("SEC_SERVER_PORT", geoDirectoryServerSecondaryServerPort);
        }

        // configure the hard-coded properties
        geoDirectoryServerProperties.setProperty("NETWORK_PROTOCOL", "TCP");

        // MAX_RETRY - how many times the quova client retries querying a
        // server, before it considers it unavailable
        geoDirectoryServerProperties.setProperty("MAX_RETRY", "1");

        // MAX_WAIT_BLOCKING - time in millis that a request will block while
        // waiting for a connection to the server
        geoDirectoryServerProperties.setProperty("MAX_WAIT_BLOCKING", "1");

        GeoDirectoryAPI oldGeoDirectoryConnection = null;
        try {
            // get a reference to the existing connection (if any)
            // so that we can close the old connection when we create the new
            // one
            oldGeoDirectoryConnection = geoDirectoryConnection;

            // get a new connection to the GeoDirectoryAPI
            geoDirectoryConnection = new GeoDirectoryAPI(geoDirectoryServerProperties);
        } catch (QuovaException qe) {
            // log the error
            String errorMessage = "GeoDirectoryAPI creation failed, unable to get new connection" + qe;
            // HwLogger.syslog(SyslogFunctionalAreas.AREA_QUOVA, errorMessage);
            log.log(Level.SEVERE, errorMessage, qe);
        } finally {
            // close the old connection if there was one
            if (oldGeoDirectoryConnection != null) {
                try {
                    oldGeoDirectoryConnection.close();
                } catch (QuovaException qe) {
                    log.log(Level.SEVERE, "Exception closing old GeoDirectoryAPI connection after refresh " + qe,
                            qe);
                }
            }
        }
    }

    public void setHwProps(Properties hwProps) {
        this.hwProps = hwProps;
    }

    public static void main(String[] args) throws HwIP_GeoException, IOException {
        AppProperties app = new AppProperties();
        Properties prop = app.readProperties();

        QuovaIP_GeoServerAdapter q = new QuovaIP_GeoServerAdapter();
        q.setHwProps(prop);
        q.configurationChanged();
        IP_GeoResponse s = q.lookup("68.184.77.220");
        System.out.println(s.to_String());
        q.printDetails(s);

        List<String> ips = new ArrayList<>();
        ips.add("172.17.29.112");
        ips.add("192.172.150.56");
        for (String ip : ips) {
            IP_GeoResponse r = q.lookup(ip);
            System.out.println("---------------------------------------------------");
            System.out.println(ip);
            q.printDetails(r);
        }
    }

    public void printDetails(IP_GeoResponse response) {
        System.out.println("City : " + response.getCityName());
        System.out.println("State : " + response.getStateName());
        System.out.println("Country Code : " + response.getCountryCode());
        System.out.println("Zip code : " + response.getZipCode());
        System.out.println("Geo Info Source : " + response.getGeoInfoSource());
        System.out.println("GMT Offset : " + response.getGmtOffset());
        System.out.println("IP address : " + response.getIP_Address());
        System.out.println("Latitude : " + response.getLatitude());
        System.out.println("Longitude : " + response.getLongitude());
        System.out.println("NDMA Code : " + response.getNDMA_Code());

    }
}