package org.cloudfoundry.runtime.env; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.codehaus.jackson.map.ObjectMapper; /** * Simpler access to Cloud Foundry environment. * <p> * This class interprets environment variables and provide a simple * access without needing JSON parsing. * </p> * * @author Ramnivas Laddad * */ public class CloudEnvironment { private ObjectMapper objectMapper = new ObjectMapper(); private EnvironmentAccessor environment = new EnvironmentAccessor(); private static Map<Class<? extends AbstractServiceInfo>, String> serviceTypeToNameMap = new HashMap<Class<? extends AbstractServiceInfo>, String>(); static { serviceTypeToNameMap.put(MysqlServiceInfo.class, "mysql-5.1"); serviceTypeToNameMap.put(RedisServiceInfo.class, "redis-2.2"); serviceTypeToNameMap.put(MongoServiceInfo.class, "mongodb-1.8"); serviceTypeToNameMap.put(RabbitServiceInfo.class, "rabbitmq-2.4"); } /* package for testing purpose */ void setCloudEnvironment(EnvironmentAccessor environment) { this.environment = environment; } public String getValue(String key) { return environment.getValue(key); } @SuppressWarnings("unchecked") public ApplicationInstanceInfo getInstanceInfo() { String instanceInfoString = getValue("VCAP_APPLICATION"); if (instanceInfoString == null || instanceInfoString.trim().isEmpty()) { return null; } try { Map<String,Object> infoMap = objectMapper.readValue(instanceInfoString, Map.class); return new ApplicationInstanceInfo(infoMap); } catch (Exception e) { throw new RuntimeException(e); } } @SuppressWarnings("unchecked") public AppDependencyInfo getAppDependencyInfo() { String dependencyInfoString = getValue("VCAP_CUSTOM_SERVICES"); if (dependencyInfoString == null || dependencyInfoString.trim().isEmpty()) { return null; } try { Map<String,Object> infoMap = objectMapper.readValue(dependencyInfoString, Map.class); return new AppDependencyInfo(infoMap); } catch (Exception e) { throw new RuntimeException(e); } } public String getCloudApiUri() { ApplicationInstanceInfo instanceInfo = getInstanceInfo(); if (instanceInfo == null) { throw new IllegalArgumentException("There is no cloud API urls in a non-cloud deployment"); } List<String> uris = instanceInfo.getUris(); String defaultUri = uris.get(0); return "api" + defaultUri.substring(defaultUri.indexOf(".")); } /** * Return object representation of the VCAP_SERIVCES environment variable * <p> * Returns a map whose key is the label (for example "redis-2.2") of the * service and value is a list of services for that label. Each list element * is a map with service attributes. * </p> * @return */ @SuppressWarnings("unchecked") private Map<String, List<Map<String,Object>>> getRawServices() { String servicesString = getValue("VCAP_SERVICES"); if (servicesString == null || servicesString.length() == 0) { return new HashMap<String, List<Map<String,Object>>>(); } try { return objectMapper.readValue(servicesString, Map.class); } catch (Exception e) { throw new RuntimeException(e); } } public List<Map<String,Object>> getServices() { Map<String, List<Map<String,Object>>> rawServices = getRawServices(); List<Map<String,Object>> flatServices = new ArrayList<Map<String,Object>>(); for (Map.Entry<String, List<Map<String,Object>>> entry : rawServices.entrySet()) { flatServices.addAll(entry.getValue()); } return flatServices; } private Map<String, Object> getServiceDataByName(String name) { List<Map<String, Object>> services = getServices(); for (Map<String, Object> service : services) { if (service.get("name").equals(name)) { return service; } } return null; } private List<Map<String, Object>> getServiceDataByServiceType(String type) { List<Map<String, Object>> services = getServices(); List<Map<String, Object>> matchedServices = new ArrayList<Map<String,Object>>(); for (Map<String, Object> service : services) { if (service.get("label").equals(type)) { matchedServices.add(service); } } return matchedServices; } public <T extends AbstractServiceInfo> T getServiceInfo(String name, Class<T> serviceInfoType) { Map<String,Object> serviceInfoMap = getServiceDataByName(name); String typeName = serviceTypeToNameMap.get(serviceInfoType); if (serviceInfoMap.get("label").equals(typeName)) { return getServiceInfo(serviceInfoMap, serviceInfoType); } return null; } @SuppressWarnings({ "unchecked", "rawtypes" }) public List<? extends AbstractServiceInfo> getServiceInfos() { List serviceInfos = new ArrayList(); for (Map.Entry<Class<? extends AbstractServiceInfo>, String> serviceTypeToNameEntry : serviceTypeToNameMap.entrySet()) { serviceInfos.addAll(getServiceInfos(serviceTypeToNameEntry.getKey())); } return serviceInfos; } public <T extends AbstractServiceInfo> List<T> getServiceInfos(Class<T> serviceInfoType) { String typeName = serviceTypeToNameMap.get(serviceInfoType); List<Map<String,Object>> serviceInfoMaps = getServiceDataByServiceType(typeName); List<T> serviceInfos = new ArrayList<T>(); for (Map<String,Object> serviceInfoMap : serviceInfoMaps) { serviceInfos.add(getServiceInfo(serviceInfoMap, serviceInfoType)); } return serviceInfos; } private <T extends AbstractServiceInfo> T getServiceInfo(Map<String,Object> serviceInfoMap, Class<T> serviceInfoType) { try { Constructor<T> ctor = serviceInfoType.getConstructor(Map.class); return ctor.newInstance(serviceInfoMap); } catch (Exception e) { throw new CloudServiceException("Failed to create service information for " + serviceInfoMap.get("name"), e); } } /** * Environment available to the deployed app. * * The main purpose of this class is to allow unit-testing of {@link CloudEnvironment} * */ public static class EnvironmentAccessor { public String getValue(String key) { return System.getenv(key); } } }