com.sap.cloud.sdk.s4hana.connectivity.ErpConfigContext Maven / Gradle / Ivy
/*
* Copyright (c) 2018 SAP SE or an SAP affiliate company. All rights reserved.
*/
package com.sap.cloud.sdk.s4hana.connectivity;
import java.util.Locale;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.slf4j.Logger;
import com.sap.cloud.sdk.cloudplatform.connectivity.DestinationAccessor;
import com.sap.cloud.sdk.cloudplatform.connectivity.DestinationType;
import com.sap.cloud.sdk.cloudplatform.connectivity.GenericDestination;
import com.sap.cloud.sdk.cloudplatform.connectivity.WithDestinationName;
import com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationAccessException;
import com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationNotFoundException;
import com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationTypeNotSupportedException;
import com.sap.cloud.sdk.cloudplatform.logging.CloudLoggerFactory;
import com.sap.cloud.sdk.cloudplatform.servlet.LocaleAccessor;
import com.sap.cloud.sdk.s4hana.serialization.SapClient;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
/**
* This class provides a context for transparently connecting to an SAP S/4HANA system via different protocols such as
* HTTP and RFC.
*
* An {@link ErpConfigContext} allows to define or obtain the {@link SapClient} and {@link Locale} that is used. In
* addition, it defines the names of the destinations that are relevant to connect to the ERP via different protocols
* which can require separate destinations.
*
* The following table shows which protocols are supported based on the existence and configuration of destinations:
*
*
* Destination type
* Supported protocols
*
*
* d1 = {@link #getDestinationName()}
* d2 = {@link #getDestinationNameRfc()}
*
*
* HTTP
* any
* HTTP via d1
*
*
* RFC
* any
* RFC via d1
*
*
* HTTP
* RFC
* HTTP via d1, RFC via d2
*
*
*
* The following table shows which protocols are required for different query types to certain editions of S/4HANA.
* Here, the usual setup for S/4HANA Cloud is to only use one single HTTP destination d1 (supporting all
* query types), while for S/4HANA on-premise systems, depending on the required query type, either a single RFC
* destination d1, or an HTTP destination d1 and an additional RFC destination d2 can
* be used to configure the connection to the respective system:
*
*
* SAP S/4HANA
* ODataQuery
* BapiQuery
* RfcQuery
*
*
* Cloud
* HTTP
* HTTP
* HTTP
*
*
* On-Premise
* HTTP
* RFC
* RFC
*
*
*/
@EqualsAndHashCode
@ToString
public class ErpConfigContext implements ConfigContext, WithDestinationName
{
private static final Logger logger = CloudLoggerFactory.getSanitizedLogger(ErpConfigContext.class);
/**
* The name suffix for an additional destination that is considered for RFC communication if
* {@link ErpConfigContext#getDestinationName()} refers to an HTTP destination. This suffix is appended to the given
* destination name unless the name of the additional RFC destination is explicitly defined. This is required to
* transparently support both HTTP and RFC communication via these two respective destinations (a destination can
* only support either HTTP or RFC).
*/
public static final String DESTINATION_NAME_SUFFIX_RFC = "_RFC";
/**
* The default name of the destination property holding the {@link SapClient} for {@link DestinationType#HTTP}.
*/
public static final String DEFAULT_SAP_CLIENT_PROPERTY = "sap-client";
/**
* The default name of the destination property holding the {@link Locale} for {@link DestinationType#HTTP}.
*/
public static final String DEFAULT_LOCALE_PROPERTY = "sap-language";
/**
* The default name of the destination property holding the {@link SapClient} for {@link DestinationType#RFC}.
*/
public static final String DEFAULT_SAP_CLIENT_PROPERTY_RFC = "jco.client.client";
/**
* The default name of the destination property holding the {@link Locale} for {@link DestinationType#RFC}.
*/
public static final String DEFAULT_LOCALE_PROPERTY_RFC = "jco.client.lang";
/**
* The name of the destination to be used for connecting to the ERP via HTTP or RFC. Defaults to
* {@link ErpDestination#getDefaultName()}.
*/
@Nonnull
@Getter
private final String destinationName;
/**
* The name of the additional destination that is considered for connecting to the ERP via the RFC protocol if
* {@link #getDestinationName()} refers to an HTTP destination. This is required to transparently support both HTTP
* and RFC communication via these two respective destinations (a destination can only support either HTTP or RFC).
* Defaults to {@link ErpDestination#getDefaultNameRfc()} = {@link ErpDestination#getDefaultName()} +
* {@link #DESTINATION_NAME_SUFFIX_RFC}.
*/
@Nonnull
@Getter
private final String destinationNameRfc;
/**
* The {@link SapClient} to be used for connecting to the ERP. If not provided upon construction, this value is read
* from the respective property of destination {@link ErpDestination#getDefaultName()}. Defaults to
* {@link SapClient#DEFAULT} otherwise.
*/
@Nonnull
@Getter
private final SapClient sapClient;
/**
* The {@link Locale} to be used for connecting to the ERP. If not provided upon construction, this value is read
* from the respective property of destination {@link ErpDestination#getDefaultName()}. Defaults to
* {@link Locale#getDefault()} otherwise.
*/
@Nonnull
@Getter
private final Locale locale;
/**
* Creates an ERP configuration context.
*
* @param destinationName
* The name of the ERP destination that can be used for HTTP or RFC communication. If this is
* {@code null}, the default name defined by {@link ErpDestination#getDefaultName()} is used.
*
* @param destinationNameRfc
* The name of the additional destination that is considered for connecting to the ERP via the RFC
* protocol if {@code destinationName} refers to an HTTP destination. This is required to transparently
* support both HTTP and RFC communication via these two respective destinations (a destination can only
* support either HTTP or RFC). If this is {@code null}, the default name is defined as:
* destinationNameRfc = destinationName +
* {@link #DESTINATION_NAME_SUFFIX_RFC}.
*
* @param sapClient
* The SAP client to be used. If this is {@code null}, the destination configuration of
* {@code destinationName} is used to read the property with the name specified by
* {@code sapClientProperty}. If this destination property does not exist, {@link SapClient#DEFAULT} is
* used.
*
* @param sapClientProperty
* The name of the destination property to be used for reading the SAP client. If this is {@code null},
* the following default property name is used:
*
* - {@link #DEFAULT_SAP_CLIENT_PROPERTY} for {@link DestinationType#HTTP} or
* - {@link #DEFAULT_SAP_CLIENT_PROPERTY_RFC} for {@link DestinationType#RFC}
*
*
* @param locale
* The locale to be used. If this is {@code null}, first, the destination configuration of
* {@code destinationName} is used to read the property with the name specified by {@code localeProperty}
* . If this fails, the locale defined by {@link LocaleAccessor#getCurrentLocale()} is used.
*
* @param localeProperty
* The name of the destination property to be used for reading the locale. If this is {@code null}, the
* following default property name is used:
*
* - {@link #DEFAULT_LOCALE_PROPERTY} for {@link DestinationType#HTTP} or
* - {@link #DEFAULT_LOCALE_PROPERTY_RFC} for {@link DestinationType#RFC}
*
*
* @throws DestinationAccessException
* If the configuration for the given destination cannot be found.
*/
public ErpConfigContext(
@Nullable final String destinationName,
@Nullable final String destinationNameRfc,
@Nullable final SapClient sapClient,
@Nullable final String sapClientProperty,
@Nullable final Locale locale,
@Nullable final String localeProperty )
throws DestinationAccessException
{
this.destinationName = destinationName == null ? ErpDestination.getDefaultName() : destinationName;
this.destinationNameRfc =
destinationNameRfc == null ? this.destinationName + DESTINATION_NAME_SUFFIX_RFC : destinationNameRfc;
GenericDestination destination = null;
if( sapClient == null ) {
destination = DestinationAccessor.getGenericDestination(this.destinationName);
final String propertyName;
if( sapClientProperty == null ) {
final DestinationType destinationType = destination.getDestinationType();
switch( destinationType ) {
case HTTP:
propertyName = DEFAULT_SAP_CLIENT_PROPERTY;
break;
case RFC:
propertyName = DEFAULT_SAP_CLIENT_PROPERTY_RFC;
break;
case MAIL:
case LDAP:
default:
throw new DestinationTypeNotSupportedException(this.destinationName, destinationType);
}
} else {
propertyName = sapClientProperty;
}
@Nullable
final String property = destination.getPropertiesByName().get(propertyName);
if( property == null ) {
if( logger.isInfoEnabled() ) {
logger.info(
"Unable to retrieve SAP client from destination property \""
+ propertyName
+ "\". Falling back to default SAP client. "
+ "To specify the SAP client, set the property on destination \""
+ this.destinationName
+ "\" or provide the SAP client as explicit argument.");
}
}
this.sapClient = property != null ? new SapClient(property) : SapClient.DEFAULT;
} else {
this.sapClient = sapClient;
}
if( locale == null ) {
final String propertyName;
if( localeProperty == null ) {
if( destination == null ) {
destination = DestinationAccessor.getGenericDestination(this.destinationName);
}
final DestinationType destinationType = destination.getDestinationType();
switch( destinationType ) {
case HTTP:
propertyName = DEFAULT_LOCALE_PROPERTY;
break;
case RFC:
propertyName = DEFAULT_LOCALE_PROPERTY_RFC;
break;
case MAIL:
case LDAP:
default:
throw new DestinationTypeNotSupportedException(this.destinationName, destinationType);
}
} else {
propertyName = localeProperty;
}
Locale fallback = null;
try {
if( destination == null ) {
destination = DestinationAccessor.getGenericDestination(this.destinationName);
}
final String localeString = destination.getPropertiesByName().get(propertyName);
if( localeString != null ) {
fallback = Locale.forLanguageTag(localeString.replace('_', '-'));
}
}
catch( final DestinationNotFoundException | DestinationAccessException e ) {
if( logger.isInfoEnabled() ) {
logger.info("Unable to retrieve locale from destination property \"" + propertyName + "\".", e);
}
}
if( fallback == null ) {
fallback = LocaleAccessor.getCurrentLocale();
if( logger.isInfoEnabled() ) {
logger.info(
"Falling back to locale \""
+ fallback
+ "\". To specify the locale, set the property on destination \""
+ this.destinationName
+ "\" or provide the locale as explicit argument.");
}
}
this.locale = fallback;
} else {
this.locale = locale;
}
}
/**
* Creates an ERP configuration context.
*
* @param destinationName
* The name of the ERP destination that can be used for HTTP or RFC communication. If this is
* {@code null}, the default name defined by {@link ErpDestination#getDefaultName()} is used.
*
* @param sapClient
* The SAP client to be used. If this is {@code null}, the destination configuration of
* {@code destinationName} is used to read the property with the name specified by
* {@code sapClientProperty}. If this destination property does not exist, {@link SapClient#DEFAULT} is
* used.
*
* @param sapClientProperty
* The name of the destination property to be used for reading the SAP client. If this is {@code null},
* the following default property name is used:
*
* - {@link #DEFAULT_SAP_CLIENT_PROPERTY} for {@link DestinationType#HTTP} or
* - {@link #DEFAULT_SAP_CLIENT_PROPERTY_RFC} for {@link DestinationType#RFC}
*
*
* @param locale
* The locale to be used. If this is {@code null}, first, the destination configuration of
* {@code destinationName} is used to read the property with the name specified by {@code localeProperty}
* . If this fails, the locale defined by {@link LocaleAccessor#getCurrentLocale()} is used.
*
* @param localeProperty
* The name of the destination property to be used for reading the locale. If this is {@code null}, the
* following default property name is used:
*
* - {@link #DEFAULT_LOCALE_PROPERTY} for {@link DestinationType#HTTP} or
* - {@link #DEFAULT_LOCALE_PROPERTY_RFC} for {@link DestinationType#RFC}
*
*
* @throws DestinationAccessException
* If the configuration for the given destination cannot be found.
*/
public ErpConfigContext(
@Nullable final String destinationName,
@Nullable final SapClient sapClient,
@Nullable final String sapClientProperty,
@Nullable final Locale locale,
@Nullable final String localeProperty )
throws DestinationAccessException
{
this(destinationName, null, sapClient, sapClientProperty, locale, localeProperty);
}
/**
* Creates an ERP configuration context.
*
* @param destinationName
* The name of the ERP destination that can be used for HTTP or RFC communication. If this is
* {@code null}, the default name defined by {@link ErpDestination#getDefaultName()} is used.
*
* @param sapClient
* The SAP client to be used. If this is {@code null}, the destination configuration of
* {@code destinationName} is used to read the property with the following default property name:
*
* - {@link #DEFAULT_SAP_CLIENT_PROPERTY} for {@link DestinationType#HTTP} or
* - {@link #DEFAULT_SAP_CLIENT_PROPERTY_RFC} for {@link DestinationType#RFC}
*
* If this destination property does not exist, {@link SapClient#DEFAULT} is used.
*
* @param locale
* The locale to be used. If this is {@code null}, first, the destination configuration of
* {@code destinationName} is used to read the property with the following default property name:
*
* - {@link #DEFAULT_LOCALE_PROPERTY} for {@link DestinationType#HTTP} or
* - {@link #DEFAULT_LOCALE_PROPERTY_RFC} for {@link DestinationType#RFC}
*
* If this fails, the locale defined by {@link LocaleAccessor#getCurrentLocale()} is used.
*
* @throws DestinationAccessException
* If the configuration for the given destination cannot be found.
*/
public ErpConfigContext(
@Nullable final String destinationName,
@Nullable final SapClient sapClient,
@Nullable final Locale locale )
throws DestinationAccessException
{
this(destinationName, sapClient, null, locale, null);
}
/**
* Creates an ERP configuration context using the current {@link Locale}.
*
* @param destinationName
* The name of the ERP destination that can be used for HTTP or RFC communication. If this is
* {@code null}, the default name defined by {@link ErpDestination#getDefaultName()} is used.
*
* @param sapClient
* The SAP client to be used. If this is {@code null}, the destination configuration of
* {@code destinationName} is used to read the property with the following default property name:
*
* - {@link #DEFAULT_SAP_CLIENT_PROPERTY} for {@link DestinationType#HTTP} or
* - {@link #DEFAULT_SAP_CLIENT_PROPERTY_RFC} for {@link DestinationType#RFC}
*
* If this destination property does not exist, {@link SapClient#DEFAULT} is used.
*/
public ErpConfigContext( @Nullable final String destinationName, @Nullable final SapClient sapClient )
{
this(destinationName, sapClient, null, null, null);
}
/**
* Creates an ERP configuration context using the default property of this destination
*
* - {@link #DEFAULT_LOCALE_PROPERTY} for {@link DestinationType#HTTP} or
* - {@link #DEFAULT_LOCALE_PROPERTY_RFC} for {@link DestinationType#RFC}
*
* or {@link SapClient#DEFAULT} if this property does not exist, as well as the current {@link Locale}.
*
* @param destinationName
* The name of the ERP destination that can be used for HTTP or RFC communication. If this is
* {@code null}, the default name defined by {@link ErpDestination#getDefaultName()} is used.
*/
public ErpConfigContext( @Nullable final String destinationName )
{
this(destinationName, null, null, null, null);
}
/**
* Creates an ERP configuration context using the default ERP destination name
* {@link ErpDestination#getDefaultName()} and the current {@link Locale}.
*
* @param sapClient
* The SAP client to be used. If this is {@code null}, the destination configuration of
* {@code destinationName} is used to read the property with name {@link #DEFAULT_SAP_CLIENT_PROPERTY}.
* If this destination property does not exist, {@link SapClient#DEFAULT} is used.
*/
public ErpConfigContext( @Nullable final SapClient sapClient )
{
this(null, sapClient, null, null, null);
}
/**
* Creates an ERP configuration context using the default ERP destination name
* {@link ErpDestination#getDefaultName()} and the default SAP client property of this destination:
*
* - {@link #DEFAULT_SAP_CLIENT_PROPERTY} for {@link DestinationType#HTTP} or
* - {@link #DEFAULT_SAP_CLIENT_PROPERTY_RFC} for {@link DestinationType#RFC}
*
* If this destination property does not exist, {@link SapClient#DEFAULT} is used.
*
* @param locale
* The locale to be used. If this is {@code null}, first, the destination configuration of
* {@code destinationName} is used to read the default locale property:
*
* - {@link #DEFAULT_LOCALE_PROPERTY} for {@link DestinationType#HTTP} or
* - {@link #DEFAULT_LOCALE_PROPERTY_RFC} for {@link DestinationType#RFC}
*
* If this fails, the locale defined by {@link LocaleAccessor#getCurrentLocale()} is used.
*/
public ErpConfigContext( @Nullable final Locale locale )
{
this(null, null, null, locale, null);
}
/**
* Creates the default ERP configuration context using
*
* - default ERP destination name {@link ErpDestination#getDefaultName()},
*
- default SAP client property of this destination
*
* - {@link #DEFAULT_SAP_CLIENT_PROPERTY} for {@link DestinationType#HTTP} or
* - {@link #DEFAULT_SAP_CLIENT_PROPERTY_RFC} for {@link DestinationType#RFC}
*
* if available - otherwise {@link SapClient#DEFAULT}.
* - default locale property of this destination
*
* - {@link #DEFAULT_LOCALE_PROPERTY} for {@link DestinationType#HTTP} or
* - {@link #DEFAULT_LOCALE_PROPERTY_RFC} for {@link DestinationType#RFC}
*
* if available - otherwise locale {@link LocaleAccessor#getCurrentLocale()}.
*
*/
public ErpConfigContext()
{
this(null, null, null, null, null);
}
}