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

com.sap.cloud.yaas.servicesdk.springboot.clients.jersey.JerseyClientAutoConfiguration Maven / Gradle / Ivy

There is a newer version: 4.17.1
Show newest version
/*
 * © 2017 SAP SE or an SAP affiliate company.
 * All rights reserved.
 * Please see http://www.sap.com/corporate-en/legal/copyright/index.epx for additional trademark information and
 * notices.
 */
package com.sap.cloud.yaas.servicesdk.springboot.clients.jersey;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.ClientResponseFilter;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.WriterInterceptor;

import com.sap.cloud.yaas.servicesdk.authorization.AccessTokenProvider;
import com.sap.cloud.yaas.servicesdk.authorization.cache.SimpleCachingProviderWrapper;
import com.sap.cloud.yaas.servicesdk.authorization.integration.jaxrs.OAuth2Filter;
import com.sap.cloud.yaas.servicesdk.authorization.protocol.ClientCredentialsGrantProvider;
import com.sap.cloud.yaas.servicesdk.jerseysupport.logging.RequestResponseLoggingFilter;

import org.glassfish.jersey.server.ResourceConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import java.util.Map;


/**
 * Auto Configuration class, registering Jersey-specific features related to the client side.
 *
 * Currently it contains Jersey-based {@link RequestResponseLoggingFilter} that logs the request and response (with
 * masking Authorization headers), and an instance of {@link AccessTokenProvider} for OAuth2 authorization using the
 * Yaas Service SDK authorization library.
 */
@Configuration
@ConditionalOnClass({Client.class, ClientBuilder.class, ResourceConfig.class})
@AutoConfigureAfter(JacksonAutoConfiguration.class)
@EnableConfigurationProperties({JerseyClientProperties.class, OAuth2ClientProperties.class})
@PropertySource("classpath:/com/sap/cloud/yaas/servicesdk/springboot/clients/jersey/jersey-client-default.properties")
public class JerseyClientAutoConfiguration implements ApplicationContextAware
{
	private ApplicationContext applicationContext;

	@Override
	public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException
	{
		this.applicationContext = applicationContext;
	}

	/**
	 * Registers the {@link StringToAuthorizationScopeConverter} bean.
	 *
	 * @return the {@link StringToAuthorizationScopeConverter} instance
	 */
	@Bean
	@ConfigurationPropertiesBinding
	public StringToAuthorizationScopeConverter stringToAuthorizationScopeConverter()
	{
		return new StringToAuthorizationScopeConverter();
	}

	/**
	 * Registers new JAX-WS {@link Feature} that registers the {@link RequestResponseLoggingFilter} to the
	 * JAX-RS {@link FeatureContext}.
	 *
	 * @param jerseyProps the properties that contain the logger and the maximum entity size
	 * @return the new {@link Feature}
	 */
	@Bean
	@ConditionalOnClass({RequestResponseLoggingFilter.class, LoggerFactory.class, Logger.class})
	@ConditionalOnProperty(prefix = "yaas.clients.jersey", name = "request-response-logging.logger-name")
	public Feature serviceRequestResponseLoggingFilter(final JerseyClientProperties jerseyProps)
	{
		return new Feature()
		{
			@Override
			public boolean configure(final FeatureContext context)
			{
				final Logger log = LoggerFactory.getLogger(jerseyProps.getRequestResponseLogging().getLoggerName());
				final int maxEntitySize = jerseyProps.getRequestResponseLogging().getMaxEntitySize();

				final RequestResponseLoggingFilter filter = maxEntitySize > 0
						? new RequestResponseLoggingFilter(log, maxEntitySize)
						: new RequestResponseLoggingFilter(log);

				context.register(
						filter,
						ClientRequestFilter.class,
						ClientResponseFilter.class,
						WriterInterceptor.class);

				return true;
			}
		};
	}

	/**
	 * If not yet registered, registers a default caching instance of {@link AccessTokenProvider}. That will take
	 * care of requesting Oauth2 token.
	 *
	 * @param jaxRsProps Jersey specific properties that should be applied to the Jersey client that is calling the
	 *           authorization server
	 * @param oAuth2Props properties containing client authorization information
	 * @return an instance of {@link AccessTokenProvider} that provides access token caching
	 */
	@Bean
	@ConditionalOnMissingBean(AccessTokenProvider.class)
	@ConditionalOnClass({ClientCredentialsGrantProvider.class, SimpleCachingProviderWrapper.class})
	public AccessTokenProvider oAuth2ClientCredentialsGrantProviderWithCaching(
			final JerseyClientProperties jaxRsProps,
			final OAuth2ClientProperties oAuth2Props)
	{
		return new SimpleCachingProviderWrapper(createClientCredentialsGrantProvider(jaxRsProps, oAuth2Props));
	}

	private ClientCredentialsGrantProvider createClientCredentialsGrantProvider(
			final JerseyClientProperties jaxRsProps,
			final OAuth2ClientProperties oAuth2Props)
	{
		final Client client = createJerseyClientBuilder(jaxRsProps).build();

		final ClientCredentialsGrantProvider accessTokenProvider = new ClientCredentialsGrantProvider(client);
		accessTokenProvider.setTokenEndpointUri(oAuth2Props.getTokenEndpointUri());
		accessTokenProvider.setClientId(oAuth2Props.getClientId());
		accessTokenProvider.setClientSecret(oAuth2Props.getClientSecret());
		accessTokenProvider.setScopeValidationEnabled(oAuth2Props.getScopeValidationEnabled());
		return accessTokenProvider;
	}

	/**
	 * Registers an instance of {@link OAuth2Filter} that can be used for automatically adding Authorization header
	 * to requests as well as requesting and caching the OAuth2 token.
	 *
	 * @param oAuth2Props the configuration related to the authorization token retrieval
	 * @param accessTokenProvider an instance of {@link AccessTokenProvider}, that will make the actual calls to the
	 *           authorization server
	 * @return an instance of {@link OAuth2Filter}
	 */
	@Bean
	@ConditionalOnClass(OAuth2Filter.class)
	@ConditionalOnBean(AccessTokenProvider.class)
	public OAuth2Filter jaxRsOAuth2Filter(final OAuth2ClientProperties oAuth2Props, final AccessTokenProvider accessTokenProvider)
	{
		return new OAuth2Filter(
				accessTokenProvider,
				oAuth2Props.getDefaultScope(),
				oAuth2Props.getTokenRequestRetries());
	}

	/**
	 * Creates an instance of Jersey HTTP {@link Client}, with given {@link OAuth2Filter} that will take care of
	 * automatically authenticating the requests.
	 *
	 * @param properties the Jersey configuration for the client
	 * @param oAuth2Filter the authorization related configuration properties
	 * @return an instance of Jersey {@link Client}
	 */
	@Bean
	@ConditionalOnMissingBean(Client.class)
	@ConditionalOnBean(OAuth2Filter.class)
	public Client jerseyClient(final JerseyClientProperties properties, final OAuth2Filter oAuth2Filter)
	{
		final ClientBuilder clientBuilder = createJerseyClientBuilder(properties);
		clientBuilder.register(oAuth2Filter);
		return clientBuilder.build();
	}

	/**
	 * Creates an instance of Jersey HTTP {@link Client}, with given {@link OAuth2Filter} that will take care of
	 * automatically authenticating the requests.
	 *
	 * Will be called only if {@link OAuth2Filter} has not been instantiated (for example because it's not on the
	 * classpath).
	 *
	 * @param properties the Jersey configuration for the client
	 * @return an instance of Jersey {@link Client}
	 */
	@Bean
	@ConditionalOnMissingBean({Client.class, OAuth2Filter.class})
	public Client jerseyClient(final JerseyClientProperties properties)
	{
		return createJerseyClientBuilder(properties).build();
	}

	/**
	 * Internal factory method to create a Jersey {@link ClientBuilder}.
	 * @param properties the properties to be used for configuration
	 * @return instance of {@link ClientBuilder}
	 */
	ClientBuilder createJerseyClientBuilder(final JerseyClientProperties properties)
	{
		final ClientBuilder clientBuilder = ClientBuilder.newBuilder();

		for (final Map.Entry configProperty : properties.getConfig().entrySet())
		{
			clientBuilder.property(
					"jersey.config." + configProperty.getKey(),
					configProperty.getValue());
		}

		final Map features = applicationContext.getBeansOfType(Feature.class);
		for (final Feature feature : features.values())
		{
			clientBuilder.register(feature);
		}

		return clientBuilder;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy