PageRenderTime 26ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 1ms

/cloudfoundry-runtime/src/main/java/org/cloudfoundry/runtime/env/CloudEnvironment.java

http://github.com/cloudfoundry/vcap-java
Java | 289 lines | 194 code | 35 blank | 60 comment | 33 complexity | 59f7d2bd4671eb8195e0c598fd1376ff MD5 | raw file
Possible License(s): Apache-2.0
  1. package org.cloudfoundry.runtime.env;
  2. import java.lang.reflect.Constructor;
  3. import java.util.ArrayList;
  4. import java.util.HashMap;
  5. import java.util.HashSet;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.Map.Entry;
  9. import java.util.Properties;
  10. import java.util.Set;
  11. import org.codehaus.jackson.map.ObjectMapper;
  12. /**
  13. * Simpler access to Cloud Foundry environment.
  14. * <p>
  15. * This class interprets environment variables and provide a simple
  16. * access without needing JSON parsing.
  17. * </p>
  18. *
  19. * @author Ramnivas Laddad
  20. * @author Scott Andrews
  21. * @author Thomas Risberg
  22. *
  23. */
  24. public class CloudEnvironment {
  25. private ObjectMapper objectMapper = new ObjectMapper();
  26. private EnvironmentAccessor environment = new EnvironmentAccessor();
  27. private static Map<Class<? extends AbstractServiceInfo>, Set<String>> serviceTypeToLabels = new HashMap<Class<? extends AbstractServiceInfo>, Set<String>>();
  28. private static void labelledServiceType(Class<? extends AbstractServiceInfo> serviceType,
  29. String label)
  30. {
  31. Set<String> labels = serviceTypeToLabels.get(serviceType);
  32. if (labels == null) {
  33. labels = new HashSet<String>();
  34. serviceTypeToLabels.put(serviceType, labels);
  35. }
  36. labels.add(label);
  37. }
  38. static {
  39. labelledServiceType(RdbmsServiceInfo.class, "mysql");
  40. labelledServiceType(RdbmsServiceInfo.class, "postgresql");
  41. labelledServiceType(RedisServiceInfo.class, "redis");
  42. labelledServiceType(MongoServiceInfo.class, "mongodb");
  43. labelledServiceType(RabbitServiceInfo.class, "rabbitmq");
  44. }
  45. /* package for testing purpose */
  46. void setCloudEnvironment(EnvironmentAccessor environment) {
  47. this.environment = environment;
  48. }
  49. public String getValue(String key) {
  50. return environment.getValue(key);
  51. }
  52. public boolean isCloudFoundry() {
  53. return getValue("VCAP_APPLICATION") != null;
  54. }
  55. @SuppressWarnings("unchecked")
  56. public ApplicationInstanceInfo getInstanceInfo() {
  57. String instanceInfoString = getValue("VCAP_APPLICATION");
  58. if (instanceInfoString == null || instanceInfoString.trim().isEmpty()) {
  59. return null;
  60. }
  61. try {
  62. Map<String,Object> infoMap = objectMapper.readValue(instanceInfoString, Map.class);
  63. return new ApplicationInstanceInfo(infoMap);
  64. } catch (Exception e) {
  65. throw new RuntimeException(e);
  66. }
  67. }
  68. public String getCloudApiUri() {
  69. ApplicationInstanceInfo instanceInfo = getInstanceInfo();
  70. if (instanceInfo == null) {
  71. throw new IllegalArgumentException("There is no cloud API urls in a non-cloud deployment");
  72. }
  73. List<String> uris = instanceInfo.getUris();
  74. String defaultUri = uris.get(0);
  75. return "api" + defaultUri.substring(defaultUri.indexOf("."));
  76. }
  77. /**
  78. * Return object representation of the VCAP_SERIVCES environment variable
  79. * <p>
  80. * Returns a map whose key is the label (for example "redis-2.2") of the
  81. * service and value is a list of services for that label. Each list element
  82. * is a map with service attributes.
  83. * </p>
  84. * @return
  85. */
  86. @SuppressWarnings("unchecked")
  87. private Map<String, List<Map<String,Object>>> getRawServices() {
  88. String servicesString = getValue("VCAP_SERVICES");
  89. if (servicesString == null || servicesString.length() == 0) {
  90. return new HashMap<String, List<Map<String,Object>>>();
  91. }
  92. try {
  93. return objectMapper.readValue(servicesString, Map.class);
  94. } catch (Exception e) {
  95. throw new RuntimeException(e);
  96. }
  97. }
  98. public List<Map<String,Object>> getServices() {
  99. Map<String, List<Map<String,Object>>> rawServices = getRawServices();
  100. List<Map<String,Object>> flatServices = new ArrayList<Map<String,Object>>();
  101. for (Map.Entry<String, List<Map<String,Object>>> entry : rawServices.entrySet()) {
  102. flatServices.addAll(entry.getValue());
  103. }
  104. return flatServices;
  105. }
  106. private Map<String, Object> getServiceDataByName(String name) {
  107. List<Map<String, Object>> services = getServices();
  108. for (Map<String, Object> service : services) {
  109. if (service.get("name").equals(name)) {
  110. return service;
  111. }
  112. }
  113. return null;
  114. }
  115. private List<Map<String, Object>> getServiceDataByLabels(Set<String> labels) {
  116. List<Map<String, Object>> services = getServices();
  117. List<Map<String, Object>> matchedServices = new ArrayList<Map<String,Object>>();
  118. for (Map<String, Object> service : services) {
  119. String serviceLabelWithoutVersion = labelWithoutVersion(service.get("label").toString());
  120. if (labels.contains(serviceLabelWithoutVersion)) {
  121. matchedServices.add(service);
  122. }
  123. }
  124. return matchedServices;
  125. }
  126. public <T extends AbstractServiceInfo> T getServiceInfo(String name, Class<T> serviceInfoType) {
  127. Map<String,Object> serviceInfoMap = getServiceDataByName(name);
  128. Set<String> labels = serviceTypeToLabels.get(serviceInfoType);
  129. if (labels != null && labels.contains(labelWithoutVersion(serviceInfoMap.get("label").toString()))) {
  130. return getServiceInfo(serviceInfoMap, serviceInfoType);
  131. } else {
  132. return null;
  133. }
  134. }
  135. public <T extends AbstractServiceInfo> List<T> getServiceInfos(Class<T> serviceInfoType) {
  136. Set<String> labels = serviceTypeToLabels.get(serviceInfoType);
  137. List<T> serviceInfos = new ArrayList<T>();
  138. if (labels != null) {
  139. List<Map<String,Object>> serviceInfoMaps = getServiceDataByLabels(labels);
  140. for (Map<String,Object> serviceInfoMap : serviceInfoMaps) {
  141. serviceInfos.add(getServiceInfo(serviceInfoMap, serviceInfoType));
  142. }
  143. }
  144. return serviceInfos;
  145. }
  146. private <T extends AbstractServiceInfo> T getServiceInfo(Map<String,Object> serviceInfoMap, Class<T> serviceInfoType) {
  147. try {
  148. Constructor<T> ctor = serviceInfoType.getConstructor(Map.class);
  149. return ctor.newInstance(serviceInfoMap);
  150. } catch (Exception e) {
  151. throw new CloudServiceException("Failed to create service information for " + serviceInfoMap.get("name"), e);
  152. }
  153. }
  154. /**
  155. * <p>General properties take the form:
  156. * <code><pre>
  157. * cloud.application.name = helloworld
  158. * cloud.provider.url = cloudfoundry.com
  159. * </pre></code>
  160. *
  161. * <p>Service specific properties are also exposed for each bound service:
  162. * <code><pre>
  163. * cloud.services.customerDb.type = mysql-5.1
  164. * cloud.services.customerDb.plan = free
  165. * cloud.services.customerDb.connection.hostname = ...
  166. * cloud.services.customerDb.connection.port = ...
  167. * etc...
  168. * </pre></code>
  169. *
  170. * <p>If a there is only a single service of a given type, that service is
  171. * aliased to the service type. For example, if there is only a single MySQL
  172. * service bound to the application, the service properties will also be
  173. * exposed under the '<code>mysql</code>' key:
  174. * <code><pre>
  175. * cloud.services.mysql.type = mysql-5.1
  176. * cloud.services.mysql.plan = free
  177. * cloud.services.mysql.connection.hostname = ...
  178. * cloud.services.mysql.connection.port = ...
  179. * etc...
  180. * </pre></code>
  181. * @return
  182. */
  183. public Properties getCloudProperties() {
  184. Properties properties = new Properties();
  185. properties.putAll(providerProperties());
  186. properties.putAll(applicationProperties());
  187. properties.putAll(serviceProperties());
  188. return properties;
  189. }
  190. private Properties providerProperties() {
  191. Properties properties = new Properties();
  192. properties.put("cloud.provider.url", getCloudApiUri().split("\\.", 2)[1]);
  193. return properties;
  194. }
  195. private Properties applicationProperties() {
  196. Properties properties = new Properties();
  197. properties.put("cloud.application.name", getInstanceInfo().getName());
  198. return properties;
  199. }
  200. private Properties serviceProperties() {
  201. Properties properties = new Properties();
  202. Map<String, Integer> serviceCounts = new HashMap<String, Integer>();
  203. List<Map<String, Object>> services = getServices();
  204. for (Map<String, Object> service : services) {
  205. String shortType = serviceShortType(service);
  206. // index services properties by name
  207. properties.putAll(servicePropertiesHelper("cloud.services." + service.get("name"), service));
  208. // count services by type (needed in next iteration)
  209. int count = serviceCounts.containsKey(shortType) ? serviceCounts.get(shortType) : 0;
  210. serviceCounts.put(shortType, count + 1);
  211. }
  212. for (Map<String, Object> service : services) {
  213. // alias service properties by type, if unique and available
  214. String shortType = serviceShortType(service);
  215. if (serviceCounts.get(shortType) == 1 && !properties.containsKey("cloud.services." + shortType + ".type")) {
  216. properties.putAll(servicePropertiesHelper("cloud.services." + shortType, service));
  217. }
  218. }
  219. return properties;
  220. }
  221. @SuppressWarnings("unchecked")
  222. private Properties servicePropertiesHelper(String propertyBase, Map<String, Object> service) {
  223. Properties source = new Properties();
  224. source.put(propertyBase + ".plan" , service.get("plan").toString());
  225. source.put(propertyBase + ".type" , service.get("label").toString());
  226. for (Entry<String, Object> connectionProperty : ((Map<String, Object>) service.get("credentials")).entrySet()) {
  227. source.put(propertyBase + ".connection." + connectionProperty.getKey(), connectionProperty.getValue().toString());
  228. }
  229. return source;
  230. }
  231. private String serviceShortType(Map<String, Object> service) {
  232. String type = (String) service.get("label");
  233. return type.split("-", 2)[0];
  234. }
  235. private static String labelWithoutVersion(String labelWithVersion) {
  236. int hyphenIndex = labelWithVersion.lastIndexOf('-');
  237. if (hyphenIndex == -1) {
  238. return labelWithVersion;
  239. } else {
  240. return labelWithVersion.substring(0, hyphenIndex);
  241. }
  242. }
  243. /**
  244. * Environment available to the deployed app.
  245. *
  246. * The main purpose of this class is to allow unit-testing of {@link CloudEnvironment}
  247. *
  248. */
  249. public static class EnvironmentAccessor {
  250. public String getValue(String key) {
  251. return System.getenv(key);
  252. }
  253. }
  254. }