All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sap.cmclient.http.CMODataAbapClient Maven / Gradle / Ivy

There is a newer version: 2.0.1
Show newest version
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