com.gsma.mobileconnect.impl.DiscoveryImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.gsma.mobileconnect.impl.DiscoveryImpl.java

Source

/*
 *                                   SOFTWARE USE PERMISSION
 *
 *  By downloading and accessing this software and associated documentation files ("Software") you are granted the
 *  unrestricted right to deal in the Software, including, without limitation the right to use, copy, modify, publish,
 *  sublicense and grant such rights to third parties, subject to the following conditions:
 *
 *  The following copyright notice and this permission notice shall be included in all copies, modifications or
 *  substantial portions of this Software: Copyright  2016 GSM Association.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. YOU
 *  AGREE TO INDEMNIFY AND HOLD HARMLESS THE AUTHORS AND COPYRIGHT HOLDERS FROM AND AGAINST ANY SUCH LIABILITY.
 */

package com.gsma.mobileconnect.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.gsma.mobileconnect.cache.DiscoveryCacheKey;
import com.gsma.mobileconnect.cache.DiscoveryCacheValue;
import com.gsma.mobileconnect.cache.IDiscoveryCache;
import com.gsma.mobileconnect.discovery.*;
import com.gsma.mobileconnect.utils.*;
import org.apache.http.NameValuePair;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.client.utils.URLEncodedUtils;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Date;
import java.util.List;

/**
 * An implementation of {@link IDiscovery}.
 * <p>
 * An instance of this class is constructed using the {@link Factory} which allows an implementation of {@link IDiscoveryCache}
 * to be specified.
 */
class DiscoveryImpl implements IDiscovery {
    final private IDiscoveryCache discoveryCache;

    final private RestClient restClient;

    DiscoveryImpl(IDiscoveryCache discoveryCache, RestClient restClient) {
        this.discoveryCache = discoveryCache;
        this.restClient = restClient;
    }

    /**
     *  See {@link IDiscovery#startAutomatedOperatorDiscovery(String, String, String, String, DiscoveryOptions, List, IDiscoveryResponseCallback)}
     */
    public void startAutomatedOperatorDiscovery(IPreferences preferences, String redirectURL,
            DiscoveryOptions specifiedOptions, List<KeyValuePair> currentCookies,
            IDiscoveryResponseCallback callback) throws DiscoveryException {
        if (null == preferences) {
            ValidationUtils.validateParameter(null, "preferences");
            return;
        }
        startAutomatedOperatorDiscovery(preferences.getClientId(), preferences.getClientSecret(),
                preferences.getDiscoveryURL(), redirectURL, specifiedOptions, currentCookies, callback);
    }

    /**
     * See {@link IDiscovery#startAutomatedOperatorDiscovery(IPreferences, String, DiscoveryOptions, List, IDiscoveryResponseCallback)}.
     */
    public void startAutomatedOperatorDiscovery(String clientId, String clientSecret, String discoveryURL,
            String redirectURL, DiscoveryOptions specifiedOptions, List<KeyValuePair> currentCookies,
            IDiscoveryResponseCallback callback) throws DiscoveryException {
        validateDiscoveryParameters(clientId, clientSecret, discoveryURL, redirectURL, callback);
        DiscoveryOptions optionsToBeUsed = getOptionsToBeUsed(specifiedOptions);

        DiscoveryCacheKey cacheKey = DiscoveryCacheKey.newWithDetails(optionsToBeUsed.getIdentifiedMCC(),
                optionsToBeUsed.getIdentifiedMNC());
        DiscoveryResponse cachedValue = getCachedValue(cacheKey);
        if (null != cachedValue) {
            callback.completed(cachedValue);
            return;
        }

        RestResponse restResponse = null;
        try {
            URI discoveryURI = new URI(discoveryURL);
            HttpGet httpGet = buildHttpGetForOperatorDiscovery(discoveryURI, redirectURL, optionsToBeUsed);

            HttpClientContext context = restClient.getHttpClientContext(clientId, clientSecret, discoveryURI);

            int timeout = optionsToBeUsed.getTimeout();

            List<KeyValuePair> cookiesToProxy = HttpUtils.getCookiesToProxy(optionsToBeUsed.isCookiesEnabled(),
                    currentCookies);

            restResponse = restClient.callRestEndPoint(httpGet, context, timeout, cookiesToProxy);

            DiscoveryResponse discoveryResponse = buildDiscoveryResponse(restResponse.getStatusCode(),
                    restResponse.getHeaders(), JsonUtils.parseJson(restResponse.getResponse()));

            addCachedValue(cacheKey, discoveryResponse);

            callback.completed(discoveryResponse);
        } catch (RestException ex) {
            throw newDiscoveryExceptionFromRestException("Call to Discovery End Point failed", ex);
        } catch (URISyntaxException ex) {
            throw newDiscoveryExceptionWithRestResponse("Failed to build discovery URI", null, ex);
        } catch (JsonProcessingException ex) {
            throw newDiscoveryExceptionWithRestResponse("Failed creating Json object", restResponse, ex);
        } catch (IOException ex) {
            throw newDiscoveryExceptionWithRestResponse("Calling Discovery service failed", restResponse, ex);
        }
    }

    /**
     * See {@link IDiscovery#getOperatorSelectionURL(IPreferences, String, TimeoutOptions, IDiscoveryResponseCallback)}
     */
    public void getOperatorSelectionURL(IPreferences preferences, String redirectURL,
            TimeoutOptions specifiedOptions, IDiscoveryResponseCallback callback) throws DiscoveryException {
        if (null == preferences) {
            ValidationUtils.validateParameter(null, "preferences");
            return;
        }
        getOperatorSelectionURL(preferences.getClientId(), preferences.getClientSecret(),
                preferences.getDiscoveryURL(), redirectURL, specifiedOptions, callback);
    }

    /**
     * See {@link IDiscovery#getOperatorSelectionURL(String, String, String, String, TimeoutOptions, IDiscoveryResponseCallback)}
     */
    public void getOperatorSelectionURL(String clientId, String clientSecret, String discoveryURL,
            String redirectURL, TimeoutOptions specifiedOptions, IDiscoveryResponseCallback callback)
            throws DiscoveryException {
        validateDiscoveryParameters(clientId, clientSecret, discoveryURL, redirectURL, callback);
        DiscoveryOptions optionsToBeUsed = getOptionsToBeUsed(specifiedOptions);

        RestResponse restResponse = null;
        try {
            URI discoveryURI = new URI(discoveryURL);
            HttpGet httpGet = buildHttpGetForOperatorDiscovery(discoveryURI, redirectURL, optionsToBeUsed);

            HttpClientContext context = restClient.getHttpClientContext(clientId, clientSecret, discoveryURI);

            int timeout = optionsToBeUsed.getTimeout();

            restResponse = restClient.callRestEndPoint(httpGet, context, timeout, null);

            callback.completed(buildDiscoveryResponse(restResponse.getStatusCode(), restResponse.getHeaders(),
                    JsonUtils.parseJson(restResponse.getResponse())));
        } catch (RestException ex) {
            throw newDiscoveryExceptionFromRestException("Call to Discovery end point failed", ex);
        } catch (URISyntaxException ex) {
            throw newDiscoveryExceptionWithRestResponse("Failed to build discovery URI", null, ex);
        } catch (JsonProcessingException ex) {
            throw newDiscoveryExceptionWithRestResponse("Failed creating Json object", restResponse, ex);
        } catch (IOException ex) {
            throw newDiscoveryExceptionWithRestResponse("Calling Discovery service failed", restResponse, ex);
        }
    }

    /**
     * See {@link IDiscovery#parseDiscoveryRedirect(String, IParsedDiscoveryRedirectCallback)}
     */
    public void parseDiscoveryRedirect(String redirectURL, IParsedDiscoveryRedirectCallback callback)
            throws URISyntaxException {
        ValidationUtils.validateParameter(redirectURL, "redirectURL");
        ValidationUtils.validateParameter(callback, "callback");

        URI uri = new URI(redirectURL);
        String query = uri.getQuery();
        if (null == query) {
            callback.completed(new ParsedDiscoveryRedirect(null, null, null));
            return;
        }
        List<NameValuePair> parameters = URLEncodedUtils.parse(uri.getQuery(), null);
        String mcc_mnc = HttpUtils.getParameterValue(parameters, Constants.MCC_MNC_PARAMETER_NAME);
        String subscriber_id = HttpUtils.getParameterValue(parameters, Constants.SUBSCRIBER_ID_PARAMETER_NAME);

        String mcc = null;
        String mnc = null;
        if (null != mcc_mnc) {
            String[] parts = mcc_mnc.split("_");
            if (parts.length == 2) {
                mcc = parts[0];
                mnc = parts[1];
            }
        }
        callback.completed(new ParsedDiscoveryRedirect(mcc, mnc, subscriber_id));
    }

    /**
     * See {@link IDiscovery#completeSelectedOperatorDiscovery(String, String, String, String, String, String, CompleteSelectedOperatorDiscoveryOptions, List, IDiscoveryResponseCallback)}
     */
    public void completeSelectedOperatorDiscovery(IPreferences preferences, String redirectURL, String selectedMCC,
            String selectedMNC, CompleteSelectedOperatorDiscoveryOptions specifiedOptions,
            List<KeyValuePair> currentCookies, IDiscoveryResponseCallback callback) throws DiscoveryException {
        if (null == preferences) {
            ValidationUtils.validateParameter(null, "preferences");
            return;
        }
        completeSelectedOperatorDiscovery(preferences.getClientId(), preferences.getClientSecret(),
                preferences.getDiscoveryURL(), redirectURL, selectedMCC, selectedMNC, specifiedOptions,
                currentCookies, callback);
    }

    /**
     * See {@link IDiscovery#completeSelectedOperatorDiscovery(IPreferences, String, String, String, CompleteSelectedOperatorDiscoveryOptions, List, IDiscoveryResponseCallback)}
     */
    public void completeSelectedOperatorDiscovery(String clientId, String clientSecret, String discoveryURL,
            String redirectURL, String selectedMCC, String selectedMNC,
            CompleteSelectedOperatorDiscoveryOptions specifiedOptions, List<KeyValuePair> currentCookies,
            IDiscoveryResponseCallback callback) throws DiscoveryException {
        if (StringUtils.isNullOrEmpty(redirectURL)) {
            redirectURL = Constants.DEFAULT_REDIRECT_URL;
        }
        validateDiscoveryParameters(clientId, clientSecret, discoveryURL, selectedMCC, selectedMNC, callback);

        DiscoveryCacheKey cacheKey = DiscoveryCacheKey.newWithDetails(selectedMCC, selectedMNC);
        DiscoveryResponse cachedValue = getCachedValue(cacheKey);
        if (null != cachedValue) {
            callback.completed(cachedValue);
            return;
        }

        RestResponse restResponse = null;
        CompleteSelectedOperatorDiscoveryOptions optionsToBeUsed = getOptionsToBeUsed(specifiedOptions);
        try {
            URI discoveryURI = new URI(discoveryURL);
            HttpGet httpGet = buildHttpGetForCompleteSelectedOperatorDiscovery(discoveryURI, redirectURL,
                    selectedMCC, selectedMNC);

            HttpClientContext context = restClient.getHttpClientContext(clientId, clientSecret, discoveryURI);

            int timeout = optionsToBeUsed.getTimeout();

            List<KeyValuePair> cookiesToProxy = HttpUtils.getCookiesToProxy(optionsToBeUsed.isCookiesEnabled(),
                    currentCookies);

            restResponse = restClient.callRestEndPoint(httpGet, context, timeout, cookiesToProxy);

            DiscoveryResponse discoveryResponse = buildDiscoveryResponse(restResponse.getStatusCode(),
                    restResponse.getHeaders(), JsonUtils.parseJson(restResponse.getResponse()));

            addCachedValue(cacheKey, discoveryResponse);

            callback.completed(discoveryResponse);
        } catch (RestException ex) {
            throw newDiscoveryExceptionFromRestException("Call to Discovery end point failed", ex);
        } catch (URISyntaxException ex) {
            throw newDiscoveryExceptionWithRestResponse("Failed to build discovery URI", null, ex);
        } catch (JsonProcessingException ex) {
            throw newDiscoveryExceptionWithRestResponse("Failed creating Json object", restResponse, ex);
        } catch (IOException ex) {
            throw newDiscoveryExceptionWithRestResponse("Calling Discovery service failed", restResponse, ex);
        }
    }

    /**
     * See {@link IDiscovery#extractOperatorSelectionURL(DiscoveryResponse)}
     */
    public String extractOperatorSelectionURL(DiscoveryResponse discoveryResult) {
        if (!isValidDiscoveryResponse(discoveryResult)) {
            return null;
        }
        return JsonUtils.extractUrl(discoveryResult.getResponseData(), Constants.OPERATOR_SELECTION_REL);
    }

    /**
     * See {@link IDiscovery#isOperatorSelectionRequired(DiscoveryResponse)}.
     */
    public boolean isOperatorSelectionRequired(DiscoveryResponse discoveryResult) {
        validateDiscoveryResponse(discoveryResult);
        return !StringUtils.isNullOrEmpty(extractOperatorSelectionURL(discoveryResult));
    }

    /**
     * See {@link IDiscovery#isErrorResponse(DiscoveryResponse)}
     */
    public boolean isErrorResponse(DiscoveryResponse discoveryResult) {
        return getErrorResponse(discoveryResult) != null;
    }

    /**
     * See {@link IDiscovery#getErrorResponse(DiscoveryResponse)}
     */
    public ErrorResponse getErrorResponse(DiscoveryResponse discoveryResult) {
        if (null == discoveryResult || null == discoveryResult.getResponseData()) {
            return null;
        }
        return JsonUtils.getErrorResponse(discoveryResult.getResponseData());
    }

    /**
     * See {@link IDiscovery#getCachedDiscoveryResult(String, String)}
     */
    public DiscoveryResponse getCachedDiscoveryResult(String mcc, String mnc) {
        ValidationUtils.validateParameter(mcc, "mcc");
        ValidationUtils.validateParameter(mnc, "mnc");

        return getCachedValue(DiscoveryCacheKey.newWithDetails(mcc, mnc));
    }

    /**
     * See {@link IDiscovery#clearDiscoveryCache(CacheOptions)}
     */
    public void clearDiscoveryCache(CacheOptions options) {
        if (null == discoveryCache) {
            return;
        }

        if (null == options) {
            discoveryCache.clear();
            return;
        }

        DiscoveryCacheKey key = DiscoveryCacheKey.newWithDetails(options.getMCC(), options.getMNC());
        if (null == key) {
            return;
        }

        discoveryCache.remove(key);
    }

    private void validateDiscoveryParameters(String clientId, String clientSecret, String discoveryURL,
            String redirectURL, IDiscoveryResponseCallback callback) {
        ValidationUtils.validateParameter(clientId, "clientId");
        ValidationUtils.validateParameter(clientSecret, "clientSecret");
        ValidationUtils.validateParameter(discoveryURL, "discoveryURL");
        ValidationUtils.validateParameter(redirectURL, "redirectURL");
        ValidationUtils.validateParameter(callback, "callback");
    }

    private void validateDiscoveryParameters(String clientId, String clientSecret, String discoveryURL,
            String selectedMCC, String selectedMNC, IDiscoveryResponseCallback callback) {
        ValidationUtils.validateParameter(clientId, "clientId");
        ValidationUtils.validateParameter(clientSecret, "clientSecret");
        ValidationUtils.validateParameter(discoveryURL, "discoveryURL");
        ValidationUtils.validateParameter(selectedMCC, "selectedMCC");
        ValidationUtils.validateParameter(selectedMNC, "selectedMNC");
        ValidationUtils.validateParameter(callback, "callback");
    }

    private void validateDiscoveryResponse(DiscoveryResponse discoveryResult) {
        ValidationUtils.validateParameter(discoveryResult, "discoveryResult");
        if (!isValidDiscoveryResponse(discoveryResult)) {
            throw new IllegalArgumentException("Not a valid discoveryResult");
        }
    }

    /**
     * Does the Discovery Response hold valid data?
     *
     * @param discoveryResult The discovery response to examine.
     * @return True is there is data and it is from the cache or the response code indicates it is valid.
     */
    private boolean isValidDiscoveryResponse(DiscoveryResponse discoveryResult) {
        if (null == discoveryResult || null == discoveryResult.getResponseData()) {
            return false;
        }

        if (discoveryResult.isCached()) {
            return true;
        }

        int responseCode = discoveryResult.getResponseCode();
        return responseCode == Constants.OPERATOR_IDENTIFIED_RESPONSE
                || responseCode == Constants.OPERATOR_NOT_IDENTIFIED_RESPONSE;
    }

    /**
     * Return the options to be used for the startAutomatedOperatorDiscovery call.
     * <p>
     * Use caller provided values if passed, use defaults otherwise.
     *
     * @param originalOptions The caller specified options if any.
     * @return The options to be used.
     */
    private DiscoveryOptions getOptionsToBeUsed(DiscoveryOptions originalOptions) {
        if (null == originalOptions) {
            return new DiscoveryOptions();
        } else {
            return originalOptions;
        }
    }

    /**
     * Return the options to be used for the getOperatorSelectionURL call.
     * <p>
     * Use caller provided values if passed, use defaults otherwise.
     *
     * @param originalOptions The caller specified options if any.
     * @return The options to be used.
     */
    private DiscoveryOptions getOptionsToBeUsed(TimeoutOptions originalOptions) {
        DiscoveryOptions optionsToBeUsed = new DiscoveryOptions();
        optionsToBeUsed.setManuallySelect(true);
        optionsToBeUsed.setIdentifiedMCC(null);
        optionsToBeUsed.setIdentifiedMNC(null);
        optionsToBeUsed.setUsingMobileData(false);
        optionsToBeUsed.setCookiesEnabled(true);
        optionsToBeUsed.setLocalClientIP(null);

        TimeoutOptions tmpOptions = originalOptions;
        if (null == tmpOptions) {
            tmpOptions = new TimeoutOptions();
        }
        optionsToBeUsed.setTimeout(tmpOptions.getTimeout());

        return optionsToBeUsed;
    }

    /**
     * Return the options to be used for the CompleteSelectedOperatorDiscovery call.
     * <p>
     * Use caller provided values if passed, use defaults otherwise.
     *
     * @param originalOptions The caller specified options if any.
     * @return The options to be used.
     */
    private CompleteSelectedOperatorDiscoveryOptions getOptionsToBeUsed(
            CompleteSelectedOperatorDiscoveryOptions originalOptions) {
        CompleteSelectedOperatorDiscoveryOptions tmpOptions = originalOptions;
        if (null == tmpOptions) {
            tmpOptions = new CompleteSelectedOperatorDiscoveryOptions();
        }

        return tmpOptions;
    }

    /**
     * Return a value from the cache.
     *
     * @param key Identifies the value to return.
     * @return The cached value if available, null otherwise.
     */
    private DiscoveryResponse getCachedValue(DiscoveryCacheKey key) {
        if (null == discoveryCache) {
            return null;
        }
        if (null == key) {
            return null;
        }
        DiscoveryCacheValue value = discoveryCache.get(key);
        if (null == value) {
            return null;
        }
        return buildDiscoveryResponse(value);
    }

    /**
     * Add a value to the cache.
     * <p>
     * Only operator identified responses are cached.
     *
     * @param key The cache key.
     * @param value The value to cache.
     */
    void addCachedValue(DiscoveryCacheKey key, DiscoveryResponse value) {
        if (null == discoveryCache) {
            return;
        }
        if (null == key || null == value) {
            return;
        }
        if (value.getResponseCode() != Constants.OPERATOR_IDENTIFIED_RESPONSE) {
            return;
        }
        if (null == value.getTtl() || null == value.getResponseData()) {
            return;
        }
        discoveryCache.add(key, new DiscoveryCacheValue(value.getTtl(), value.getResponseData()));
    }

    /**
     * Build a HttpGet for the Discovery Service.
     *
     * @param discoveryURI URI of the discovery service.
     * @param redirectURL The redirect URL to pass as a parameter.
     * @param options Optional parameters to the get.
     * @return HttpGet to the Discovery Service to identify an operator.
     * @throws URISyntaxException
     */
    private HttpGet buildHttpGetForOperatorDiscovery(URI discoveryURI, String redirectURL, DiscoveryOptions options)
            throws URISyntaxException {
        URIBuilder uri = new URIBuilder(discoveryURI);
        HttpUtils.addParameter(uri, Constants.MANUALLY_SELECT_PARAMETER_NAME, options.isManuallySelect());
        HttpUtils.addParameter(uri, Constants.IDENTIFIED_MCC_PARAMETER_NAME, options.getIdentifiedMCC());
        HttpUtils.addParameter(uri, Constants.IDENTIFIED_MNC_PARAMETER_NAME, options.getIdentifiedMNC());
        HttpUtils.addParameter(uri, Constants.USING_MOBILE_DATA_PARAMETER_NAME, options.isUsingMobileData());
        HttpUtils.addParameter(uri, Constants.LOCAL_CLIENT_IP_PARAMETER_NAME, options.getLocalClientIP());
        HttpUtils.addParameter(uri, Constants.REDIRECT_URL_PARAMETER_NAME, redirectURL);

        HttpGet httpGet = new HttpGet(uri.build());
        httpGet.setHeader(Constants.ACCEPT_HEADER_NAME, Constants.ACCEPT_JSON_HEADER_VALUE);

        if (!StringUtils.isNullOrEmpty(options.getClientIP())) {
            httpGet.setHeader(Constants.X_SOURCE_IP_HEADER_NAME, options.getClientIP());
        }

        return httpGet;
    }

    /**
     * Build a HttpGet for the CompleteSelectedOperatorDiscovery method.
     *
     * @param discoveryURI The URI of the discovery service.
     * @param redirectURL The redirect URL to pass as a parameter.
     * @param selectedMCC The MCC of the selected operator.
     * @param selectedMNC The MNC of the selected operator.
     * @return HttpGet to the Discovery Service to identify an operator.
     * @throws URISyntaxException
     */
    private HttpGet buildHttpGetForCompleteSelectedOperatorDiscovery(URI discoveryURI, String redirectURL,
            String selectedMCC, String selectedMNC) throws URISyntaxException {
        URIBuilder uri = new URIBuilder(discoveryURI);
        HttpUtils.addParameter(uri, Constants.REDIRECT_URL_PARAMETER_NAME, redirectURL);
        HttpUtils.addParameter(uri, Constants.SELECTED_MCC_PARAMETER_NAME, selectedMCC);
        HttpUtils.addParameter(uri, Constants.SELECTED_MNC_PARAMETER_NAME, selectedMNC);

        HttpGet httpGet = new HttpGet(uri.build());
        httpGet.setHeader(Constants.ACCEPT_HEADER_NAME, Constants.ACCEPT_JSON_HEADER_VALUE);
        return httpGet;
    }

    private DiscoveryException newDiscoveryExceptionFromRestException(String message, RestException restException) {
        return new DiscoveryException(message, restException.getUri(), restException.getStatusCode(),
                restException.getHeaders(), restException.getContents(), restException);
    }

    private DiscoveryException newDiscoveryExceptionWithRestResponse(String message, RestResponse restResponse,
            Throwable ex) {
        if (null == restResponse) {
            return new DiscoveryException(message, ex);
        } else {
            return new DiscoveryException(message, restResponse.getUri(), restResponse.getStatusCode(),
                    restResponse.getHeaders(), restResponse.getResponse(), ex);
        }
    }

    private DiscoveryResponse buildDiscoveryResponse(int responseCode, List<KeyValuePair> headers,
            JsonNode jsonDoc) {
        Date ttl = determineTtl(JsonUtils.getDiscoveryResponseTtl(jsonDoc));
        return new DiscoveryResponse(false, ttl, responseCode, headers, jsonDoc);
    }

    private DiscoveryResponse buildDiscoveryResponse(DiscoveryCacheValue value) {
        return new DiscoveryResponse(true, value.getTtl(), 0, null, value.getValue());
    }

    /**
     * Determine the time-to-live for a discovery response.
     * <p>
     * Ensure the ttl is between a minimum and maximum value.
     *
     * @param ttlTime The ttl value from the discovery result.
     * @return The ttl to use.
     */
    Date determineTtl(Long ttlTime) {
        Long now = new Date().getTime();
        Date min = new Date(now + Constants.MINIMUM_TTL_MS);
        Date max = new Date(now + Constants.MAXIMUM_TTL_MS);

        if (null == ttlTime) {
            return min;
        }

        Date currentTtl = new Date(ttlTime);

        if (currentTtl.before(min)) {
            return min;
        }

        if (currentTtl.after(max)) {
            return max;
        }

        return currentTtl;
    }
}