All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.oracle.coherence.client.GrpcChannelFactory Maven / Gradle / Ivy
/*
* Copyright (c) 2000, 2022, Oracle and/or its affiliates.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* https://oss.oracle.com/licenses/upl.
*/
package com.oracle.coherence.client;
import com.oracle.coherence.common.base.Classes;
import com.oracle.coherence.common.base.Logger;
import com.oracle.coherence.common.net.InetSocketAddress32;
import com.oracle.coherence.grpc.CredentialsHelper;
import com.tangosol.coherence.component.net.extend.remoteService.RemoteNameService;
import com.tangosol.coherence.config.builder.FactoryBasedAddressProviderBuilder;
import com.tangosol.coherence.config.builder.ParameterizedBuilder;
import com.tangosol.coherence.config.builder.SocketProviderBuilder;
import com.tangosol.config.expression.NullParameterResolver;
import com.tangosol.internal.net.grpc.RemoteGrpcCacheServiceDependencies;
import com.tangosol.internal.net.service.extend.remote.DefaultRemoteNameServiceDependencies;
import com.tangosol.internal.net.service.extend.remote.LegacyXmlRemoteNameServiceHelper;
import com.tangosol.internal.net.service.peer.initiator.DefaultTcpInitiatorDependencies;
import com.tangosol.net.AddressProviderFactory;
import com.tangosol.net.CacheFactory;
import com.tangosol.net.NameService;
import com.tangosol.net.OperationalContext;
import com.tangosol.net.SocketAddressProvider;
import com.tangosol.net.SocketProviderFactory;
import com.tangosol.net.grpc.GrpcChannelDependencies;
import com.tangosol.net.grpc.GrpcDependencies;
import com.tangosol.net.messaging.ConnectionException;
import io.grpc.Attributes;
import io.grpc.Channel;
import io.grpc.ChannelCredentials;
import io.grpc.EquivalentAddressGroup;
import io.grpc.Grpc;
import io.grpc.ManagedChannelBuilder;
import io.grpc.NameResolver;
import io.grpc.NameResolverProvider;
import io.grpc.NameResolverRegistry;
import io.grpc.ProxyDetector;
import io.grpc.Status;
import io.grpc.internal.GrpcUtil;
import io.grpc.internal.SharedResourceHolder;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
/**
* A default implementation of {@link GrpcChannelFactory}.
*
* @author Jonathan Knight 2022.08.25
* @since 22.06.2
*/
public class GrpcChannelFactory
extends NameResolverProvider
{
// ----- constructors ---------------------------------------------------
/**
* Create a {@link GrpcChannelFactory}
*/
private GrpcChannelFactory()
{
NameResolverRegistry.getDefaultRegistry().register(this);
}
// ----- GrpcChannelFactory methods -------------------------------------
/**
* Returns the singleton instance of {@link GrpcChannelFactory}.
*
* @return the singleton instance of {@link GrpcChannelFactory}
*/
public static GrpcChannelFactory singleton()
{
return Instance.Singleton.getFactory();
}
public Channel getChannel(GrpcRemoteCacheService service)
{
RemoteGrpcCacheServiceDependencies depsService = service.getDependencies();
GrpcChannelDependencies depsChannel = depsService.getChannelDependencies();
ManagedChannelBuilder> builder = (ManagedChannelBuilder>) depsChannel.getChannelProvider()
.orElse(createManagedChannelBuilder(service));
return builder.build();
}
// ----- helper methods -------------------------------------------------
private ManagedChannelBuilder> createManagedChannelBuilder(GrpcRemoteCacheService service)
{
RemoteGrpcCacheServiceDependencies depsService = service.getDependencies();
OperationalContext ctx = (OperationalContext) service.getCluster();
String sService = service.getServiceName();
String sKey = GrpcServiceInfo.createKey(service);
String sRemoteService = depsService.getRemoteServiceName();
String sRemoteCluster = depsService.getRemoteClusterName();
GrpcChannelDependencies depsChannel = depsService.getChannelDependencies();
m_mapServiceInfo.put(sKey, new GrpcServiceInfo(ctx, sService, sRemoteService, sRemoteCluster, depsChannel));
String sTarget = depsChannel.getTarget();
if (sTarget == null)
{
sTarget = GrpcServiceInfo.createTargetURI(service);
}
SocketProviderBuilder builder = depsChannel.getSocketProviderBuilder();
ChannelCredentials credentials = CredentialsHelper.createChannelCredentials(sService, builder);
ManagedChannelBuilder> channelBuilder = Grpc.newChannelBuilder(sTarget, credentials);
depsChannel.getAuthorityOverride().ifPresent(channelBuilder::overrideAuthority);
depsChannel.getConfigurer()
.filter(GrpcChannelConfigurer.class::isInstance)
.map(GrpcChannelConfigurer.class::cast)
.ifPresent(c -> c.apply(channelBuilder));
channelBuilder.defaultLoadBalancingPolicy(depsChannel.getDefaultLoadBalancingPolicy());
channelBuilder.userAgent("Coherence Java Client");
return channelBuilder;
}
// ----- NameResolverProvider methods -----------------------------------
@Override
public NameResolver newNameResolver(URI targetUri, NameResolver.Args args)
{
String sKey = GrpcServiceInfo.parseServiceInfoKey(targetUri);
GrpcServiceInfo serviceInfo = m_mapServiceInfo.get(sKey);
GrpcChannelDependencies dependencies = serviceInfo.getDependencies();
return new AddressProviderNameResolver(dependencies, serviceInfo, args);
}
@Override
public String getDefaultScheme()
{
return RESOLVER_SCHEME;
}
@Override
protected boolean isAvailable()
{
return true;
}
@Override
protected int priority()
{
return 0;
}
// ----- inner class: AddressProviderNameResolver -----------------------
public static class AddressProviderNameResolver
extends NameResolver
{
@SuppressWarnings("rawtypes")
public AddressProviderNameResolver(GrpcChannelDependencies deps,
GrpcServiceInfo serviceInfo, NameResolver.Args args)
{
m_fNameServiceAddressProvider = deps.isNameServiceAddressProvider();
m_executorResource = GrpcUtil.SHARED_CHANNEL_EXECUTOR;
m_serviceInfo = serviceInfo;
m_nameResolverArgs = args;
ParameterizedBuilder bldr = deps.getRemoteAddressProviderBuilder();
if (bldr == null)
{
// default to the "cluster-discovery" address provider which consists of MC or WKAs
AddressProviderFactory factory = serviceInfo.getOperationalContext()
.getAddressProviderMap().get("cluster-discovery");
if (factory != null)
{
if (factory instanceof ParameterizedBuilder)
{
bldr = (ParameterizedBuilder) factory;
}
else
{
bldr = new FactoryBasedAddressProviderBuilder(factory);
}
}
}
ClassLoader loader = Classes.getContextClassLoader();
m_addressProvider = (SocketAddressProvider) bldr.realize(new NullParameterResolver(), loader, null);
// run an initial resolve to set the authority
new Resolve(this, serviceInfo).run();
}
@Override
public String getServiceAuthority()
{
return m_sAuthority;
}
@Override
public void start(Listener2 listener)
{
m_listener = Objects.requireNonNull(listener);
m_executor = SharedResourceHolder.get(m_executorResource);
resolve();
}
@Override
public void refresh()
{
if (m_listener != null)
{
resolve();
}
}
@Override
public void shutdown()
{
if (m_fShutdown)
{
return;
}
m_fShutdown = true;
if (m_executor != null)
{
m_executor = SharedResourceHolder.release(m_executorResource, m_executor);
}
}
// ----- helper methods ---------------------------------------------
protected Args getNameResolverArgs()
{
return m_nameResolverArgs;
}
protected SocketAddressProvider getSocketAddressProvider()
{
return m_addressProvider;
}
protected boolean isNameServiceAddressProvider()
{
return m_fNameServiceAddressProvider;
}
protected void setAuthority(String sAuthority)
{
m_sAuthority = sAuthority;
}
private void resolve()
{
if (m_fResolving || m_fShutdown)
{
return;
}
m_fResolving = true;
m_executor.execute(new Resolve(this, m_serviceInfo, m_listener));
}
// ----- data members -----------------------------------------------
private final GrpcServiceInfo m_serviceInfo;
private final NameResolver.Args m_nameResolverArgs;
private String m_sAuthority;
private final SocketAddressProvider m_addressProvider;
private final boolean m_fNameServiceAddressProvider;
private volatile boolean m_fResolving;
private volatile boolean m_fShutdown;
private Executor m_executor;
private final SharedResourceHolder.Resource m_executorResource;
private Listener2 m_listener;
}
// ----- inner class: Resolve -------------------------------------------
protected static class Resolve
implements Runnable
{
protected Resolve(AddressProviderNameResolver addressProviderNameResolver, GrpcServiceInfo serviceInfo)
{
this(addressProviderNameResolver, serviceInfo, null);
}
protected Resolve(AddressProviderNameResolver addressProviderNameResolver,
GrpcServiceInfo serviceInfo, NameResolver.Listener2 listener)
{
m_addressProviderNameResolver = addressProviderNameResolver;
m_serviceInfo = serviceInfo;
m_listener = listener;
}
@Override
public void run()
{
List list = m_addressProviderNameResolver.isNameServiceAddressProvider()
? lookupAddresses()
: resolveAddresses();
NameResolver.ResolutionResult result;
if (list.isEmpty())
{
NameResolver.ConfigOrError error = NameResolver.ConfigOrError
.fromError(Status.FAILED_PRECONDITION.withDescription("Failed to resolve any gRPC proxy addresses"));
result = NameResolver.ResolutionResult.newBuilder()
.setServiceConfig(error)
.setAttributes(Attributes.EMPTY)
.build();
}
else
{
try
{
NameResolver.Args args = m_addressProviderNameResolver.getNameResolverArgs();
ProxyDetector proxyDetector = args == null ? null : args.getProxyDetector();
if (proxyDetector != null)
{
List proxiedAddresses = new ArrayList<>();
for (SocketAddress socketAddress : list)
{
proxiedAddresses.add(Objects.requireNonNullElse(proxyDetector.proxyFor(socketAddress), socketAddress));
}
list = proxiedAddresses;
}
result = NameResolver.ResolutionResult.newBuilder()
.setAddresses(Collections.singletonList(new EquivalentAddressGroup(list)))
.setAttributes(Attributes.EMPTY)
.build();
}
catch (IOException e)
{
Logger.err(e);
NameResolver.ConfigOrError error = NameResolver.ConfigOrError
.fromError(Status.INTERNAL.withDescription(e.getMessage()));
result = NameResolver.ResolutionResult.newBuilder()
.setServiceConfig(error)
.setAttributes(Attributes.EMPTY)
.build();
}
}
if (m_listener != null)
{
m_listener.onResult(result);
}
}
protected List resolveAddresses()
{
SocketAddressProvider addressProvider = m_addressProviderNameResolver.getSocketAddressProvider();
List list = new ArrayList<>();
SocketAddress address = addressProvider.getNextAddress();
boolean fFirst = true;
while (address != null)
{
if (address instanceof InetSocketAddress32)
{
// gRPC Java only allows plain InetSocketAddress
address = new InetSocketAddress(((InetSocketAddress32) address).getAddress(),
((InetSocketAddress32) address).getPort());
}
if (address instanceof InetSocketAddress)
{
if (fFirst)
{
updateAuthority((InetSocketAddress) address);
fFirst = false;
}
list.add(address);
}
address = addressProvider.getNextAddress();
}
return list;
}
private void updateAuthority(InetSocketAddress address)
{
String sAuthority = GrpcUtil.authorityFromHostAndPort(address.getHostString(), address.getPort());
m_addressProviderNameResolver.setAuthority(sAuthority);
}
@SuppressWarnings("resource")
protected List lookupAddresses()
{
SocketAddressProvider addressProvider = m_addressProviderNameResolver.getSocketAddressProvider();
RemoteNameService serviceNS = new RemoteNameService();
OperationalContext context = m_serviceInfo.getOperationalContext();
serviceNS.setOperationalContext(context);
serviceNS.setContextClassLoader(Classes.getContextClassLoader());
serviceNS.setServiceName(m_serviceInfo.getService() + ':' + NameService.TYPE_REMOTE);
DefaultRemoteNameServiceDependencies nameServiceDeps =
LegacyXmlRemoteNameServiceHelper.fromXml(
CacheFactory.getServiceConfig(NameService.TYPE_REMOTE),
new DefaultRemoteNameServiceDependencies(),
context,
Classes.getContextClassLoader());
// clone and inject the RemoteAddressProvider from this service's dependencies
// into the RemoteNameService
DefaultTcpInitiatorDependencies depsNsTcp = new DefaultTcpInitiatorDependencies();
depsNsTcp.setRemoteSocketAddressProviderBuilder((resolver, loader, listParameters) -> addressProvider);
// use the default socket provider, as we don't want to inherit SSL settings, NS is always in the clear
depsNsTcp.setSocketProviderBuilder(new SocketProviderBuilder(SocketProviderFactory.DEFAULT_SOCKET_PROVIDER, false));
String sServiceRemote = m_serviceInfo.getRemoteService();
String sCluster = m_serviceInfo.getRemoteCluster();
if (sCluster == null || sCluster.isEmpty())
{
// NS lookups and corresponding redirects are always done with a cluster name since multiple
// clusters may effectively share the cluster port we don't know what cluster we'd land in.
// remote-address based lookups on the other hand use the cluster name configured in the remote
// scheme, which is allowed to be null. This is because a remote-address based lookup is pointing
// at an explicit unsharable port, and it is presumed the configuration is correct.
sCluster = context.getLocalMember().getClusterName();
}
nameServiceDeps.setInitiatorDependencies(depsNsTcp);
nameServiceDeps.setRemoteClusterName(sCluster);
nameServiceDeps.setRemoteServiceName("NameService");
serviceNS.setDependencies(nameServiceDeps);
try
{
serviceNS.start();
Object[] aoResult = (Object[]) serviceNS.lookup(sServiceRemote);
if (aoResult == null)
{
// we got an answer, which means we found the cluster, but not the service
throw new ConnectionException("Unable to locate ProxyService '" + sServiceRemote
+ "' within cluster '" + m_serviceInfo.getRemoteCluster() + "'");
}
List list = new ArrayList<>();
for (int i = 0; i < aoResult.length; i += 2)
{
list.add(new InetSocketAddress((String) aoResult[i], (Integer) aoResult[i+1]));
}
list.stream()
.findAny()
.ifPresent(address -> updateAuthority((InetSocketAddress) address));
if (list.isEmpty())
{
throw new ConnectionException("Unable to locate any addresses in cluster '" + sCluster
+ "' while looking for its ProxyService '" + sServiceRemote + "'");
}
return list;
}
catch (Exception ex)
{
// we failed to connect, thus the cluster was not reachable
throw new ConnectionException("Unable to locate cluster '" + sCluster
+ "' while looking for its ProxyService '" + sServiceRemote + "'", ex);
}
finally
{
serviceNS.stop();
}
}
private final AddressProviderNameResolver m_addressProviderNameResolver;
private final NameResolver.Listener2 m_listener;
private final GrpcServiceInfo m_serviceInfo;
}
// ----- inner class: GrpcServiceInfo -----------------------------------
public static class GrpcServiceInfo
{
public GrpcServiceInfo(OperationalContext ctx, String sService, String sRemoteService,
String sRemoteCluster, GrpcChannelDependencies dependencies)
{
m_operationalContext = ctx;
m_sService = sService;
m_sRemoteService = sRemoteService;
m_sRemoteCluster = sRemoteCluster;
m_dependencies = dependencies;
}
public static String createKey(GrpcRemoteCacheService service)
{
String sService = service.getServiceName();
String sScope = service.getScopeName();
if (sScope == null)
{
return sService + KEY_SEPARATOR;
}
return sService + KEY_SEPARATOR + sScope;
}
public static String parseServiceInfoKey(URI uri)
{
String sService = uri.getAuthority();
String sScope = uri.getQuery();
if (sScope != null && !sScope.isEmpty() && sScope.charAt(0) == '/')
{
sScope = sService.substring(1);
}
if (sScope == null)
{
return sService + KEY_SEPARATOR;
}
return sService + KEY_SEPARATOR + sScope;
}
public static String createTargetURI(GrpcRemoteCacheService service)
{
String sService = service.getServiceName();
String sScope = service.getScopeName();
if (sScope == null)
{
return RESOLVER_SCHEME + "://" + sService;
}
return RESOLVER_SCHEME + "://" + sService + "?" + sScope;
}
public OperationalContext getOperationalContext()
{
return m_operationalContext;
}
public String getService()
{
return m_sService;
}
public String getRemoteService()
{
if (m_sRemoteService == null || m_sRemoteService.isBlank())
{
return GrpcDependencies.SCOPED_PROXY_SERVICE_NAME;
}
return m_sRemoteService;
}
public String getRemoteCluster()
{
return m_sRemoteCluster;
}
public GrpcChannelDependencies getDependencies()
{
return m_dependencies;
}
// ----- constants ------------------------------------------------------
public static final String KEY_SEPARATOR = "$";
// ----- data members ---------------------------------------------------
private final OperationalContext m_operationalContext;
private final String m_sService;
private final String m_sRemoteService;
private final String m_sRemoteCluster;
private final GrpcChannelDependencies m_dependencies;
}
// ----- singleton enum -------------------------------------------------
private enum Instance
{
Singleton(new GrpcChannelFactory());
Instance(GrpcChannelFactory factory)
{
m_factory = factory;
}
public GrpcChannelFactory getFactory()
{
return m_factory;
}
private final GrpcChannelFactory m_factory;
}
// ----- constants ------------------------------------------------------
public static final String RESOLVER_SCHEME = "coherence";
// ----- data members ---------------------------------------------------
private final Map m_mapServiceInfo = new ConcurrentHashMap<>();
}