/**
*
*/
package org.opentosca.csarrepo.util;
import java.io.File;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation.Builder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.media.multipart.FormDataBodyPart;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.opentosca.csarrepo.exception.DeploymentException;
import org.opentosca.csarrepo.model.OpenToscaServer;
import org.opentosca.csarrepo.util.jaxb.DeployedCsars;
import org.opentosca.csarrepo.util.jaxb.ServiceInstanceEntry;
import org.opentosca.csarrepo.util.jaxb.ServiceInstanceList;
import org.opentosca.csarrepo.util.jaxb.SimpleXLink;
/**
* This class establishes a connection to a given ContainerAPI URL
*
* It enables a User of this class to upload a CSAR and trigger its deployment
*
* @author Marcus Eisele (marcus.eisele@gmail.com), Dennis Przytarski, Thomas
* Kosch
*
*/
public class ContainerApiClient {
private WebTarget baseWebTarget;
private Client client;
private static final Logger LOGGER = LogManager.getLogger(ContainerApiClient.class);
/**
* Creates a ContainerApiClient which connects to the given URI
*
* @param address
* @throws URISyntaxException
*/
public ContainerApiClient(OpenToscaServer openToscaServer) throws URISyntaxException {
ClientConfig clientConfig = new ClientConfig();
clientConfig.register(MultiPartFeature.class);
clientConfig.property(ClientProperties.CHUNKED_ENCODING_SIZE, 1024);
this.client = ClientBuilder.newClient(clientConfig);
// TODO: check if it possible to store address as URI instead of URL
baseWebTarget = client.target(openToscaServer.getAddress().toURI());
}
/**
* Uploads a CsarFile and triggers its processing
*
* @param csarFile
* @return the location where the instance was created
*/
public String uploadFileToOpenTOSCA(File file, String fileName) throws DeploymentException {
try {
if (!file.exists()) {
throw new DeploymentException(String.format("File %s doesn't exist", file.getAbsolutePath()));
}
// build the message
FormDataMultiPart multiPart = new FormDataMultiPart();
FormDataContentDisposition.FormDataContentDispositionBuilder dispositionBuilder = FormDataContentDisposition
.name("file");
dispositionBuilder.fileName(fileName);
dispositionBuilder.size(file.getTotalSpace());
FormDataContentDisposition formDataContentDisposition = dispositionBuilder.build();
multiPart.bodyPart(new FormDataBodyPart("file", file, MediaType.APPLICATION_OCTET_STREAM_TYPE)
.contentDisposition(formDataContentDisposition));
Entity<FormDataMultiPart> entity = Entity.entity(multiPart, MediaType.MULTIPART_FORM_DATA_TYPE);
// submit the request
WebTarget path = baseWebTarget.path("CSARs");
Builder request = path.request();
Response response = request.post(entity);
// handle response
if (Status.CREATED.getStatusCode() == response.getStatus()) {
return response.getHeaderString("location");
} else {
LOGGER.warn("Failed to deploy: " + file.getAbsolutePath() + " to " + path);
throw new DeploymentException("Deployment failed - OpenTOSCA Server returned " + response.getStatus());
}
} catch (ProcessingException e) {
LOGGER.warn("Failed to upload CSAR: Server - server was not reachable", e);
throw new DeploymentException("Deletion failed - OpenTOSCA Server was not reachable");
}
}
/**
* Submits a Delete at the given location
*
* @param location
* where the DELETE will be submitted
* @throws DeploymentException
* when the deletion fails
*/
public void deleteCsarAtLocation(String location) throws DeploymentException {
try {
WebTarget deleteTarget = client.target(location);
Builder request = deleteTarget.request();
Response response = request.delete();
if (Status.OK.getStatusCode() == response.getStatus()) {
return;
} else {
LOGGER.warn("Failed to delete CSAR at: " + location);
throw new DeploymentException("Deletion failed - OpenTOSCA Server returned " + response.getStatus());
}
} catch (ProcessingException e) {
LOGGER.warn("Failed to delete CSAR at: " + location + " Server was not reachable.", e);
throw new DeploymentException("Deletion failed - OpenTOSCA Server was not reachable");
}
}
/**
* Submits a GET on the instancedata/serviceInstances Path of the given
* openToscaServer
*
* @param openToscaServer
* @return
* @throws DeploymentException
*
*/
// TODO: maybe we can extend the containerAPI to supply all needed
// attributes inside the serviceInstances resource directly
public List<ServiceInstanceEntry> getServiceInstances() throws DeploymentException {
try {
WebTarget path = baseWebTarget.path("instancedata/serviceInstances");
Builder request = path.request().accept(MediaType.APPLICATION_XML_TYPE);
ServiceInstanceList serviceInstanceList = request.get().readEntity(ServiceInstanceList.class);
List<ServiceInstanceEntry> results = new ArrayList<ServiceInstanceEntry>();
for (SimpleXLink link : serviceInstanceList.getLinks()) {
WebTarget target = client.target(link.getHref());
ServiceInstanceEntry serviceInstanceEntry = target.request().accept(MediaType.APPLICATION_XML_TYPE)
.get(ServiceInstanceEntry.class);
results.add(serviceInstanceEntry);
}
return results;
} catch (ProcessingException e) {
LOGGER.warn("Failed to get running InstancesLiveList - Server was not reachable.", e);
throw new DeploymentException(
"Failed to get running InstancesLiveList - OpenTOSCA Server was not reachable");
}
}
/**
* Gets all deployed CSARs
*
* @return list of deployed csars
* @throws DeploymentException
*/
public List<SimpleXLink> getDeployedCsars() throws DeploymentException {
try {
WebTarget path = baseWebTarget.path("CSARs");
Builder request = path.request();
DeployedCsars deployedCsars = request.get().readEntity(DeployedCsars.class);
List<SimpleXLink> results = new ArrayList<SimpleXLink>();
for (SimpleXLink link : deployedCsars.getLinks()) {
if ("Self".equals(link.getTitle())) {
continue;
}
results.add(link);
}
return results;
} catch (ProcessingException e) {
LOGGER.warn("Failed to get deployed CSARs - Server was not reachable.", e);
throw new DeploymentException("Failed to get deployed CSARs - OpenTOSCA Server was not reachable");
}
}
/**
* Returns the CSAR file id for the given CSAR filename
*
* @param csarFileName
* @return null, if csarFileId not found.
* @throws DeploymentException
*/
public Long getRepositoryCsarFileId(String csarFileName) throws DeploymentException {
try {
WebTarget path = baseWebTarget.path(String.format("CSARs/%s/Content/CSAR-REPOSITORY.txt", csarFileName));
Builder request = path.request().accept(MediaType.APPLICATION_OCTET_STREAM_TYPE);
Response response = request.get();
if (200 == response.getStatus()) {
String data = response.readEntity(String.class);
Long csarFileId = Long.valueOf(data);
LOGGER.debug("CSAR file id for {} found: {}", csarFileName, csarFileId);
return csarFileId;
} else {
LOGGER.debug("CSAR file id for {} not found: Status code was not 200.", csarFileName);
}
} catch (ProcessingException e) {
LOGGER.warn("Failed to get CSAR file id - Server was not reachable.", e);
throw new DeploymentException("Failed to get CSAR file id - OpenTOSCA Server was not reachable");
}
return null;
}
}