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

com.stackify.api.common.AppIdentityService Maven / Gradle / Ivy

There is a newer version: 4.0.3
Show newest version
/*
 * 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 The cached app identity
	 */
	private Map applicationIdentityCache = new Hashtable();

	/**
	 * 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 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();
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy