/* * 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.service; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Implements a Cloud Foundry service broker. This broker does not implement the actual REST endpoint. This must be * done using Spring MVC or some other HTTP server technology. This class is intended to act as an intermediary between * a set of HTTP endpoints and a user provide implementation of {@link cf.service.Provisioner}. * * @author Mike Heath * @deprecated Use the V2 services. */ @Deprecated public class ServiceBroker { private static final Logger LOGGER = LoggerFactory.getLogger(ServiceBroker.class); /** * The name of the HTTP header holding the service auth token. */ public static final String VCAP_SERVICE_TOKEN_HEADER = "X-VCAP-Service-Token"; public static final String SERVICE_INSTANCE_ID = "service_id"; public static final String SERVICE_BINDING_ID = "binding_id"; public static final Pattern BINDING_PATTERN = Pattern.compile("/+gateway/v1/configurations/(.*?)/handles(/(.*))?"); public static final Pattern INSTANCE_PATTERN = Pattern.compile("/+gateway/v1/configurations(/(.*))?"); final private ObjectMapper mapper = new ObjectMapper(); private final String authToken; private final Provisioner provisioner; public ServiceBroker(Provisioner provisioner, String authToken) { if (authToken == null) { throw new IllegalArgumentException("authToken can not be null"); } this.provisioner = provisioner; this.authToken = authToken; mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); } public String createService(String authToken, byte[] body) throws ServiceBrokerException { LOGGER.debug("Creating service"); validateAuthToken(authToken); final CreateRequest createRequest = decode(CreateRequest.class, body); final ServiceInstance serviceInstance = provisioner.create(createRequest); final ObjectNode gatewayData = mapper.createObjectNode(); putAll(gatewayData, serviceInstance.getGatewayData()); gatewayData.put(SERVICE_INSTANCE_ID, serviceInstance.getInstanceId()); final ObjectNode credentials = mapper.createObjectNode(); putAll(credentials, serviceInstance.getCredentials()); final CreateResponse response = new CreateResponse(serviceInstance.getInstanceId(), gatewayData, credentials); return encode(response); } public String deleteService(String authToken, String uri) throws ServiceBrokerException { LOGGER.debug("Deleting service"); validateAuthToken(authToken); final Matcher matcher = INSTANCE_PATTERN.matcher(uri); if (!matcher.matches() && matcher.groupCount() != 2) { throw new ResourceNotFoundException(); } final String serviceInstanceId = matcher.group(2); provisioner.delete(serviceInstanceId); return "{}"; } public String bindService(String authToken, byte[] body) throws ServiceBrokerException { LOGGER.debug("Binding service"); validateAuthToken(authToken); final BindRequest bindRequest = decode(BindRequest.class, body); final ServiceBinding serviceBinding = provisioner.bind(bindRequest); final Map<String,Object> gatewayData = new HashMap<>(serviceBinding.getGatewayData()); gatewayData.put(SERVICE_INSTANCE_ID, serviceBinding.getInstanceId()); gatewayData.put(SERVICE_BINDING_ID, serviceBinding.getBindingId()); final BindResponse bindResponse = new BindResponse(serviceBinding.getBindingId(), gatewayData, serviceBinding.getCredentials()); return encode(bindResponse); } public String unbindService(String authToken, String uri) throws ServiceBrokerException { LOGGER.debug("Unbinding service"); validateAuthToken(authToken); final Matcher matcher = BINDING_PATTERN.matcher(uri); if (!matcher.matches() && matcher.groupCount() != 3) { throw new ResourceNotFoundException(); } final String instanceId = matcher.group(1); final String bindingId = matcher.group(2); provisioner.unbind(instanceId, bindingId); return "{}"; } private void validateAuthToken(String authToken) throws AuthenticationException { if (!this.authToken.equals(authToken)) { LOGGER.warn("Received invalid service-auth-token from Cloud Controller."); throw new AuthenticationException(); } } private <T> T decode(Class<T> type, byte[] body) throws BadRequestException { try { return mapper.readValue(body, type); } catch (IOException e) { throw new BadRequestException(); } } private String encode(Object object) { try { return mapper.writeValueAsString(object); } catch (JsonProcessingException e) { throw new RuntimeException(e); } } private void putAll(ObjectNode object, Map<String,Object> map) { for (Map.Entry<String, Object> entry : map.entrySet()) { final JsonNode value; if (entry.getValue() instanceof JsonNode) { value = (JsonNode) entry.getValue(); } else { value = mapper.valueToTree(entry.getValue()); } object.put(entry.getKey(), value); } } }