com.sap.cmclient.http.CMODataAbapClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ci-integration-lib-abap Show documentation
Show all versions of ci-integration-lib-abap Show documentation
SAP ABAP Change Management Integration
package com.sap.cmclient.http;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.primitives.Ints.asList;
import static java.lang.String.format;
import static org.apache.http.HttpStatus.SC_CREATED;
import static org.apache.http.HttpStatus.SC_NOT_FOUND;
import static org.apache.http.HttpStatus.SC_OK;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import org.apache.http.Header;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CookieStore;
import org.apache.http.client.entity.EntityBuilder;
import org.apache.http.client.methods.CloseableHttpResponse;
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.methods.HttpUriRequest;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.olingo.odata2.api.edm.Edm;
import org.apache.olingo.odata2.api.edm.EdmEntityContainer;
import org.apache.olingo.odata2.api.edm.EdmEntitySet;
import org.apache.olingo.odata2.api.edm.EdmException;
import org.apache.olingo.odata2.api.ep.EntityProvider;
import org.apache.olingo.odata2.api.ep.EntityProviderException;
import org.apache.olingo.odata2.api.ep.EntityProviderReadProperties;
import org.apache.olingo.odata2.api.ep.EntityProviderWriteProperties;
import org.apache.olingo.odata2.api.exception.ODataException;
import org.apache.olingo.odata2.api.processor.ODataResponse;
import com.google.common.base.Charsets;
import com.google.common.io.CharStreams;
import com.sap.cmclient.dto.Transport;
public class CMODataAbapClient {
private final TransportRequestBuilder requestBuilder;
private final URI endpoint;
private final HttpClientFactory clientFactory;
private String csrfToken = null;
private Edm dataModel = null;
public CMODataAbapClient(String endpoint, String user, String password) throws URISyntaxException {
this.endpoint = new URI(endpoint);
this.requestBuilder = new TransportRequestBuilder(this.endpoint);
// the same instance needs to be used as long as we are in the same session. Hence multiple
// clients must share the same cookie store. Reason: we are logged on with the first request
// and get a cookie. That cookie - expressing the user has already been logged in - needs to be
// present in all subsequent requests.
CookieStore sessionCookieStore = new BasicCookieStore();
BasicCredentialsProvider basicCredentialsProvider = new BasicCredentialsProvider();
basicCredentialsProvider.setCredentials(
new AuthScope(this.endpoint.getHost(), this.endpoint.getPort()),
new UsernamePasswordCredentials(user, password));
this.clientFactory = new HttpClientFactory(sessionCookieStore, basicCredentialsProvider);
}
public Transport getTransport(String transportId) throws IOException, EntityProviderException, EdmException, UnexpectedHttpResponseException {
try (CloseableHttpClient client = clientFactory.createClient()) {
HttpUriRequest get = requestBuilder.getTransport(transportId);
try(CloseableHttpResponse response = client.execute(get)) {
hasStatusOrFail(response, SC_OK, SC_NOT_FOUND, 500); // 500 is currently returned in case the transport cannot be found.a
if(! hasStatus(response, SC_OK))
return null;
return getTransport(response);
}
}
}
/*
* Response content must represent a transport. In order to ensure this a check for SC_OK or SC_CREATED should be
* performed prior to calling this method.
*/
private Transport getTransport(CloseableHttpResponse response) throws UnsupportedOperationException, IOException, EntityProviderException, EdmException, UnexpectedHttpResponseException {
Header[] contentType = response.getHeaders(HttpHeaders.CONTENT_TYPE);
try(InputStream content = response.getEntity().getContent()) {
return new Transport(EntityProvider.readEntry(contentType[0].getValue(),
getEntityDataModel().getDefaultEntityContainer()
.getEntitySet(TransportRequestBuilder.getEntityKey()),
content,
EntityProviderReadProperties.init().build()).getProperties());
}
}
public void updateTransport(Transport transport) throws IOException, URISyntaxException, ODataException, UnexpectedHttpResponseException
{
Edm edm = getEntityDataModel();
try (CloseableHttpClient client = clientFactory.createClient()) {
HttpPut put = requestBuilder.updateTransport(transport.getTransportID());
put.setHeader("x-csrf-token", getCSRFToken());
EdmEntityContainer entityContainer = edm.getDefaultEntityContainer();
EdmEntitySet entitySet = entityContainer.getEntitySet(TransportRequestBuilder.getEntityKey());
URI rootUri = new URI(endpoint.toASCIIString() + "/");
EntityProviderWriteProperties properties = EntityProviderWriteProperties.serviceRoot(rootUri).build();
ODataResponse response = null;
try {
response = EntityProvider.writeEntry(put.getHeaders(HttpHeaders.CONTENT_TYPE)[0].getValue(), entitySet,transport.getValueMap(), properties);
put.setEntity(EntityBuilder.create().setStream(response.getEntityAsStream()).build());
try (CloseableHttpResponse httpResponse = client.execute(put)) {
hasStatusOrFail(httpResponse, SC_OK, HttpStatus.SC_NO_CONTENT);
}
} finally {
if(response != null) response.close();
}
}
}
public Transport createTransport(Map transport) throws IOException, URISyntaxException, ODataException, UnexpectedHttpResponseException
{
try (CloseableHttpClient client = clientFactory.createClient()) {
HttpPost post = requestBuilder.createTransport();
post.setHeader("x-csrf-token", getCSRFToken());
EdmEntityContainer entityContainer = getEntityDataModel().getDefaultEntityContainer();
EdmEntitySet entitySet = entityContainer.getEntitySet(TransportRequestBuilder.getEntityKey());
URI rootUri = new URI(endpoint.toASCIIString() + "/");
EntityProviderWriteProperties properties = EntityProviderWriteProperties.serviceRoot(rootUri).build();
ODataResponse marshaller = null;
try {
marshaller = EntityProvider.writeEntry(post.getHeaders(HttpHeaders.CONTENT_TYPE)[0].getValue(), entitySet, transport, properties);
post.setEntity(EntityBuilder.create().setStream(marshaller.getEntityAsStream()).build());
try(CloseableHttpResponse httpResponse = client.execute(post)) {
hasStatusOrFail(httpResponse, SC_CREATED);
return getTransport(httpResponse);
}
} finally {
if(marshaller != null) marshaller.close();
}
}
}
public void deleteTransport(String id) throws EntityProviderException, IOException, UnexpectedHttpResponseException
{
try (CloseableHttpClient client = clientFactory.createClient()) {
HttpDelete delete = requestBuilder.deleteTransport(id);
delete.setHeader("x-csrf-token", getCSRFToken());
try(CloseableHttpResponse response = client.execute(delete)) {
hasStatusOrFail(response, HttpStatus.SC_NO_CONTENT);
}
}
}
public String upload(String transportId, File content) throws IOException, CMODataClientException, UnexpectedHttpResponseException {
checkArgument(content.canRead(), format("Cannot read file '%s'.", content.getAbsolutePath()));
try(InputStream c = new FileInputStream(content)) {
return upload(transportId, c);
}
}
public String upload(String transportId, InputStream content) throws IOException, CMODataClientException, UnexpectedHttpResponseException {
checkArgument(! isNullOrEmpty(transportId), "TransportId is null or empty.");
checkArgument(content != null, "No content provided for upload.");
//
// Below we work with a non-repeatable entity. This is fine since we can be sure the
// request does not need to be repeated. Reason: the need for repeating a request inside
// the client (transparent for the caller) is an authentification scenario (first request
// without credentials, resulting in 401, repeated request with credentials). When uploading
// the content we are already authenticated since we have to fetch the csrf token beforehand
// in any case.
//
try (CloseableHttpClient client = clientFactory.createClient()) {
HttpPost request = requestBuilder.upload(transportId);
request.setEntity(new InputStreamEntity(content));
request.addHeader("x-csrf-token", getCSRFToken());
try (CloseableHttpResponse response = client.execute(request)) {
hasStatusOrFail(response, HttpStatus.SC_CREATED);
return response.getHeaders("location")[0].getValue();
}
}
}
public Transport releaseTransport(String transportId) throws IOException, UnexpectedHttpResponseException, EntityProviderException, EdmException {
try(CloseableHttpClient client = clientFactory.createClient()) {
HttpGet request = requestBuilder.exportTransport(transportId);
request.addHeader(HttpHeaders.ACCEPT, "application/xml");
request.addHeader("x-csrf-token", getCSRFToken());
try (CloseableHttpResponse response = client.execute(request)) {
hasStatusOrFail(response, HttpStatus.SC_OK);
return getTransport(response);
}
}
}
public Transport importTransport(String systemId, String transportId) throws IOException, UnexpectedHttpResponseException, EntityProviderException, EdmException {
try(CloseableHttpClient client = clientFactory.createClient()) {
HttpGet request = requestBuilder.importTransport(systemId, transportId);
request.addHeader(HttpHeaders.ACCEPT, "application/xml");
request.addHeader("x-csrf-token", getCSRFToken());
try(CloseableHttpResponse response = client.execute(request)) {
hasStatusOrFail(response, HttpStatus.SC_OK);
return getTransport(response);
}
}
}
private synchronized Edm getEntityDataModel() throws IOException, EntityProviderException, UnexpectedHttpResponseException {
if(dataModel == null) {
try (CloseableHttpClient edmClient = clientFactory.createClient()) {
try (CloseableHttpResponse response = edmClient.execute(new HttpGet(endpoint.toASCIIString() + "/" + "$metadata"))) {
hasStatusOrFail(response, SC_OK);
dataModel = EntityProvider.readMetadata(response.getEntity().getContent(), false);
}
}
}
return dataModel;
}
private synchronized String getCSRFToken() throws ClientProtocolException, IOException, UnexpectedHttpResponseException {
if(this.csrfToken == null) {
HttpGet httpGet = new HttpGet(this.endpoint);
httpGet.addHeader("X-CSRF-Token", "Fetch");
httpGet.addHeader(HttpHeaders.ACCEPT, "application/xml");
try(CloseableHttpClient client = clientFactory.createClient()) {
try(CloseableHttpResponse response = client.execute(httpGet)) {
hasStatusOrFail(response, 200);
Header[] csrfHeaders = response.getHeaders("x-csrf-token");
if(csrfHeaders.length != 1) throw new IllegalStateException("Multiple or no csrfHeaders received.");
this.csrfToken = csrfHeaders[0].getValue();
}
}
}
return this.csrfToken;
}
private static void hasStatusOrFail(HttpResponse response, int... expected) throws UnexpectedHttpResponseException {
if(! hasStatus(response, expected)) {
String reason;
try(Reader content = new InputStreamReader(response.getEntity().getContent(), Charsets.UTF_8)) {
// TODO hard coded char set is a best guess. Should be retrieved from response.
reason = CharStreams.toString(content);
} catch(IOException e) {
reason = String.format("Cannot provide a reason string. %s caught while retrieving the reason. Message of that excepetion was: %s", e.getClass().getName(), e.getMessage());
}
throw new UnexpectedHttpResponseException(format("Unexpected response code '%d'. Reason: %s", response.getStatusLine().getStatusCode(), reason),
response.getStatusLine());
}
}
private static boolean hasStatus(HttpResponse response, int... expected) {
return asList(expected).contains(response.getStatusLine().getStatusCode());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy