cf.client.DefaultCloudController Maven / Gradle / Ivy
/*
* Copyright (c) 2013 Intellectual Reserve, Inc. All rights reserved.
*
* 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 cf.client;
import java.io.IOException;
import java.net.URI;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cf.client.model.Application;
import cf.client.model.ApplicationInstance;
import cf.client.model.Info;
import cf.client.model.Organization;
import cf.client.model.PrivateDomain;
import cf.client.model.Route;
import cf.client.model.SecurityGroup;
import cf.client.model.Service;
import cf.client.model.ServiceAuthToken;
import cf.client.model.ServiceBinding;
import cf.client.model.ServiceInstance;
import cf.client.model.ServicePlan;
import cf.client.model.Space;
import cf.client.model.User;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* @author Mike Heath
*/
public class DefaultCloudController implements CloudController {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultCloudController.class);
private static final String APP_INSTANCES = "/instances";
private static final String V2_APPS = "/v2/apps";
private static final String V2_PRIVATE_DOMAINS = "/v2/private_domains";
private static final String V2_ROUTES = "/v2/routes";
private static final String V2_SERVICES = "/v2/services";
private static final String V2_SERVICE_AUTH_TOKENS = "/v2/service_auth_tokens";
private static final String V2_SERVICE_BINDINGS = "/v2/service_bindings";
private static final String V2_SERVICE_INSTANCES = "/v2/service_instances";
private static final String V2_SERVICE_PLANS = "/v2/service_plans";
private static final String V2_SPACES = "/v2/spaces";
private static final String V2_ORGANIZATIONS = "/v2/organizations";
private final HttpClient httpClient;
private final URI target;
private final ObjectMapper mapper;
private final Object lock = new Object();
// Access to the following fields needs to be done holding the #lock monitor
private Info info;
private Uaa uaa;
public DefaultCloudController(HttpClient httpClient, URI target) {
this.httpClient = httpClient;
this.target = target;
mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
public DefaultCloudController(HttpClient httpClient, String uri) {
this(httpClient, URI.create(uri));
}
@Override
public URI getTarget() {
return target;
}
@Override
public Info getInfo() {
synchronized (lock) {
if (info == null) {
fetchInfo();
}
return info;
}
}
@Override
public Uaa getUaa() {
synchronized (lock) {
if (uaa == null) {
uaa = new DefaultUaa(httpClient, getInfo().getAuthorizationEndpoint());
}
return uaa;
}
}
@Override
public Map getApplicationInstances(Token token, UUID applicationGuid) {
final JsonNode jsonNode = fetchResource(token, V2_APPS + "/" + applicationGuid.toString() + APP_INSTANCES);
final Iterator> fields = jsonNode.fields();
final Map instances = new HashMap<>();
while (fields.hasNext()) {
final Map.Entry field = fields.next();
try {
instances.put(field.getKey(), mapper.readValue(field.getValue().traverse(), ApplicationInstance.class));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return instances;
}
@Override
public Application getApplication(Token token, UUID applicationGuid) {
JsonNode jsonNode = fetchResource(token, V2_APPS +"/"+ applicationGuid.toString());
try {
return mapper.readValue(jsonNode.get("entity").traverse(), Application.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public Application updateApplication(Token token, UUID applicationGuid, Application application) {
JsonNode jsonNode = putJsonToUri(token, application, V2_APPS, applicationGuid);
try {
return mapper.readValue(jsonNode.get("entity").traverse(), Application.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public UUID createService(Token token, Service service) {
try {
final String requestString = mapper.writeValueAsString(service);
final HttpPost post = new HttpPost(target.resolve(V2_SERVICES));
post.addHeader(token.toAuthorizationHeader());
post.setEntity(new StringEntity(requestString, ContentType.APPLICATION_JSON));
final HttpResponse response = httpClient.execute(post);
try {
validateResponse(response, 201);
final JsonNode json = mapper.readTree(response.getEntity().getContent());
return UUID.fromString(json.get("metadata").get("guid").asText());
} finally {
HttpClientUtils.closeQuietly(response);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public RestCollection getServices(Token token) {
return getServices(token ,null);
}
@Override
public RestCollection getServices(Token token, UUID servicePlanGuid) {
final ResultIterator iterator = new ResultIterator<>(
token,
V2_SERVICES,
Service.class,
servicePlanGuid == null ? null : ServiceQueryAttribute.SERVICE_PLAN_GUID,
servicePlanGuid == null ? null : servicePlanGuid.toString());
return new RestCollection<>(iterator.getSize(), iterator);
}
@Override
public Service getService(Token token, UUID serviceGuid) {
JsonNode jsonNode = fetchResource(token, V2_SERVICES +"/"+ serviceGuid.toString());
try {
return mapper.readValue(jsonNode.get("entity").traverse(), Service.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public ServicePlan getServicePlan(Token token, UUID servicePlanGuid) {
JsonNode jsonNode = fetchResource(token, V2_SERVICE_PLANS +"/"+ servicePlanGuid.toString());
try {
return mapper.readValue(jsonNode.get("entity").traverse(), ServicePlan.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public RestCollection getServicePlans(Token token) {
return getServicePlans(token, null, null);
}
@Override
public RestCollection getServicePlans(Token token, ServicePlanQueryAttribute queryAttribute, String queryValue) {
final ResultIterator iterator = new ResultIterator<>(
token,
V2_SERVICE_PLANS,
ServicePlan.class,
queryAttribute,
queryValue);
return new RestCollection<>(iterator.getSize(), iterator);
}
@Override
public ServiceInstance getServiceInstance(Token token, UUID instanceGuid) {
JsonNode jsonNode = fetchResource(token, V2_SERVICE_INSTANCES +"/"+ instanceGuid.toString());
try {
return mapper.readValue(jsonNode.get("entity").traverse(), ServiceInstance.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public Space getSpace(Token token, UUID spaceGuid) {
JsonNode jsonNode = fetchResource(token, V2_SPACES +"/"+ spaceGuid.toString());
try {
return mapper.readValue(jsonNode.get("entity").traverse(), Space.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public RestCollection getManagersInSpace(Token token, UUID spaceGuid) {
final ResultIterator iterator = new ResultIterator<>(
token,
V2_SPACES+"/"+spaceGuid.toString()+"/managers",
User.class,
null,
null);
return new RestCollection<>(iterator.getSize(), iterator);
}
@Override
public RestCollection getSecurityGroupsForSpace(Token token, UUID spaceGuid) {
//Hack until this issue is fixed: https://www.pivotaltracker.com/story/show/82055042
JsonNode node = fetchResource(token, V2_SPACES+"/"+spaceGuid.toString()+"?inline-relations-depth=1");
JsonNode entity = node.get("entity");
final ResultIterator iterator = new ResultIterator<>(SecurityGroup.class, (ArrayNode)entity.get("security_groups"));
return new RestCollection<>(iterator.getSize(), iterator);
}
@Override
public Organization getOrganization(Token token, UUID organizationGuid) {
JsonNode jsonNode = fetchResource(token, V2_ORGANIZATIONS +"/"+ organizationGuid.toString());
try {
return mapper.readValue(jsonNode.get("entity").traverse(), Organization.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public RestCollection getServiceInstances(Token token) {
return getServiceInstances(token, null, null);
}
@Override
public RestCollection getServiceInstances(Token token, ServiceInstanceQueryAttribute queryAttribute, String queryValue) {
final ResultIterator iterator = new ResultIterator<>(
token,
V2_SERVICE_INSTANCES,
ServiceInstance.class,
queryAttribute,
queryValue);
return new RestCollection<>(iterator.getSize(), iterator);
}
@Override
public RestCollection getServiceBindings(Token token) {
return getServiceBindings(token, null, null);
}
@Override
public RestCollection getServiceBindings(Token token, ServiceBindingQueryAttribute queryAttribute, String queryValue) {
final ResultIterator iterator = new ResultIterator<>(
token,
V2_SERVICE_BINDINGS,
ServiceBinding.class,
queryAttribute,
queryValue);
return new RestCollection<>(iterator.getSize(), iterator);
}
@Override
public ServiceBinding getServiceBinding(Token token, UUID serviceBindingGuid) {
JsonNode jsonNode = fetchResource(token, V2_SERVICE_BINDINGS +"/"+ serviceBindingGuid.toString());
try {
return mapper.readValue(jsonNode.get("entity").traverse(), ServiceBinding.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void validateResponse(HttpResponse response, int... expectedStatusCodes) {
final StatusLine statusLine = response.getStatusLine();
final int statusCode = statusLine.getStatusCode();
for (int code : expectedStatusCodes) {
if (code == statusCode) {
return;
}
}
throw new UnexpectedResponseException(response);
}
@Override
public void deleteService(Token token, UUID serviceGuid) {
deleteUri(token, V2_SERVICES + "/" + serviceGuid);
}
@Override
public Service updateService(Token token, UUID serviceGuid, Service service) {
JsonNode jsonNode = putJsonToUri(token, service, V2_SERVICES, serviceGuid);
try {
return mapper.readValue(jsonNode.get("entity").traverse(), Service.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public UUID createServicePlan(Token token, ServicePlan request) {
return postJsonToUri(token, request, V2_SERVICE_PLANS);
}
@Override
public ServicePlan updateServicePlan(Token token, UUID servicePlanGuid, ServicePlan service) {
JsonNode jsonNode = putJsonToUri(token, service, V2_SERVICE_PLANS, servicePlanGuid);
try {
return mapper.readValue(jsonNode.get("entity").traverse(), ServicePlan.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public RestCollection getAuthTokens(Token token) {
final ResultIterator iterator = new ResultIterator<>(
token,
V2_SERVICE_AUTH_TOKENS,
ServiceAuthToken.class,
null,
null);
return new RestCollection<>(iterator.getSize(), iterator);
}
@Override
public UUID createAuthToken(Token token, ServiceAuthToken request) {
return postJsonToUri(token, request, V2_SERVICE_AUTH_TOKENS);
}
@Override
public void deleteServiceAuthToken(Token token, UUID authTokenGuid) {
deleteUri(token, V2_SERVICE_AUTH_TOKENS + "/" + authTokenGuid);
}
@Override
public UUID createServiceInstance(Token token, String name, UUID planGuid, UUID spaceGuid) {
try {
final ObjectNode json = mapper.createObjectNode();
json.put("name", name);
json.put("service_plan_guid", planGuid.toString());
json.put("space_guid", spaceGuid.toString());
final HttpPost post = new HttpPost(target.resolve(V2_SERVICE_INSTANCES));
post.addHeader(token.toAuthorizationHeader());
post.setEntity(new StringEntity(json.toString(), ContentType.APPLICATION_JSON));
final HttpResponse response = httpClient.execute(post);
try {
validateResponse(response, 201);
final JsonNode jsonResponse = mapper.readTree(response.getEntity().getContent());
return UUID.fromString(jsonResponse.get("metadata").get("guid").asText());
} finally {
HttpClientUtils.closeQuietly(response);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void deleteServiceInstance(Token token, UUID instanceGuid) {
deleteUri(token, V2_SERVICE_INSTANCES + "/" + instanceGuid);
}
@Override
public RestCollection getPrivateDomains(Token token) {
final ResultIterator iterator = new ResultIterator<>(
token,
V2_PRIVATE_DOMAINS,
PrivateDomain.class,
null,
null);
return new RestCollection<>(iterator.getSize(), iterator);
}
@Override
public RestCollection getRoutes(Token token) {
final ResultIterator iterator = new ResultIterator<>(
token,
V2_ROUTES,
Route.class,
null,
null);
return new RestCollection<>(iterator.getSize(), iterator);
}
@Override
public RestCollection getAppsForRoute(Token token, UUID routeGuid) {
final ResultIterator iterator = new ResultIterator<>(
token,
V2_ROUTES+"/"+routeGuid+"/apps",
Application.class,
null,
null);
return new RestCollection<>(iterator.getSize(), iterator);
}
@Override
public RestCollection getRoutes(Token token, RouteQueryAttribute queryAttribute, String queryValue) {
final ResultIterator iterator = new ResultIterator<>(
token,
V2_ROUTES,
Route.class,
queryAttribute,
queryValue);
return new RestCollection<>(iterator.getSize(), iterator);
}
private UUID postJsonToUri(Token token, Object json, String uri) {
try {
final String requestString = mapper.writeValueAsString(json);
final HttpPost post = new HttpPost(target.resolve(uri));
post.addHeader(token.toAuthorizationHeader());
post.setEntity(new StringEntity(requestString, ContentType.APPLICATION_JSON));
final HttpResponse response = httpClient.execute(post);
try {
validateResponse(response, 201);
final JsonNode responseJson = mapper.readTree(response.getEntity().getContent());
return UUID.fromString(responseJson.get("metadata").get("guid").asText());
} finally {
HttpClientUtils.closeQuietly(response);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private JsonNode putJsonToUri(Token token, Object json, String uri, UUID guid) {
try {
final String requestString = mapper.writeValueAsString(json);
final HttpPut put = new HttpPut(target.resolve(uri+"/"+ guid.toString()));
put.addHeader(token.toAuthorizationHeader());
put.setEntity(new StringEntity(requestString, ContentType.APPLICATION_JSON));
final HttpResponse response = httpClient.execute(put);
try {
validateResponse(response, 201);
return mapper.readTree(response.getEntity().getContent());
} finally {
HttpClientUtils.closeQuietly(response);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void deleteUri(Token token, String uri) {
try {
final HttpDelete delete = new HttpDelete(target.resolve(uri));
delete.addHeader(token.toAuthorizationHeader());
final HttpResponse response = httpClient.execute(delete);
try {
validateResponse(response, 204);
} finally {
HttpClientUtils.closeQuietly(response);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void fetchInfo() {
try {
final HttpGet get = new HttpGet(target.resolve("/info"));
// TODO Standardize on error handling
// TODO Throw exception if non version 2 Cloud Controller
final HttpResponse response = httpClient.execute(get);
try {
synchronized (lock) {
info = mapper.readValue(response.getEntity().getContent(), Info.class);
}
} finally {
HttpClientUtils.closeQuietly(response);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private JsonNode fetchResource(Token token, String uri) {
LOGGER.debug("GET {}", uri);
try {
final HttpGet httpGet = new HttpGet(target.resolve(uri));
httpGet.setHeader(token.toAuthorizationHeader());
final HttpResponse response = httpClient.execute(httpGet);
try {
validateResponse(response, 200);
return mapper.readTree(response.getEntity().getContent());
} finally {
HttpClientUtils.closeQuietly(response);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private class ResultIterator implements Iterator> {
private ThreadLocal dateFormat = new ThreadLocal() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
}
};
private final Token token;
private final int size;
private final Class type;
private String nextUri;
private Iterator> iterator;
private ResultIterator(Token token, String uri, Class type, QueryAttribute queryAttribute, String queryValue) {
this.type = type;
this.token = token;
if (queryAttribute != null) {
uri += "?q=" + queryAttribute + ":" + queryValue;
}
final JsonNode jsonNode = fetchResource(token, uri);
size = jsonNode.get("total_results").asInt();
final JsonNode nextUrlNode = jsonNode.get("next_url");
nextUri = nextUrlNode.isNull() ? null : nextUrlNode.asText();
parseResources((ArrayNode)jsonNode.get("resources"));
}
private ResultIterator(Class type, ArrayNode jsonNode) {
this.type = type;
this.token = null;
this.size = jsonNode.size();
parseResources(jsonNode);
}
private void parseResources(ArrayNode resourcesJsonNode) {
final Iterator resourceNodeIterator = resourcesJsonNode.elements();
final ArrayList> resources = new ArrayList<>();
while (resourceNodeIterator.hasNext()) {
final JsonNode node = resourceNodeIterator.next();
final JsonNode metadata = node.get("metadata");
final String guid = metadata.get("guid").asText();
final URI uri = URI.create(metadata.get("url").asText());
Date created;
try {
created = dateFormat.get().parse(metadata.get("created_at").asText());
} catch (ParseException e) {
created = null;
}
Date updated;
final String updatedAt = metadata.get("updated_at").asText();
try {
updated = updatedAt == null ? null : dateFormat.get().parse(updatedAt);
} catch (ParseException e) {
updated = null;
}
final T entity;
try {
entity = mapper.readValue(node.get("entity").traverse(), type);
} catch (IOException e) {
throw new RuntimeException(e);
}
resources.add(new Resource<>(entity, guid, uri, created, updated));
}
iterator = resources.iterator();
}
public boolean fetchNextPage() {
if (nextUri == null) {
return false;
}
final JsonNode jsonNode = fetchResource(token, nextUri);
final JsonNode nextUrlNode = jsonNode.get("next_url");
nextUri = nextUrlNode.isNull() ? null : nextUrlNode.asText();
parseResources((ArrayNode)jsonNode.get("resources"));
return true;
}
@Override
public boolean hasNext() {
// Check if current iterator has an element, if not load the next page and check again.
return iterator.hasNext() || (fetchNextPage() && iterator.hasNext());
}
@Override
public Resource next() {
if (iterator.hasNext()) {
return iterator.next();
}
if (!fetchNextPage()) {
return null;
}
return iterator.next();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
private int getSize() {
return size;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy