com.stackify.api.common.AppIdentityService.java Source code

Java tutorial

Introduction

Here is the source code for com.stackify.api.common.AppIdentityService.java

Source

/*
 * Copyright 2014 Stackify
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.stackify.api.common;

import java.io.IOException;
import java.util.Hashtable;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.stackify.api.AppIdentity;
import com.stackify.api.EnvironmentDetail;
import com.stackify.api.common.http.HttpClient;
import com.stackify.api.common.http.HttpException;
import com.stackify.api.common.util.Preconditions;

/**
 * AppIdentityService
 * @author Eric Martin
 */
public class AppIdentityService {

    /**
     * The internal logger
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(AppIdentityService.class);

    /**
     * Five minutes (in milliseconds)
     */
    private static long FIVE_MINUTES_MILLIS = 300000;

    /**
     * Map<ApplicationName, AppIdentityState> The cached app identity
     */
    private Map<String, AppIdentityState> applicationIdentityCache = new Hashtable<String, AppIdentityState>();

    /**
     * The API configuration
     */
    private final ApiConfiguration defaultApiConfig;

    /**
     * Jackson object mapper
     */
    private final ObjectMapper objectMapper;

    /**
     * True if deviceAppId is required in the response
     */
    private final boolean deviceAppIdRequired;

    /**
     * Constructor
     * @param apiConfig The API configuration
     * @param objectMapper Jackson object mapper
     */
    public AppIdentityService(final ApiConfiguration apiConfig, final ObjectMapper objectMapper) {
        Preconditions.checkNotNull(apiConfig);
        Preconditions.checkNotNull(objectMapper);

        this.defaultApiConfig = apiConfig;
        this.objectMapper = objectMapper;
        this.deviceAppIdRequired = false;
    }

    /**
     * Constructor
     * @param apiConfig The API configuration
     * @param objectMapper Jackson object mapper
     * @param deviceAppIdRequired True if deviceAppId is required in the response
     */
    public AppIdentityService(final ApiConfiguration apiConfig, final ObjectMapper objectMapper,
            final boolean deviceAppIdRequired) {
        Preconditions.checkNotNull(apiConfig);
        Preconditions.checkNotNull(objectMapper);

        this.defaultApiConfig = apiConfig;
        this.objectMapper = objectMapper;
        this.deviceAppIdRequired = deviceAppIdRequired;
    }

    /**
     * Retrieves the application identity given the environment details
     * @return The application identity
     */
    private AppIdentity getAppIdentity(ApiConfiguration apiConfig) {
        final String applicationName = apiConfig.getApplication();

        if (applicationName == null)
            return null;

        // If there's no record create it.
        if (!applicationIdentityCache.containsKey(applicationName)) {
            applicationIdentityCache.put(applicationName, new AppIdentityState());
        }

        final AppIdentityState state = applicationIdentityCache.get(applicationName);
        final long now = System.currentTimeMillis();

        if ((state.lastModified() + FIVE_MINUTES_MILLIS) < now) {
            state.touch();
            try {
                final AppIdentity identity = identifyApp(apiConfig);
                applicationIdentityCache.put(applicationName, state.updateAppIdentity(identity));

                LOGGER.debug("Application identity: {}", identity);

            } catch (Throwable t) {
                LOGGER.info("Unable to determine application identity", t);
            }
        }

        return applicationIdentityCache.get(apiConfig.getApplication()).getAppIdentity();
    }

    /**
     * Retrieves the application identity given the environment details
     * @param applicationName - name of the application
     * @return The application identity
     */
    public AppIdentity getAppIdentity(final String applicationName) {
        if (isCached(applicationName)) {
            return applicationIdentityCache.get(applicationName).getAppIdentity();

        } else {
            // Update environment detail with new configured application name
            final EnvironmentDetail updatedEnvDetail = updateEnvironmentDetail(defaultApiConfig.getEnvDetail(),
                    applicationName);

            // use existing apiConfig, with new application name
            final ApiConfiguration updatedApiConfig = defaultApiConfig.toBuilder().application(applicationName)
                    .envDetail(updatedEnvDetail).build();

            return getAppIdentity(updatedApiConfig);
        }
    }

    /**
     * getAppIdentity
     * @return The application identity
     */
    public AppIdentity getAppIdentity() {
        if (isCached(defaultApiConfig.getApplication()))
            return applicationIdentityCache.get(defaultApiConfig.getApplication()).getAppIdentity();
        else
            return getAppIdentity(defaultApiConfig);
    }

    /**
     * Retrieves the application identity given the environment details
     * @return The application identity
     * @throws IOException
     * @throws HttpException
     */
    private AppIdentity identifyApp(ApiConfiguration apiConfig) throws IOException, HttpException {
        // convert to json bytes
        byte[] jsonBytes = objectMapper.writer().writeValueAsBytes(apiConfig.getEnvDetail());

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("IdentifyApp Request: {}", new String(jsonBytes, "UTF-8"));
        }

        // post to stackify
        final HttpClient httpClient = new HttpClient(apiConfig);
        final String responseString = httpClient.post("/Metrics/IdentifyApp", jsonBytes);

        LOGGER.debug("IdentifyApp Response: {}", responseString);

        // deserialize the response and return the app identity
        ObjectReader jsonReader = objectMapper.reader(new TypeReference<AppIdentity>() {
        });
        AppIdentity identity = jsonReader.readValue(responseString);

        // make sure it has a valid DeviceAppID before accepting it

        if (deviceAppIdRequired) {
            if (identity.getDeviceAppId() == null) {
                throw new NullPointerException("DeviceAppId is null");
            }
        }

        // done

        return identity;
    }

    private boolean isCached(final String applicationName) {
        Preconditions.checkNotNull(applicationName);

        return applicationIdentityCache.containsKey(applicationName)
                && applicationIdentityCache.get(applicationName).getAppIdentity() != null;
    }

    private EnvironmentDetail updateEnvironmentDetail(final EnvironmentDetail envDetail,
            final String newConfAppName) {
        return EnvironmentDetail.newBuilder().deviceName(envDetail.getDeviceName()).appName(envDetail.getAppName())
                .appLocation(envDetail.getAppLocation()).configuredAppName(newConfAppName)
                .configuredEnvironmentName(envDetail.getConfiguredEnvironmentName()).build();
    }

    /**
     * This class contains appIdentity and it's modification date
     */
    private class AppIdentityState {

        private AppIdentity mayBeAppIdentity;
        private long lastQueryTimeStamp;

        public AppIdentityState() {
            this.lastQueryTimeStamp = 0;
            this.mayBeAppIdentity = null;
        }

        public final AppIdentityState updateAppIdentity(final AppIdentity appIdentity) {
            mayBeAppIdentity = appIdentity;
            return this;
        }

        public final AppIdentity getAppIdentity() {
            return mayBeAppIdentity;
        }

        public final long lastModified() {
            return lastQueryTimeStamp;
        }

        /**
         * Changes last modified date
         */
        public final void touch() {
            this.lastQueryTimeStamp = System.currentTimeMillis();
        }
    }
}