/*******************************************************************************
* Copyright (c) 2013 IBM Corporation and others
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.orion.server.cf.commands;
import java.net.URI;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.URIUtil;
import org.eclipse.orion.server.cf.CFProtocolConstants;
import org.eclipse.orion.server.cf.manifest.v2.InvalidAccessException;
import org.eclipse.orion.server.cf.manifest.v2.ManifestParseTree;
import org.eclipse.orion.server.cf.objects.App;
import org.eclipse.orion.server.cf.objects.Target;
import org.eclipse.orion.server.cf.utils.HttpUtil;
import org.eclipse.orion.server.cf.utils.MultiServerStatus;
import org.eclipse.orion.server.core.ServerStatus;
import org.eclipse.osgi.util.NLS;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BindServicesCommand extends AbstractCFApplicationCommand {
private final Logger logger = LoggerFactory.getLogger("org.eclipse.orion.server.cf"); //$NON-NLS-1$
private String commandName;
public BindServicesCommand(Target target, App app) {
super(target, app);
String[] bindings = {app.getName(), app.getGuid()};
this.commandName = NLS.bind("Bind new services to application {1} (guid: {2})", bindings);
}
@Override
protected ServerStatus _doIt() {
/* multi server status */
MultiServerStatus status = new MultiServerStatus();
try {
/* bind services */
URI targetURI = URIUtil.toURI(target.getUrl());
ManifestParseTree manifest = getApplication().getManifest();
ManifestParseTree app = manifest.get("applications").get(0); //$NON-NLS-1$
if (app.has(CFProtocolConstants.V2_KEY_SERVICES)) {
/* fetch all services */
URI servicesURI = targetURI.resolve("/v2/services"); //$NON-NLS-1$
GetMethod getServicesMethod = new GetMethod(servicesURI.toString());
ServerStatus confStatus = HttpUtil.configureHttpMethod(getServicesMethod, target.getCloud());
if (!confStatus.isOK())
return confStatus;
getServicesMethod.setQueryString("inline-relations-depth=1"); //$NON-NLS-1$
/* send request */
ServerStatus jobStatus = HttpUtil.executeMethod(getServicesMethod);
status.add(jobStatus);
if (!jobStatus.isOK())
return status;
JSONObject resp = jobStatus.getJsonData();
JSONArray servicesJSON = resp.getJSONArray(CFProtocolConstants.V2_KEY_RESOURCES);
/* check for manifest version */
ManifestParseTree services = app.getOpt(CFProtocolConstants.V2_KEY_SERVICES);
if (services == null)
/* nothing to do */
return status;
int version = services.isList() ? 6 : 2;
if (version == 2) {
String spaceGuid = target.getSpace().getGuid();
URI serviceInstancesURI2 = targetURI.resolve("/v2/spaces/" + spaceGuid + "/service_instances"); //$NON-NLS-1$//$NON-NLS-2$
for (ManifestParseTree service : services.getChildren()) {
String serviceName = service.getLabel();
String nameService = "name:" + serviceName; //$NON-NLS-1$
NameValuePair[] pa = new NameValuePair[] {new NameValuePair("return_user_provided_service_instances", "true"), // //$NON-NLS-1$//$NON-NLS-2$
new NameValuePair("q", nameService), new NameValuePair("inline-relations-depth", "1") // //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
};
GetMethod getServiceMethod = new GetMethod(serviceInstancesURI2.toString());
getServiceMethod.setQueryString(pa);
confStatus = HttpUtil.configureHttpMethod(getServiceMethod, target.getCloud());
if (!confStatus.isOK())
return confStatus;
/* send request */
jobStatus = HttpUtil.executeMethod(getServiceMethod);
status.add(jobStatus);
if (!jobStatus.isOK())
return status;
resp = jobStatus.getJsonData();
String serviceInstanceGUID = null;
JSONArray respArray = resp.getJSONArray(CFProtocolConstants.V2_KEY_RESOURCES);
for (int i = 0; i < respArray.length(); ++i) {
JSONObject o = respArray.optJSONObject(i);
if (o != null) {
JSONObject str = o.optJSONObject(CFProtocolConstants.V2_KEY_METADATA);
if (str != null) {
serviceInstanceGUID = str.getString(CFProtocolConstants.V2_KEY_GUID);
break;
}
}
}
if (serviceInstanceGUID == null) {
/* no service instance bound to the application, create one if possible */
/* support both 'type' and 'label' fields as service type */
ManifestParseTree serviceType = service.getOpt(CFProtocolConstants.V2_KEY_TYPE);
if (serviceType == null)
serviceType = service.get(CFProtocolConstants.V2_KEY_LABEL);
ManifestParseTree provider = service.get(CFProtocolConstants.V2_KEY_PROVIDER);
ManifestParseTree plan = service.get(CFProtocolConstants.V2_KEY_PLAN);
String servicePlanGUID = findServicePlanGUID(serviceType.getValue(), provider.getValue(), plan.getValue(), servicesJSON);
if (servicePlanGUID == null) {
String[] bindings = {serviceName, serviceType.getValue(), plan.getValue()};
String msg = NLS.bind("Could not find service instance {0} nor service {1} with plan {2} in target.", bindings);
status.add(new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, msg, null));
return status;
}
/* create service instance */
URI serviceInstancesURI = targetURI.resolve("/v2/service_instances"); //$NON-NLS-1$
PostMethod createServiceMethod = new PostMethod(serviceInstancesURI.toString());
confStatus = HttpUtil.configureHttpMethod(createServiceMethod, target.getCloud());
if (!confStatus.isOK())
return confStatus;
/* set request body */
JSONObject createServiceRequest = new JSONObject();
createServiceRequest.put(CFProtocolConstants.V2_KEY_SPACE_GUID, target.getSpace().getCFJSON().getJSONObject(CFProtocolConstants.V2_KEY_METADATA).getString(CFProtocolConstants.V2_KEY_GUID));
createServiceRequest.put(CFProtocolConstants.V2_KEY_NAME, serviceName);
createServiceRequest.put(CFProtocolConstants.V2_KEY_SERVICE_PLAN_GUID, servicePlanGUID);
createServiceMethod.setRequestEntity(new StringRequestEntity(createServiceRequest.toString(), "application/json", "utf-8")); //$NON-NLS-1$ //$NON-NLS-2$
/* send request */
jobStatus = HttpUtil.executeMethod(createServiceMethod);
status.add(jobStatus);
if (!jobStatus.isOK())
return status;
resp = jobStatus.getJsonData();
serviceInstanceGUID = resp.getJSONObject(CFProtocolConstants.V2_KEY_METADATA).getString(CFProtocolConstants.V2_KEY_GUID);
}
/* bind service to the application */
URI serviceBindingsURI = targetURI.resolve("/v2/service_bindings"); //$NON-NLS-1$
PostMethod bindServiceMethod = new PostMethod(serviceBindingsURI.toString());
confStatus = HttpUtil.configureHttpMethod(bindServiceMethod, target.getCloud());
if (!confStatus.isOK())
return confStatus;
/* set request body */
JSONObject bindServiceRequest = new JSONObject();
bindServiceRequest.put(CFProtocolConstants.V2_KEY_APP_GUID, getApplication().getGuid());
bindServiceRequest.put(CFProtocolConstants.V2_KEY_SERVICE_INSTANCE_GUID, serviceInstanceGUID);
bindServiceMethod.setRequestEntity(new StringRequestEntity(bindServiceRequest.toString(), "application/json", "utf-8")); //$NON-NLS-1$ //$NON-NLS-2$
/* send request */
jobStatus = HttpUtil.executeMethod(bindServiceMethod);
status.add(jobStatus);
if (!jobStatus.isOK())
return status;
}
}
if (version == 6) {
String spaceGuid = target.getSpace().getGuid();
URI serviceInstancesURI = targetURI.resolve("/v2/spaces/" + spaceGuid + "/service_instances"); //$NON-NLS-1$//$NON-NLS-2$
for (ManifestParseTree service : services.getChildren()) {
String nameService = service.getValue();
NameValuePair[] pa = new NameValuePair[] {new NameValuePair("return_user_provided_service_instances", "true"), // //$NON-NLS-1$ //$NON-NLS-2$
new NameValuePair("q", "name:" + nameService), new NameValuePair("inline-relations-depth", "1")}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
GetMethod getServiceMethod = new GetMethod(serviceInstancesURI.toString());
getServiceMethod.setQueryString(pa);
confStatus = HttpUtil.configureHttpMethod(getServiceMethod, target.getCloud());
if (!confStatus.isOK())
return confStatus;
/* send request */
jobStatus = HttpUtil.executeMethod(getServiceMethod);
status.add(jobStatus);
if (!jobStatus.isOK())
return status;
resp = jobStatus.getJsonData();
String serviceInstanceGUID = null;
JSONArray respArray = resp.getJSONArray(CFProtocolConstants.V2_KEY_RESOURCES);
for (int i = 0; i < respArray.length(); i++) {
JSONObject o = respArray.optJSONObject(i);
if (o != null) {
JSONObject str = o.optJSONObject(CFProtocolConstants.V2_KEY_METADATA);
if (str != null) {
serviceInstanceGUID = str.getString(CFProtocolConstants.V2_KEY_GUID);
}
}
}
if (serviceInstanceGUID == null) {
JSONObject error = new JSONObject();
error.put("service", nameService);
status.add(HttpUtil.createErrorStatus(IStatus.ERROR, "ServiceNotFound", "Service instance " + nameService + " cannot be found in target space", error));
return status;
}
/* bind service to the application */
URI serviceBindingsURI = targetURI.resolve("/v2/service_bindings"); //$NON-NLS-1$
PostMethod bindServiceMethod = new PostMethod(serviceBindingsURI.toString());
confStatus = HttpUtil.configureHttpMethod(bindServiceMethod, target.getCloud());
if (!confStatus.isOK())
return confStatus;
/* set request body */
JSONObject bindServiceRequest = new JSONObject();
bindServiceRequest.put(CFProtocolConstants.V2_KEY_APP_GUID, getApplication().getGuid());
bindServiceRequest.put(CFProtocolConstants.V2_KEY_SERVICE_INSTANCE_GUID, serviceInstanceGUID);
bindServiceMethod.setRequestEntity(new StringRequestEntity(bindServiceRequest.toString(), "application/json", "utf-8")); //$NON-NLS-1$ //$NON-NLS-2$
/* send request */
jobStatus = HttpUtil.executeMethod(bindServiceMethod);
if (!jobStatus.isOK()) {
/* the binding might be already present - detect it by checking the error code type */
if (!jobStatus.getJsonData().has("error_code") || !"CF-ServiceBindingAppServiceTaken".equals(jobStatus.getJsonData().getString("error_code"))) { //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
status.add(jobStatus);
return status;
}
} else
status.add(jobStatus);
}
}
}
return status;
} catch (InvalidAccessException e) {
status.add(new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_BAD_REQUEST, e.getMessage(), null));
return status;
} catch (Exception e) {
String msg = NLS.bind("An error occured when performing operation {0}", commandName); //$NON-NLS-1$
logger.error(msg, e);
status.add(new ServerStatus(IStatus.ERROR, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg, e));
return status;
}
}
/* helper method to find the appropriate service plan guid */
private String findServicePlanGUID(String service, String provider, String plan, JSONArray servicesJSON) throws JSONException {
for (int i = 0; i < servicesJSON.length(); ++i) {
JSONObject serviceJSON = servicesJSON.getJSONObject(i).getJSONObject(CFProtocolConstants.V2_KEY_ENTITY);
if (service.equals(serviceJSON.getString(CFProtocolConstants.V2_KEY_LABEL)) && provider.equals(serviceJSON.getString(CFProtocolConstants.V2_KEY_PROVIDER))) {
/* find correct service plan */
JSONArray servicePlans = serviceJSON.getJSONArray(CFProtocolConstants.V2_KEY_SERVICE_PLANS);
for (int j = 0; j < servicePlans.length(); ++j) {
JSONObject servicePlan = servicePlans.getJSONObject(j);
if (plan.equals(servicePlan.getJSONObject(CFProtocolConstants.V2_KEY_ENTITY).getString(CFProtocolConstants.V2_KEY_NAME)))
return servicePlan.getJSONObject(CFProtocolConstants.V2_KEY_METADATA).getString(CFProtocolConstants.V2_KEY_GUID);
}
}
}
return null;
}
}