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

com.peterphi.std.guice.restclient.resteasy.impl.ResteasyProxyClientFactoryImpl Maven / Gradle / Ivy

There is a newer version: 9.2.0
Show newest version
package com.peterphi.std.guice.restclient.resteasy.impl;

import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import com.peterphi.std.annotation.Doc;
import com.peterphi.std.annotation.ServiceName;
import com.peterphi.std.guice.apploader.GuiceConstants;
import com.peterphi.std.guice.common.serviceprops.composite.GuiceConfig;
import com.peterphi.std.guice.restclient.JAXRSProxyClientFactory;
import com.peterphi.std.guice.restclient.annotations.FastFailServiceClient;
import org.apache.commons.lang.StringUtils;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;

import javax.ws.rs.client.WebTarget;
import java.net.URI;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.Supplier;

@Singleton
public class ResteasyProxyClientFactoryImpl implements JAXRSProxyClientFactory
{
	@Inject
	ResteasyClientFactoryImpl clientFactory;

	@Inject
	GuiceConfig config;

	@Inject(optional = true)
	@Named("jaxrs.cookie-store")
	@Doc("Whether default JAX-RS clients should maintain a cookie store (default false)")
	public boolean defaultStoreCookies = false;


	public ResteasyProxyClientFactoryImpl()
	{
	}


	public ResteasyProxyClientFactoryImpl(ResteasyClientFactoryImpl clientFactory, GuiceConfig config)
	{
		this.clientFactory = clientFactory;
		this.config = config;
	}


	public static String getConfiguredBoundServiceName(final GuiceConfig config, Class iface, String... names)
	{
		if (names == null || names.length == 0)
		{
			if (iface == null)
				throw new IllegalArgumentException("If not specifying service names you must provide a service interface");
			else
				names = getServiceNames(iface);
		}

		for (String name : names)
		{
			if (name == null)
				continue;

			if (config.containsKey("service." + name + ".endpoint"))
				return name;
		}

		return null;
	}


	@Override
	public ResteasyWebTarget getWebTarget(final String... names)
	{
		return getWebTarget(false, names);
	}


	@Override
	public ResteasyWebTarget getWebTarget(final Class iface, final String... names)
	{
		final boolean fastFail = iface.isAnnotationPresent(FastFailServiceClient.class);

		return getWebTarget(fastFail, names);
	}


	private ResteasyWebTarget getWebTarget(final boolean defaultFastFail, final String... names)
	{
		final String name = getConfiguredBoundServiceName(config, null, names);

		if (name == null)
			throw new IllegalArgumentException("Cannot find service in configuration by any of these names: " +
			                                   Arrays.asList(names));

		final String endpoint = config.get("service." + name + ".endpoint", null);
		final URI uri = URI.create(endpoint);

		// TODO allow other per-service configuration?
		final String username = config.get("service." + name + ".username", getUsername(uri));
		final String password = config.get("service." + name + ".password", getPassword(uri));
		final boolean fastFail = config.getBoolean("service." + name + ".fast-fail", defaultFastFail);
		final String authType = config.get("service." + name + ".auth-type", GuiceConstants.JAXRS_CLIENT_AUTH_DEFAULT);
		final boolean storeCookies = config.getBoolean("service." + name + ".cookie-store", defaultStoreCookies);
		final String bearerToken = config.get("service." + name + ".bearer", null);
		final Supplier bearerSupplier = (bearerToken != null) ? () -> bearerToken : null; // Supply fixed token

		final boolean preemptiveAuth;
		if (bearerToken != null)
			preemptiveAuth = true; // force pre-emptive auth
		else if (authType.equalsIgnoreCase(GuiceConstants.JAXRS_CLIENT_AUTH_DEFAULT))
			preemptiveAuth = false;
		else if (authType.equalsIgnoreCase(GuiceConstants.JAXRS_CLIENT_AUTH_PREEMPT))
			preemptiveAuth = true;
		else
			throw new IllegalArgumentException("Illegal auth-type for service " + name + ": " + authType);

		return createWebTarget(uri, fastFail, username, password, bearerSupplier, storeCookies, preemptiveAuth);
	}


	@Override
	public  T getClient(final Class iface, final String... names)
	{
		return getWebTarget(iface, names).proxy(iface);
	}


	@Override
	public  T getClient(final Class iface)
	{
		return getClient(iface, getServiceNames(iface));
	}


	@Override
	public  T getClient(final Class iface, final WebTarget target)
	{
		final ResteasyWebTarget resteasyTarget = (ResteasyWebTarget) target;

		return resteasyTarget.proxy(iface);
	}


	/**
	 * Computes the default set of names for a service based on an interface class. The names produced are an ordered list:
	 * 
    *
  • The fully qualified class name
  • *
  • If present, the {@link com.peterphi.std.annotation.ServiceName} annotation on the class (OR if not specified on the * class, the {@link com.peterphi.std.annotation.ServiceName} specified on the package)
  • *
  • The simple name of the class (the class name without the package prefix)
  • *
* * @param iface * a JAX-RS service interface * * @return An array containing one or more names that could be used for the class; may contain nulls (which should be ignored) */ private static String[] getServiceNames(Class iface) { Objects.requireNonNull(iface, "Missing param: iface!"); return new String[]{iface.getName(), getServiceName(iface), iface.getSimpleName()}; } private static String getServiceName(Class iface) { Objects.requireNonNull(iface, "Missing param: iface!"); if (iface.isAnnotationPresent(ServiceName.class)) { return iface.getAnnotation(ServiceName.class).value(); } else if (iface.getPackage().isAnnotationPresent(ServiceName.class)) { return iface.getPackage().getAnnotation(ServiceName.class).value(); } else { return null; // No special name } } @Override public ResteasyWebTarget createWebTarget(final URI endpoint, String username, String password) { return createWebTarget(endpoint, username, password, null, defaultStoreCookies, true); } public ResteasyWebTarget createWebTarget(final URI endpoint, String username, String password, Supplier bearerToken, boolean storeCookies, boolean preemptiveAuth) { return createWebTarget(endpoint, false, username, password, bearerToken, storeCookies, preemptiveAuth); } ResteasyWebTarget createWebTarget(final URI endpoint, boolean fastFail, String username, String password, final Supplier bearerToken, final boolean storeCookies, boolean preemptiveAuth) { final AuthScope scope; final Credentials credentials; if (bearerToken != null) { scope = new AuthScope(endpoint.getHost(), AuthScope.ANY_PORT, AuthScope.ANY_REALM, "Bearer"); credentials = new BearerCredentials(bearerToken); } else if (username != null || password != null || StringUtils.isNotEmpty(endpoint.getAuthority())) { scope = new AuthScope(endpoint.getHost(), AuthScope.ANY_PORT); if (username != null || password != null) credentials = new UsernamePasswordCredentials(username, password); else credentials = new UsernamePasswordCredentials(endpoint.getAuthority()); } else { scope = null; credentials = null; } return clientFactory.getOrCreateClient(fastFail, scope, credentials, (credentials != null) && preemptiveAuth, storeCookies, null).target(endpoint); } @Override public T createClient(final Class iface, final String endpoint) { return createClient(iface, URI.create(endpoint)); } @Override public T createClient(Class iface, URI endpoint) { return createClient(iface, endpoint, false); } @Override public T createClient(final Class iface, final URI endpoint, final boolean preemptiveAuth) { return createClientWithPasswordAuth(iface, endpoint, getUsername(endpoint), getPassword(endpoint), preemptiveAuth); } @Override @Deprecated public T createClientWithPasswordAuth(Class iface, URI endpoint, String username, String password) { return createClientWithPasswordAuth(iface, endpoint, username, password, false); } @Override public T createClientWithBearerAuth(final Class iface, final URI endpoint, final Supplier token) { final boolean fastFail = iface.isAnnotationPresent(FastFailServiceClient.class); return createWebTarget(endpoint, fastFail, null, null, token, defaultStoreCookies, true).proxy(iface); } @Override public T createClientWithPasswordAuth(final Class iface, final URI endpoint, final String username, final String password, final boolean preemptiveAuth) { final boolean fastFail = iface.isAnnotationPresent(FastFailServiceClient.class); return createWebTarget(endpoint, fastFail, username, password, null, defaultStoreCookies, preemptiveAuth).proxy(iface); } private static String getUsername(URI endpoint) { final String info = endpoint.getUserInfo(); if (StringUtils.isEmpty(info)) return null; else if (info.indexOf(':') != -1) return info.split(":", 2)[0]; else return null; } private static String getPassword(URI endpoint) { final String info = endpoint.getUserInfo(); if (StringUtils.isEmpty(info)) return null; else if (info.indexOf(':') != -1) return info.split(":", 2)[1]; else return null; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy