com.stackify.api.common.AppIdentityService Maven / Gradle / Ivy
/*
* 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