Java tutorial
/* * Copyright 2011 GigaSpaces Technologies Ltd * * 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 org.openspaces.rest.space; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.logging.Logger; import javax.servlet.ServletContext; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClientBuilder; import org.cloudifysource.domain.Application; import org.cloudifysource.dsl.internal.CloudifyConstants.DeploymentState; import org.cloudifysource.dsl.internal.DSLReader; import org.cloudifysource.dsl.internal.DSLUtils; import org.cloudifysource.dsl.internal.packaging.Packager; import org.cloudifysource.dsl.rest.request.InstallApplicationRequest; import org.cloudifysource.dsl.rest.response.ApplicationDescription; import org.cloudifysource.dsl.rest.response.InstallApplicationResponse; import org.cloudifysource.dsl.rest.response.UploadResponse; import org.cloudifysource.restclient.RestClient; import org.cloudifysource.restclient.exceptions.RestClientResponseException; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.map.ObjectMapper; import org.openspaces.admin.Admin; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.context.ServletContextAware; /** * Spring MVC controller for a RESTful Replication API * * API: * * Create topology ------- * * Manipulates multiple Cloudify installations to configure replication topologies. * Sequence of operations: * * - Validate request body * -- make sure all target Cloudify REST API are accessible. * -- check whether repl services are running at all locations. * --- if not running, start them * --- if running, check if this topology is already running * ---- if topology running, exit with warning * -- deploy repl spaces * -- deploy gateways * * */ @Controller @RequestMapping(value = "/rest/repl/*") public class ReplicationRESTController implements ServletContextAware { private static final Logger log = Logger.getLogger(ReplicationRESTController.class.getName()); private static final String APPLICATION_PATH = "", CLOUDIFY_VERSION = "2.7.0"; private static final String DEFAULT_DATA_PORT = "10000"; private static final String DEFAULT_DISCOVERY_PORT = "10001"; private Admin admin = null; private HttpClient client = HttpClientBuilder.create().build(); private boolean test = true; @Value("${recipe.path}") private String recipePath; private ServletContext servletContext = null; @RequestMapping(value = "/topology/{name}", method = RequestMethod.POST) @ResponseStatus(HttpStatus.OK) public @ResponseBody String deployTopology(@PathVariable String name, @RequestBody HashMap body) { log.info("deployTopology called"); //Parse and validate body log.info("validating request"); DeployRequest req = new DeployRequest(body); //Check participant sites. log.info("validating participants"); req.tspec.name = name; validateSites(req); //deploy repl services log.info("deploying replication services at all sites"); deployReplApps(req); //wait for completion log.info("deployment initiated, waiting for completion"); if (!waitForDeployments(req, 300000)) { log.info("deployment timed out, rolling back deployment"); rollbackApps(req); throw new RuntimeException("repl application deployment failed"); } log.info("deployment complete"); //xap running everywhere, deploy spaces log.info("deploying replication spaces on all sites"); deploySpaces(req); log.info("spaces deployed"); //deploy gateways log.info("deploying gateways on all sites"); deployGateways(req); log.info("gateways deployed"); return "done"; } private Map<String, List<String>> makeGatewayAddressMap(DeployRequest req) { Map<String, List<String>> result = new HashMap<String, List<String>>(); HttpClient client = HttpClientBuilder.create().build(); for (CloudifyRestEndpoint ep : req.endpoints) { //get gateway host (assumes 1 per site) public ip String publicIp = ""; String privateIp = ""; HttpGet get = new HttpGet("http://" + ep.address + ":" + ep.port + "/" + CLOUDIFY_VERSION + "/deployments/" + req.tspec.name + "/service/repl-gateway/instances/1/metadata"); try { HttpResponse resp = client.execute(get); String resptext = IOUtils.toString(resp.getEntity().getContent()); log.info("got ip request response:" + resptext); JsonNode json = new ObjectMapper().readTree(resptext); publicIp = json.get("response").get("processDetails").get("Cloud Public IP").getTextValue(); privateIp = json.get("response").get("processDetails").get("Cloud Private IP").getTextValue(); log.info(String.format("%s returned gateway private ip:%s public ip:%s", ep.address, privateIp, publicIp)); } catch (Exception e) { throw new RuntimeException("failed to get public ip of gateway", e); } result.put(ep.siteId, Arrays.asList(privateIp, publicIp)); } return result; } private void deploySpaces(final DeployRequest req) { ExecutorService esvc = Executors.newFixedThreadPool(req.endpoints.size()); List<Callable<DeployResult>> tasks = new ArrayList<Callable<DeployResult>>(); try { for (final CloudifyRestEndpoint ep : req.endpoints) { tasks.add(new Callable<DeployResult>() { @Override public DeployResult call() throws Exception { HttpClient client = HttpClientBuilder.create().build(); HttpPost post = new HttpPost("http://" + ep.address + ":" + ep.port + "/" + CLOUDIFY_VERSION + "/deployments/applications/" + req.tspec.name + "/services/repl-management/invoke"); log.fine("invoking custom command via REST :" + "http://" + ep.address + ":" + ep.port + "/" + CLOUDIFY_VERSION + "/deployments/applications/" + req.tspec.name + "/services/repl-management/invoke"); post.setEntity(new StringEntity(String.format( "{\"commandName\":\"add-repl-space\",\"parameters\":[\"%s\",%d,%d,\"%s\"]}", req.tspec.name, 1, 0, ep.siteId), ContentType.APPLICATION_JSON)); log.fine(" post entity:" + String.format( "{\"commandName\":\"add-repl-space\",\"parameters\":[\"%s\",%d,%d,\"%s\"]}", req.tspec.name, 1, 0, ep.siteId)); HttpResponse resp = client.execute(post); return new DeployResult(resp, ep); } }); } List<Future<DeployResult>> results = new ArrayList<Future<DeployResult>>(); try { results = esvc.invokeAll(tasks); } catch (InterruptedException e) { throw new RuntimeException(e); } StringBuilder sb = new StringBuilder("Repl space deploy failed:"); int initlength = sb.length(); DeployResult dr = null; for (Future<DeployResult> future : results) { try { dr = future.get(); } catch (Exception e) { sb.append(dr.ep.address).append(" caught exception - ").append(e.getMessage()).append(","); } try { if (dr.response.getStatusLine().getStatusCode() != 200) { String body = IOUtils.toString(dr.response.getEntity().getContent()); sb.append(dr.ep.address).append(" returned status ") .append(dr.response.getStatusLine().getStatusCode()).append(",reason=") .append(dr.response.getStatusLine().getReasonPhrase()).append(",").append(body); } else { log.info("got response 200: " + dr.response.getStatusLine().toString()); } } catch (Exception e) { sb.append(dr.ep.address).append(" caught exception processing response body- ") .append(e.getMessage()).append(","); } } if (sb.length() > initlength) {//error caught throw new RuntimeException(sb.toString()); } } finally { esvc.shutdown(); } } private void deployGateways(final DeployRequest req) { ExecutorService esvc = Executors.newFixedThreadPool(req.endpoints.size()); List<Callable<DeployResult>> tasks = new ArrayList<Callable<DeployResult>>(); try { final Map<String, List<String>> gwaddresses = makeGatewayAddressMap(req); final String natArg = addressesToNat(gwaddresses); for (final CloudifyRestEndpoint ep : req.endpoints) { tasks.add(new Callable<DeployResult>() { @Override public DeployResult call() throws Exception { HttpClient client = HttpClientBuilder.create().build(); HttpPost post = new HttpPost("http://" + ep.address + ":" + ep.port + "/" + CLOUDIFY_VERSION + "/deployments/applications/" + req.tspec.name + "/services/repl-gateway/invoke"); log.info("invoking custom command via REST :" + "http://" + ep.address + ":" + ep.port + "/" + CLOUDIFY_VERSION + "/deployments/applications/" + req.tspec.name + "/services/repl-gateway/invoke"); String entityString = String.format( "{\"commandName\":\"install-gateway\",\"parameters\":[\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"]}", req.tspec.name, ep.siteId, tspecToPairsArg(req.tspec), tspecToLookups(ep, req.tspec, gwaddresses), natArg); post.setEntity(new StringEntity(entityString, ContentType.APPLICATION_JSON)); log.info(" post entity:" + entityString); HttpResponse resp = client.execute(post); return new DeployResult(resp, ep); } }); } List<Future<DeployResult>> results = new ArrayList<Future<DeployResult>>(); try { results = esvc.invokeAll(tasks); } catch (InterruptedException e) { throw new RuntimeException(e); } StringBuilder sb = new StringBuilder("Gateway deploy failed:"); int initlength = sb.length(); DeployResult dr = null; for (Future<DeployResult> future : results) { try { dr = future.get(); } catch (Exception e) { sb.append(dr.ep.address).append(" caught exception - ").append(e.getMessage()).append(","); } try { if (dr.response.getStatusLine().getStatusCode() != 200) { String body = IOUtils.toString(dr.response.getEntity().getContent()); sb.append(dr.ep.address).append(" returned status ") .append(dr.response.getStatusLine().getStatusCode()).append(",reason=") .append(dr.response.getStatusLine().getReasonPhrase()).append(",").append(body); } else { log.info("got response 200: " + dr.response.getStatusLine().toString()); } } catch (Exception e) { sb.append(dr.ep.address).append(" caught exception processing response body- ") .append(e.getMessage()).append(","); } } if (sb.length() > initlength) {//error caught throw new RuntimeException(sb.toString()); } } finally { esvc.shutdown(); } } //Make public to private map for gateway clients private String addressesToNat(Map<String, List<String>> gwaddresses) { StringBuilder sb = new StringBuilder("["); for (Map.Entry<String, List<String>> entry : gwaddresses.entrySet()) { sb.append(entry.getValue().get(1)).append(":").append(entry.getValue().get(0)).append(","); } sb.deleteCharAt(sb.length() - 1); sb.append("]"); log.info("addressesToNat returning:" + sb.toString()); return sb.toString(); } private boolean waitForDeployments(DeployRequest req, long timeout) { Map<CloudifyRestEndpoint, RestClient> clients = new HashMap<CloudifyRestEndpoint, RestClient>(); Set<CloudifyRestEndpoint> deployed = new HashSet<CloudifyRestEndpoint>(); for (CloudifyRestEndpoint ep : req.endpoints) { try { clients.put(ep, new RestClient(new URL("http://" + ep.address + ":" + ep.port), "", "", CLOUDIFY_VERSION)); } catch (Exception e) { throw new RuntimeException(e); } } long start = System.currentTimeMillis(); try { while (true) { for (CloudifyRestEndpoint ep : req.endpoints) { ApplicationDescription desc = clients.get(ep).getApplicationDescription(req.tspec.name); if (desc.getApplicationState() == DeploymentState.STARTED) { deployed.add(ep); if (deployed.size() == req.endpoints.size()) return true; } else if (desc.getApplicationState() == DeploymentState.FAILED) { log.warning("deployment failed at ip:" + ep.address); return false; } if (start - System.currentTimeMillis() > timeout - 2000) { log.warning("deployment timeout"); return false; } } Thread.sleep(2000); } } catch (Exception e) { //rollback installs log.warning("exception on repl installation: rolling back -->" + e.getMessage()); e.printStackTrace(); rollbackApps(req); return false; } } /* * Initiates deployment of the replication infrastructure. Returns * deployment ids for each application */ private void deployReplApps(DeployRequest req) { try { int gscCnt = req.maxMemorySize / req.maxVmSize; //launch deployments of repl-service app Map<CloudifyRestEndpoint, String> ids = new HashMap<CloudifyRestEndpoint, String>(); for (CloudifyRestEndpoint endpoint : req.endpoints) { final RestClient restClient = new RestClient( new URL("http://" + endpoint.address + ":" + endpoint.port), "", "", CLOUDIFY_VERSION); restClient.connect(); //upload app File packed = Packager.packApplication( createDslReader(new File(recipePath + File.separator + "repl-service" + File.separator + "cloudify" + File.separator + "repl")).readDslEntity(Application.class), new File(recipePath)); UploadResponse uploadResponse = restClient.upload(null, packed); //upload overrides String appUploadKey = uploadResponse.getUploadKey(); File props = File.createTempFile("repl", "properties"); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(props))); writer.write(String.format("containerCount=%d", gscCnt)); writer.newLine(); writer.write(String.format("gscMinSize=\"-Xms%dm\"", req.maxVmSize)); writer.newLine(); writer.write(String.format("gscMaxSize=\"-Xms%dm\"", req.maxVmSize)); writer.newLine(); writer.write(String.format("gscOtherOptions=\"" + " -Dcom.gs.transport_protocol.lrmi.network-mapper=org.openspaces.repl.natmapper.ReplNatMapper" + " -Dcom.gs.transport_protocol.lrmi.network-mapping-file=/tmp/network_mapping.config\"")); writer.newLine(); writer.write(String.format("zones=\"%s-datazone\"", req.tspec.name)); writer.newLine(); writer.close(); uploadResponse = restClient.upload(null, props); props.delete(); //Install InstallApplicationRequest installreq = new InstallApplicationRequest(); installreq.setApplcationFileUploadKey(appUploadKey); installreq.setApplicationOverridesUploadKey(uploadResponse.getUploadKey()); installreq.setApplicationName(req.tspec.name); installreq.setDebugAll(false); installreq.setAuthGroups(""); // for now InstallApplicationResponse response = restClient.installApplication(req.tspec.name, installreq); ids.put(endpoint, response.getDeploymentID()); } } catch (RestClientResponseException re) { log.severe("caught exception installing apps:" + re.getReasonPhrase() + ":" + re.getMessageFormattedText()); log.severe("rolling back and failing"); rollbackApps(req); throw new RuntimeException(re); } catch (Exception e) { log.severe("caught exception installing apps:" + e.getMessage()); log.severe("rolling back and failing"); rollbackApps(req); if (e instanceof RuntimeException) throw (RuntimeException) e; throw new RuntimeException(e); } } private void rollbackApps(DeployRequest req) { for (CloudifyRestEndpoint ep : req.endpoints) { try { RestClient rc = new RestClient(new URL("http://" + ep.address + ":" + ep.port), "", "", CLOUDIFY_VERSION); rc.uninstallApplication(req.tspec.name, 5); } catch (Exception e2) { log.warning("caught exception rolling back " + ep.address); e2.printStackTrace(); } } } public Admin getAdmin() { return admin; } public void setAdmin(Admin admin) { this.admin = admin; } protected void validateSites(DeployRequest req) { //verify basic connectivity between sites for (CloudifyRestEndpoint ep : req.endpoints) { if (!checkSite(req.tspec.name, "http://" + ep.address + ":" + ep.port).equals("success")) throw new RuntimeException("failed to contact rest api at " + ep.toString()); } } // Produces "pairs" arg from topo spec protected String tspecToPairsArg(TopologySpec tspec) { if (tspec == null) return "[]"; if (tspec.edges == null || tspec.edges.size() == 0) return "[]"; StringBuilder sb = new StringBuilder("["); for (TopologyEdge edge : tspec.edges) { sb.append("["); sb.append("").append(edge.fromSite).append("").append(","); sb.append(edge.toSite); sb.append("],"); } sb.deleteCharAt(sb.length() - 1); sb.append("]"); return sb.toString(); } // Produces "lookups" arg from topo spec protected String tspecToLookups(CloudifyRestEndpoint ep, TopologySpec tspec, Map<String, List<String>> addresses) { if (tspec == null || addresses == null) return "[]"; StringBuilder sb = new StringBuilder("["); Set<String> done = new HashSet<String>(); for (TopologyEdge edge : tspec.edges) { for (String site : Arrays.asList(edge.fromSite, edge.toSite)) { if (done.contains(site)) continue; done.add(site); sb.append("["); sb.append("gwname:").append(site).append(","); if (ep.siteId.equals(edge.fromSite)) { sb.append("address:").append(addresses.get(site).get(0)).append(","); } else { sb.append("address:").append(addresses.get(site).get(1)).append(","); } sb.append("discoport:").append(tspec.ports.get(site).get(0)).append(","); sb.append("commport:").append(tspec.ports.get(site).get(1)); sb.append("],"); } } sb.deleteCharAt(sb.length() - 1); //trim comma sb.append("]"); return sb.toString(); } protected String checkSite(String appName, String url) { try { HttpGet get = new HttpGet(url + "/service/applications"); log.info("pinging " + get.getURI()); HttpResponse response = client.execute(get); ObjectMapper om = new ObjectMapper(); JsonNode node = om.readTree(response.getEntity().getContent()); String status = node.get("status").getValueAsText(); log.info("got response " + (status == null ? "null" : status)); for (Iterator<JsonNode> it = node.get("response").getElements(); it.hasNext();) { if (it.next().equals(appName)) throw new RuntimeException("topology already running"); } return status; } catch (Exception e) { if (e instanceof RuntimeException) throw (RuntimeException) e; throw new RuntimeException(e); } } private static DSLReader createDslReader(final File applicationFile) { final DSLReader dslReader = new DSLReader(); final File dslFile = DSLReader.findDefaultDSLFile(DSLUtils.APPLICATION_DSL_FILE_NAME_SUFFIX, applicationFile); dslReader.setDslFile(dslFile); dslReader.setCreateServiceContext(false); dslReader.addProperty(DSLUtils.APPLICATION_DIR, dslFile.getParentFile().getAbsolutePath()); return dslReader; } public static void main(String[] args) throws Exception { ReplicationRESTController ctl = new ReplicationRESTController(); HashMap body = new HashMap(); Map site1 = new HashMap(); site1.put("rest-api", "ec2-54-221-106-51.compute-1.amazonaws.com:8100"); site1.put("site-id", "EC2"); Map site2 = new HashMap(); site2.put("rest-api", "15.185.186.251:8100"); site2.put("site-id", "HPCS"); List sites = new ArrayList(); sites.add(site1); sites.add(site2); body.put("cloudify-instances", sites); HashMap tspec = new HashMap(); tspec.put("name", "my-topo"); List edges = new ArrayList(); HashMap edge1 = new HashMap(); edge1.put("fromSite", "EC2"); edge1.put("fromSpace", "test"); edge1.put("toSite", "HPCS"); edge1.put("toSpace", "test"); edges.add(edge1); tspec.put("edges", edges); HashMap ports = new HashMap(); HashMap port1 = new HashMap(); port1.put("discovery", DEFAULT_DISCOVERY_PORT); port1.put("data", DEFAULT_DATA_PORT); ports.put("EC2", port1); HashMap port2 = new HashMap(); port2.put("discovery", DEFAULT_DISCOVERY_PORT); port2.put("data", DEFAULT_DATA_PORT); ports.put("HPCS", port2); tspec.put("ports", ports); body.put("topology-spec", tspec); body.put("initial-memory-size", "256m"); body.put("max-memory-size", "512m"); body.put("highly-available", "false"); body.put("max-vm-size", "512"); body.put("batch-size", "1024"); body.put("send-interval", "1000"); ctl.deployTopology("my-topo", body); //deploy service test /* RestClient rc=new RestClient(new URL("http://localhost:8100"),"","",CLOUDIFY_VERSION); rc.connect(); InstallServiceRequest r=new InstallServiceRequest(); File packed=Packager.pack(new File("c:\\gigaspaces\\src\\github\\cloudify-recipes\\services\\tomcat")); final UploadResponse uploadResponse = rc.upload(null, packed); final String uploadKey = uploadResponse.getUploadKey(); r.setServiceFolderUploadKey(uploadKey); r.setAuthGroups(""); r.setServiceFileName("tomcat-service.groovy"); InstallServiceResponse installServiceRespone = rc.installService("test","yomama", r); System.out.println(uploadKey); //r.set //rc.installService("default","test",); */ /* HttpClient client=HttpClientBuilder.create().build(); HttpGet get=new HttpGet("http://ec2-54-197-25-132.compute-1.amazonaws.com:8100/"); HttpResponse response=client.execute(get); InputStream is=response.getEntity().getContent(); BufferedReader rdr=new BufferedReader(new InputStreamReader(is)); String line=""; while((line=rdr.readLine())!=null){ System.out.println(line); } */ /*ObjectMapper om=new ObjectMapper(); JsonNode node=om.readTree(is); System.out.println(node.get("response").get(1));*/ /* ObjectMapper om=new ObjectMapper(); JsonNode node=om.readTree("{\"response\":[\"app1\",\"app2\"]}"); for(Iterator<JsonNode> it=node.get("response").getElements();it.hasNext();){ System.out.println(it.next().getValueAsText()); } */ } @Override public void setServletContext(ServletContext servletContext) { this.servletContext = servletContext; } } class DeployRequest { public List<CloudifyRestEndpoint> endpoints; public TopologySpec tspec; public int initMemorySize; public int maxMemorySize; public boolean highlyAvailable = true; public int maxVmSize; public int batchSize; public int sendInterval; public DeployRequest(Map body) { List<Map> inst = (List<Map>) body.get("cloudify-instances"); if (inst == null || inst.size() == 0) throw new RuntimeException("validation error:no cloudify instances provided"); for (Map<String, String> map : inst) { String api = map.get("rest-api"); String id = map.get("site-id"); if (endpoints == null) endpoints = new ArrayList<CloudifyRestEndpoint>(); endpoints.add(new CloudifyRestEndpoint(id, api)); } Map ts = (Map) body.get("topology-spec"); if (ts == null) throw new RuntimeException("validation error: no topology spec"); tspec = new TopologySpec(ts); initMemorySize = parseMemoryString((String) body.get("initial-memory-size")); maxMemorySize = parseMemoryString((String) body.get("max-memory-size")); Boolean ha = Boolean.parseBoolean((String) body.get("highly-available")); if (ha != null) highlyAvailable = ha; maxVmSize = Integer.parseInt((String) body.get("max-vm-size")); // in mb units Integer bs = Integer.parseInt((String) body.get("batch-size")); if (bs != null) batchSize = bs; Integer si = Integer.parseInt((String) body.get("send-interval")); if (si != null) sendInterval = si; } private int parseMemoryString(String string) { if (string.length() < 2) throw new RuntimeException("invalid memory string:" + string); if (Character.isDigit(string.charAt(string.length() - 1))) throw new RuntimeException("invalid memory string: units required (m or g)"); int base = Integer.parseInt(string.substring(0, string.length() - 1)); if (string.substring(string.length() - 1).toLowerCase().equals("g")) base *= 1024; //default is mb return base; } } class CloudifyRestEndpoint { public String address; public int port = 8100; public String siteId; public CloudifyRestEndpoint(String id, String address_in) { String[] toks = address_in.split(":"); this.siteId = id; this.address = toks[0]; if (toks.length > 1) this.port = Integer.parseInt(toks[1]); } public String toString() { return siteId + "@" + address + ":" + port; } } class TopologySpec { public String name; public Set<TopologyEdge> edges = new HashSet<TopologyEdge>(); public Map<String, List<Integer>> ports = new HashMap<String, List<Integer>>(); public TopologySpec() { } public TopologySpec(Map ts) { this.name = (String) ts.get("name"); List<Map<String, String>> inputedges = (List<Map<String, String>>) ts.get("edges"); if (inputedges == null || inputedges.size() == 0) throw new RuntimeException("no replication pairs defined"); for (Map<String, String> edge : inputedges) { TopologyEdge newedge = new TopologyEdge(edge.get("fromSite"), edge.get("fromSpace"), edge.get("toSite"), edge.get("toSpace")); edges.add(newedge); } Map<String, Map<String, String>> portmap = (Map<String, Map<String, String>>) ts.get("ports"); if (portmap == null) portmap = new HashMap<String, Map<String, String>>(); for (Map.Entry<String, Map<String, String>> entry : portmap.entrySet()) { List<Integer> plist = new ArrayList<Integer>(); plist.add(Integer.parseInt(entry.getValue().get("discovery"))); plist.add(Integer.parseInt(entry.getValue().get("data"))); ports.put(entry.getKey(), plist); } } } class TopologyEdge { String fromSite; String fromSpace; String toSite; String toSpace; public TopologyEdge(String fromSite, String fromSpace, String toSite, String toSpace) { this.fromSite = fromSite; this.fromSpace = fromSpace; this.toSite = toSite; this.toSpace = toSpace; } @Override public boolean equals(Object other) { if (other == null) return false; if (!(this.getClass().isInstance(other))) return false; TopologyEdge otheredge = (TopologyEdge) other; if (otheredge.fromSite.equals(this.fromSite) && otheredge.fromSpace.equals(this.fromSpace) && otheredge.toSite.equals(this.toSite) && otheredge.toSpace.equals(this.toSpace)) return true; return false; } } class DeployResult { HttpResponse response; CloudifyRestEndpoint ep; public DeployResult(HttpResponse response, CloudifyRestEndpoint ep) { super(); this.response = response; this.ep = ep; } }