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

com.sap.cloud.sdk.odatav2.connectivity.internal.ODataConnectivityUtil Maven / Gradle / Ivy

There is a newer version: 1.40.11
Show newest version
/*******************************************************************************
 * (c) 201X SAP SE or an SAP affiliate company. All rights reserved.
 ******************************************************************************/
package com.sap.cloud.sdk.odatav2.connectivity.internal;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.olingo.odata2.api.edm.Edm;
import org.apache.olingo.odata2.api.edm.EdmEntityType;
import org.apache.olingo.odata2.api.edm.EdmException;
import org.apache.olingo.odata2.api.edm.EdmLiteralKind;
import org.apache.olingo.odata2.api.edm.EdmMultiplicity;
import org.apache.olingo.odata2.api.edm.EdmNavigationProperty;
import org.apache.olingo.odata2.api.edm.EdmProperty;
import org.apache.olingo.odata2.api.edm.EdmSimpleType;
import org.apache.olingo.odata2.api.edm.EdmSimpleTypeException;
import org.apache.olingo.odata2.api.ep.entry.ODataEntry;
import org.apache.olingo.odata2.api.ep.feed.ODataFeed;
import org.apache.olingo.odata2.client.api.ep.Entity;
import org.apache.olingo.odata2.client.api.ep.EntityCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sap.cloud.sdk.cloudplatform.cache.CacheKey;
import com.sap.cloud.sdk.odatav2.connectivity.ErrorResultHandler;
import com.sap.cloud.sdk.odatav2.connectivity.ODataException;
import com.sap.cloud.sdk.odatav2.connectivity.ODataExceptionType;
import com.sap.cloud.sdk.odatav2.connectivity.cache.metadata.GuavaMetadataCache;
import com.sap.cloud.sdk.odatav2.connectivity.cache.metadata.MetadataCache;

public class ODataConnectivityUtil {

	private static Logger logger = LoggerFactory.getLogger(ODataConnectivityUtil.class);
	public static final String CSRF_HEADER = "x-csrf-token";
	private static MetadataCache metadataCache = new GuavaMetadataCache();
	public static final String SEPARATOR_PATH = "/";
	static final int HTTP_CODE_ERROR_MIN = 400;
	static final int HTTP_CODE_ERROR_MAX = 599;
	private static final int HTTP_CODE_METHOD_NOT_ALLOWED = 405;

	public static EdmWithCSRF readMetadataWithCSRF(String serviceUri, HttpClient httpClient,
			Map headers, ErrorResultHandler errorHandler, Boolean cacheMetadata,
			URL metadataFilePath, CacheKey cacheKey, Boolean isCacheRefresh)
			throws ClientProtocolException, IOException, ODataException {
		EdmWithCSRF edmWithCSRF = null;
		HttpResponse httpResponse = null;
		HttpHead httpGet = new HttpHead(serviceUri);
		try {
			Edm edm = metadataCache.getEdm(serviceUri + "/$metadata", httpClient, headers, errorHandler, cacheMetadata,
					metadataFilePath, cacheKey, isCacheRefresh);
			httpGet.setHeader(CSRF_HEADER, "Fetch");
			if (headers != null) {
				for (Entry e : headers.entrySet()) {
					httpGet.addHeader(e.getKey(), e.getValue());
				}
			}
			httpResponse = httpClient.execute(httpGet);
			ODataConnectivityUtil.checkHttpResponseForCSRF(httpResponse, errorHandler);

			// TODO - Code for null check.
			Header csrfHeader = httpResponse.getFirstHeader(CSRF_HEADER);
			String csrfToken = csrfHeader == null ? null : csrfHeader.getValue();
			edmWithCSRF = new EdmWithCSRF(edm, csrfToken);
			return edmWithCSRF;
		} catch (Exception e) {
			logger.error("Error occurred during create operation of Type : " + e);
			throw e;
		} finally {
			HttpClientUtils.closeQuietly(httpResponse);
			ODataConnectivityUtil.closeQuietly(httpResponse, httpGet);
		}
	}

	public static EdmWithCSRF readMetadataWithCSRF(String serviceUri, HttpClient httpClient,
			Map headers, ErrorResultHandler errorHandler, Boolean cacheMetadata,
			URL metadataFilePath) throws ClientProtocolException, IOException, ODataException {
		return readMetadataWithCSRF(serviceUri, httpClient, headers, errorHandler, cacheMetadata, metadataFilePath,
				null, false);
	}

	public static String convertKeyValuesToString(Map keys, EdmEntityType entityType)
			throws ODataException, EdmException {

		String keyPredicateString = "";
		for (Entry e : keys.entrySet()) {
			if (!keyPredicateString.isEmpty())
				keyPredicateString += ',';
			String key = e.getKey();
			Object value = e.getValue();
			EdmProperty prop = (EdmProperty) entityType.getProperty(key);
			if (prop == null)
				throw new ODataException(ODataExceptionType.KEY_NOT_PRESENT, "No such key property. " + key, null);
			EdmSimpleType type = (EdmSimpleType) (prop.getType());
			String convertedValue;
			try {
				if("String".equals(type.getName()) && value != null){
					value = UrlEncoder.encode(value.toString());
				}
				convertedValue = type.toUriLiteral(type.valueToString(value, EdmLiteralKind.DEFAULT, prop.getFacets()));
			} catch (EdmSimpleTypeException e1) {
				throw new ODataException(ODataExceptionType.BAD_KEY_VALUE, e1.getMessage(), e1);
			}
			keyPredicateString += key + '=' + convertedValue;
		}
		return keyPredicateString;
	}

	public static Entity addPropertiesToEntity(Map properties, EdmEntityType entityType)
			throws EdmException, ODataException {
		Entity entity = new Entity();
		List navigationProperties = entityType.getNavigationPropertyNames();
		List propertiesInEntity = entityType.getPropertyNames();

		for (Iterator keySetIterator = properties.keySet().iterator(); keySetIterator.hasNext();) {
			String propertyName = keySetIterator.next().toString();
			// Handled Deep Insert for Multilevel
			if (navigationProperties.contains(propertyName)) {
				EdmNavigationProperty navigationProperty = (EdmNavigationProperty) entityType.getProperty(propertyName);
				EdmEntityType targetType = navigationProperty.getRelationship().getEnd(navigationProperty.getToRole())
						.getEntityType();
				if (navigationProperty.getMultiplicity().equals(EdmMultiplicity.MANY)) {
					List> childEntities = null;
					if (properties.get(propertyName) instanceof List)
						childEntities = (List>) properties.get(propertyName);
					else if (properties.get(propertyName) instanceof Map) {
						childEntities = new ArrayList>();
						childEntities.add((Map) properties.get(propertyName));
					}
					else if (properties.get(propertyName) instanceof ODataFeed){
						ODataFeed feed = (ODataFeed) properties.get(propertyName);
						childEntities = new ArrayList>();
						for (ODataEntry feedEntry : feed.getEntries()) {				
							childEntities.add(mapEntryToMap(feedEntry));					
						}		
					}

					EntityCollection deepInsertChildren = new EntityCollection();
					for (Map entry : childEntities) {
						deepInsertChildren.addEntity(addPropertiesToEntity(entry, targetType));
					}
					entity.addNavigation(propertyName, deepInsertChildren);
				} else {
					Map childEntity = null;
					if (properties.get(propertyName) instanceof List)
						childEntity = ((List>) properties.get(propertyName)).get(0);
					else if (properties.get(propertyName) instanceof Map) {
						childEntity = (Map) properties.get(propertyName);
					}
					else if (properties.get(propertyName) instanceof ODataEntry){
						ODataEntry entryFeed = (ODataEntry) properties.get(propertyName);								
							childEntity = (Map) mapEntryToMap(entryFeed);					
							
					}
					
					entity.addNavigation(propertyName, addPropertiesToEntity(childEntity, targetType));
				}
			} else if (!propertiesInEntity.contains(propertyName)) {
				throw new ODataException(ODataExceptionType.INVALID_PROPERTY_NAME,
						"No property with name " + propertyName + " present in the entity", null);
			} else if (properties.get(propertyName) instanceof Map) { // Complex
																		// Type
				entity.addProperty(propertyName,
						addPropertiesToMap((Map) properties.get(propertyName)));
			} else
				entity.addProperty(propertyName, properties.get(propertyName));
		}
		return entity;
	}

	private static Map mapEntryToMap(ODataEntry entry) {
		Map finalResult = new HashMap<>();
		for (Entry property : entry.getProperties().entrySet()) {
			if (property.getValue() instanceof ODataFeed) {
				ODataFeed feedExpand = (ODataFeed) property.getValue();
				List> allChildren = new ArrayList>();
				for (ODataEntry feedEntry : feedExpand.getEntries()) {
					allChildren.add(mapEntryToMap(feedEntry));
				}
				finalResult.put(property.getKey(), allChildren);
			} else if (property.getValue() instanceof ODataEntry) {
				ODataEntry entryFeed = (ODataEntry) property.getValue();
				finalResult.put(property.getKey(), mapEntryToMap(entryFeed));
			} else {
				finalResult.put(property.getKey(), property.getValue());
			}
		}
		return finalResult;
	}
	
	@SuppressWarnings("unchecked")
	private static Map addPropertiesToMap(Map complexProperties) {
		Map returnedComplexType = new HashMap<>();
		for (Entry entry : complexProperties.entrySet()) {
			if (entry.getValue() instanceof Map) {
				returnedComplexType.put(entry.getKey(), addPropertiesToMap((Map) entry.getValue()));
			} else {// it is a simple property
				returnedComplexType.put(entry.getKey(), entry.getValue());
			}
		}
		return returnedComplexType;
	}

	/**
	 * Checks the HttpStatus code and returns an ODataException object if it falls
	 * between 400 and 599.
	 * 
	 * @param response     HttpResponse object
	 * @param errorHandler
	 * @throws ODataException
	 */
	public static void checkHttpStatus(final HttpResponse response, ErrorResultHandler errorHandler)
			throws ODataException {
		if (errorHandler == null)
			errorHandler = (ErrorResultHandler) new ODataExceptionInternalResultHandler();
		final int httpStatusCode = response.getStatusLine().getStatusCode();		
		if (HTTP_CODE_ERROR_MIN <= httpStatusCode && httpStatusCode <= HTTP_CODE_ERROR_MAX) {
			String fullResponse = "";
			try {
				if (response.getEntity() != null) {
					fullResponse = IOUtils.toString(response.getEntity().getContent(), "UTF-8");
				}
				if (logger.isDebugEnabled()) {
					logger.debug("OData service response: " + fullResponse + ".");
				}
			} catch (final IOException e) {
				fullResponse = "Failed to load error page from OData service: " + e.getMessage() + ".";
			}finally{
			HttpClientUtils.closeQuietly(response);
			}
			if (errorHandler instanceof ODataExceptionInternalResultHandler) {
				throw errorHandler.createError(fullResponse, (Object) response, httpStatusCode);
			} else {
				throw errorHandler.createError(fullResponse, null, httpStatusCode);
			}
		}
	}

	/**
	 * Check if the Response is an invalid response but the CSRF is returned by the
	 * server In case some servers return 405 and CSRF Token is returned, we
	 * consider such cases as success
	 * 
	 * @param response
	 * @param errorHandler
	 * @throws ODataException
	 */
	private static void checkHttpResponseForCSRF(final HttpResponse response, ErrorResultHandler errorHandler)
			throws ODataException {
		if (errorHandler == null)
			errorHandler = (ErrorResultHandler) new ODataExceptionInternalResultHandler();
		final int httpStatusCode = response.getStatusLine().getStatusCode();
		final Header csrfHeader = response.getFirstHeader(CSRF_HEADER);
		if (httpStatusCode == HTTP_CODE_METHOD_NOT_ALLOWED && csrfHeader != null) {
			return;
		}
		checkHttpStatus(response, errorHandler);

	}
	
	public static void closeQuietly(HttpResponse httpResponse, HttpRequestBase request) {
		HttpClientUtils.closeQuietly(httpResponse);
		request.releaseConnection();
		
	}

	@Deprecated
	public static void safeCloseHttpResponse(HttpResponse httpResponse) {
		if (httpResponse != null) {
			HttpEntity entity = httpResponse.getEntity();
			try {
				if (entity != null && entity.isStreaming()) {
					InputStream instream = entity.getContent();
					if (instream != null) {
						instream.close();
					}
				}
				// EntityUtils.consume(entity);
			} catch (final IOException ex) {
				logger.info("IOException while closing HttpResponse entity.getContent()");
			} catch (final IllegalStateException e) {
				logger.info("IllegalStateException while closing HttpResponse entity.getContent()");
			}
		}
	}

	public static InputStream getObjectStream(Object entity) {

		InputStream inputStream = null;

		if (entity instanceof String) {
			inputStream = new ByteArrayInputStream(((String) entity).getBytes(StandardCharsets.UTF_8));
		} else {

			inputStream = (InputStream) entity;

		}
		return inputStream;
	}

	public static String withSeparator(final String separator, final String... parts) {
		final StringBuilder result = new StringBuilder();
		for (String s : parts) {
			if (!StringUtils.isEmpty(s)) {
				s = StringUtils.prependIfMissing(s, separator);
				s = StringUtils.removeEnd(s, separator);
				result.append(s);
			}
		}
		return result.toString();
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy