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

org.jboss.ejb.client.EJBClientContext Maven / Gradle / Ivy

/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2017 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jboss.ejb.client;

import static java.security.AccessController.doPrivileged;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import org.jboss.ejb._private.Keys;
import org.jboss.ejb._private.Logs;
import org.jboss.ejb.protocol.remote.RemotingEJBClientInterceptor;
import org.wildfly.common.Assert;
import org.wildfly.common.context.ContextManager;
import org.wildfly.common.context.Contextual;
import org.wildfly.discovery.Discovery;
import org.wildfly.discovery.ServiceType;
import org.wildfly.naming.client.NamingProvider;
import org.wildfly.security.auth.client.AuthenticationContext;

/**
 * The public API for an EJB client context.  An EJB client context may be associated with (and used by) one or more threads concurrently.
 *
 * @author David M. Lloyd
 */
public final class EJBClientContext extends Attachable implements Contextual {

    /**
     * The service type to use for EJB discovery.
     */
    public static final ServiceType EJB_SERVICE_TYPE = ServiceType.of("ejb", "jboss");

    private static final ContextManager CONTEXT_MANAGER = new ContextManager(EJBClientContext.class, "jboss.ejb.client");

    private static final Supplier DISCOVERY_SUPPLIER = doPrivileged((PrivilegedAction>) Discovery.getContextManager()::getPrivilegedSupplier);

    private static final Supplier GETTER = doPrivileged((PrivilegedAction>) CONTEXT_MANAGER::getPrivilegedSupplier);

    private static final EJBTransportProvider[] NO_TRANSPORT_PROVIDERS = new EJBTransportProvider[0];

    private static final EJBClientInterceptor.Registration[] NO_INTERCEPTORS = new EJBClientInterceptor.Registration[0];
    private static final AtomicReferenceFieldUpdater registrationsUpdater = AtomicReferenceFieldUpdater.newUpdater(EJBClientContext.class, EJBClientInterceptor.Registration[].class, "registrations");
    private volatile EJBClientInterceptor.Registration[] registrations = NO_INTERCEPTORS;

    /**
     * The discovery attribute name which contains the application and module name of the located EJB.
     */
    public static final String FILTER_ATTR_EJB_MODULE = "ejb-module";
    /**
     * The discovery attribute name which contains the application and module name with the distinct name of the located EJB.
     */
    public static final String FILTER_ATTR_EJB_MODULE_DISTINCT = "ejb-module-distinct";
    /**
     * The discovery attribute name which contains a node name.
     */
    public static final String FILTER_ATTR_NODE = "node";
    /**
     * The discovery attribute name which contains a cluster name.
     */
    public static final String FILTER_ATTR_CLUSTER = "cluster";
    /**
     * The discovery attribute name for when a rule only applies to a specific source IP address range.
     */
    public static final String FILTER_ATTR_SOURCE_IP = "source-ip";

    private static final int MAX_SESSION_RETRIES = 8;

    private static final Logs log = Logs.MAIN;

    static {
        CONTEXT_MANAGER.setGlobalDefaultSupplier(EJBClientContext::getDefault);
    }

    static EJBClientContext getDefault() {
        return ConfigurationBasedEJBClientContextSelector.get();
    }

    private final EJBTransportProvider[] transportProviders;
    private final long invocationTimeout;
    private final EJBReceiverContext receiverContext;
    private final List configuredConnections;
    private final Map configuredClusters;
    private final ClusterNodeSelector clusterNodeSelector;
    private final DeploymentNodeSelector deploymentNodeSelector;
    private final ClassValue> proxyInfoValue = new ClassValue>() {
        protected EJBProxyInterceptorInformation computeValue(final Class type) {
            return EJBProxyInterceptorInformation.construct(type, EJBClientContext.this);
        }
    };

    static final InterceptorList defaultInterceptors = new InterceptorList(new EJBClientInterceptorInformation[] {
        EJBClientInterceptorInformation.forClass(TransactionInterceptor.class),
        EJBClientInterceptorInformation.forClass(AuthenticationContextEJBClientInterceptor.class),
        EJBClientInterceptorInformation.forClass(NamingEJBClientInterceptor.class),
        EJBClientInterceptorInformation.forClass(DiscoveryEJBClientInterceptor.class),
        EJBClientInterceptorInformation.forClass(TransactionPostDiscoveryInterceptor.class),
        EJBClientInterceptorInformation.forClass(RemotingEJBClientInterceptor.class),
    });

    private final InterceptorList classPathInterceptors;
    private final InterceptorList globalInterceptors;
    private final Map configuredPerClassInterceptors;
    private final Map> configuredPerMethodInterceptors;
    private final int maximumConnectedClusterNodes;
    private final int defaultCompression;

    EJBClientContext(Builder builder) {
        log.tracef("Creating new EJBClientContext: %s", this);
        final List builderTransportProviders = builder.transportProviders;
        if (builderTransportProviders == null || builderTransportProviders.isEmpty()) {
            transportProviders = NO_TRANSPORT_PROVIDERS;
            log.tracef("New EJBClientContext %s contains no transport providers", this);
        } else {
            transportProviders = builderTransportProviders.toArray(new EJBTransportProvider[builderTransportProviders.size()]);
        }
        invocationTimeout = builder.invocationTimeout;
        receiverContext = new EJBReceiverContext(this);
        final List clientConnections = builder.clientConnections;
        if (clientConnections == null || clientConnections.isEmpty()) {
            configuredConnections = Collections.emptyList();
            log.tracef("New EJBClientContext %s contains no configured connections", this);
        } else if (clientConnections.size() == 1) {
            if (log.isTraceEnabled())
                log.tracef("New EJBClientContext %s contains one configured connection: %s", this, clientConnections.get(0));
            configuredConnections = Collections.singletonList(clientConnections.get(0));
        } else {
            configuredConnections = Collections.unmodifiableList(new ArrayList<>(clientConnections));
            if (log.isTraceEnabled()) {
                StringBuffer buffer = new StringBuffer();
                Iterator iterator = configuredConnections.iterator();
                buffer.append(clientConnections.iterator().next());
                while (iterator.hasNext())
                    buffer.append(", ").append(iterator.next());
                log.tracef("New EJBClientContext %s contains configured connections: %s", this, buffer);
            }
        }
        final List clientClusters = builder.clientClusters;
        if (clientClusters == null || clientClusters.isEmpty()) {
            configuredClusters = Collections.emptyMap();
        } else if (clientClusters.size() == 1) {
            final EJBClientCluster clientCluster = clientClusters.get(0);
            configuredClusters = Collections.singletonMap(clientCluster.getName(), clientCluster);
            log.tracef("New EJBClientContext %s contains configured cluster: %s", this, clientCluster);
        } else {
            Map map = new HashMap<>();
            for (EJBClientCluster clientCluster : clientClusters) {
                map.put(clientCluster.getName(), clientCluster);
                log.tracef("New EJBClientContext %s contains configured cluster: %s", this, clientCluster);
            }
            configuredClusters = Collections.unmodifiableMap(map);
        }
        clusterNodeSelector = builder.clusterNodeSelector;
        deploymentNodeSelector = builder.deploymentNodeSelector;
        maximumConnectedClusterNodes = builder.maximumConnectedClusterNodes;
        defaultCompression = builder.defaultCompression;

        log.tracef("New EJBClientContext %s contains cluster configuration: node selector=%s, deployment selector=%s, maximum nodes=%s",
                this, clusterNodeSelector, deploymentNodeSelector, maximumConnectedClusterNodes);

        // global interceptors
        final List globalInterceptors = builder.globalInterceptors;
        if (globalInterceptors != null) {
            final Iterator globalInterceptorsIterator = globalInterceptors.iterator();
            if (globalInterceptorsIterator.hasNext()) {
                final EJBClientInterceptorInformation first = globalInterceptorsIterator.next();
                if (globalInterceptorsIterator.hasNext()) {
                    // two or more
                    final ArrayList arrayList = new ArrayList<>();
                    arrayList.add(first);
                    do {
                        arrayList.add(globalInterceptorsIterator.next());
                    } while (globalInterceptorsIterator.hasNext());
                    if (arrayList.isEmpty()) {
                        this.globalInterceptors = InterceptorList.EMPTY;
                    } else {
                        this.globalInterceptors = new InterceptorList(arrayList.toArray(EJBClientInterceptorInformation.NO_INTERCEPTORS));
                    }
                } else {
                    // just one
                    this.globalInterceptors = first.getSingletonList();
                }
            } else {
                this.globalInterceptors = InterceptorList.EMPTY;
            }
        } else {
            this.globalInterceptors = InterceptorList.EMPTY;
        }

        // class path interceptors
        this.classPathInterceptors = System.getSecurityManager() != null? doPrivileged((PrivilegedAction) EJBClientContext::getClassPathInterceptorList)
                : getClassPathInterceptorList();

        // configured per-class interceptors
        final List classInterceptors = builder.classInterceptors;
        if (classInterceptors != null) {
            final Iterator classInterceptorsIterator = classInterceptors.iterator();
            if (classInterceptorsIterator.hasNext()) {
                final HashMap> map = new HashMap<>();
                do {
                    final ClassInterceptor classInterceptor = classInterceptorsIterator.next();
                    map.computeIfAbsent(classInterceptor.getClassName(), ignored -> new ArrayList<>()).add(classInterceptor.getInterceptor());
                } while (classInterceptorsIterator.hasNext());
                final Iterator>> mapIterator = map.entrySet().iterator();
                if (mapIterator.hasNext()) {
                    final Map.Entry> first = mapIterator.next();
                    if (mapIterator.hasNext()) {
                        Map targetMap = new HashMap<>(map.size());
                        targetMap.put(first.getKey(), InterceptorList.ofList(first.getValue()));
                        do {
                            final Map.Entry> next = mapIterator.next();
                            targetMap.put(next.getKey(), InterceptorList.ofList(next.getValue()));
                        } while (mapIterator.hasNext());
                        this.configuredPerClassInterceptors = targetMap;
                    } else {
                        this.configuredPerClassInterceptors = Collections.singletonMap(first.getKey(), InterceptorList.ofList(first.getValue()));
                    }
                } else {
                    this.configuredPerClassInterceptors = Collections.emptyMap();
                }
            } else {
                this.configuredPerClassInterceptors = Collections.emptyMap();
            }
        } else {
            this.configuredPerClassInterceptors = Collections.emptyMap();
        }

        // configured per-method interceptors
        final List methodInterceptors = builder.methodInterceptors;
        if (methodInterceptors != null) {
            final Iterator methodInterceptorIterator = methodInterceptors.iterator();
            if (methodInterceptorIterator.hasNext()) {
                final HashMap>> map = new HashMap<>();
                do {
                    final MethodInterceptor methodInterceptor = methodInterceptorIterator.next();
                    map.computeIfAbsent(methodInterceptor.getClassName(), ignored -> new HashMap<>()).computeIfAbsent(methodInterceptor.getMethodLocator(), ignored -> new ArrayList<>()).add(methodInterceptor.getInterceptor());
                } while (methodInterceptorIterator.hasNext());
                final Iterator>>> outerIter = map.entrySet().iterator();
                if (outerIter.hasNext()) {
                    final Map.Entry>> first = outerIter.next();
                    if (outerIter.hasNext()) {
                        Map> targetMap = new HashMap<>(map.size());
                        targetMap.put(first.getKey(), calculateMethodInterceptors(first.getValue()));
                        do {
                            final Map.Entry>> next = outerIter.next();
                            targetMap.put(next.getKey(), calculateMethodInterceptors(next.getValue()));
                        } while (outerIter.hasNext());
                        this.configuredPerMethodInterceptors = targetMap;
                    } else {
                        this.configuredPerMethodInterceptors = Collections.singletonMap(first.getKey(), calculateMethodInterceptors(first.getValue()));
                    }
                } else {
                    this.configuredPerMethodInterceptors = Collections.emptyMap();
                }
            } else {
                this.configuredPerMethodInterceptors = Collections.emptyMap();
            }
        } else {
            this.configuredPerMethodInterceptors = Collections.emptyMap();
        }

        if (log.isTraceEnabled()) {
            if (globalInterceptors != null)
                for (EJBClientInterceptorInformation ejbClientInterceptorInformation : globalInterceptors)
                    log.tracef("New EJBClientContext %s contains global interceptor: %s", this,
                        ejbClientInterceptorInformation);
            if (classPathInterceptors != null)
                for (EJBClientInterceptorInformation ejbClientInterceptorInformation : classPathInterceptors.getInformation())
                    log.tracef("New EJBClientContext %s contains class path interceptor: %s", this,
                        ejbClientInterceptorInformation);
            if (configuredPerClassInterceptors != null)
                for (Map.Entry entry : configuredPerClassInterceptors.entrySet())
                    log.tracef("New EJBClientContext %s contains class interceptor: class=%s, intercpetor=%s", this,
                        entry.getKey(), entry.getValue().getInformation());
            if (configuredPerMethodInterceptors != null)
                for (Map.Entry> classEntry : configuredPerMethodInterceptors.entrySet())
                    for (Map.Entry methodEntry : classEntry.getValue().entrySet())
                        log.tracef("New EJBClientContext %s contains method interceptor: class=%s, method=%s, interceptor=%s",
                            this, classEntry.getKey(), methodEntry.getKey(), methodEntry.getValue());
        }

        // this must be last
        for (EJBTransportProvider transportProvider : transportProviders) {
            log.tracef("New EJBClientContext %s notifying transport provider: %s", this, transportProvider);
            transportProvider.notifyRegistered(receiverContext);
        }
    }

    private static Map calculateMethodInterceptors(final HashMap> map) {
        final Iterator>> iterator = map.entrySet().iterator();
        if (iterator.hasNext()) {
            final Map.Entry> first = iterator.next();
            if (iterator.hasNext()) {
                HashMap targetMap = new HashMap<>(map.size());
                targetMap.put(first.getKey(), InterceptorList.ofList(first.getValue()));
                do {
                    final Map.Entry> next = iterator.next();
                    targetMap.put(next.getKey(), InterceptorList.ofList(next.getValue()));
                } while (iterator.hasNext());
                return targetMap;
            } else {
                return Collections.singletonMap(first.getKey(), InterceptorList.ofList(first.getValue()));
            }
        } else {
            return Collections.emptyMap();
        }
    }

    static InterceptorList getClassPathInterceptorList() {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        try {
            final Enumeration resources = classLoader.getResources("META-INF/services/org.jboss.ejb.client.EJBClientInterceptor");
            if (resources.hasMoreElements()) {
                final ArrayList list = new ArrayList<>();
                do {
                    final URL url = resources.nextElement();
                    try (InputStream st = url.openStream()) {
                        try (InputStreamReader isr = new InputStreamReader(st, StandardCharsets.UTF_8)) {
                            try (BufferedReader r = new BufferedReader(isr)) {
                                String line;
                                while ((line = r.readLine()) != null) {
                                    line = line.trim();
                                    if (line.isEmpty() || line.charAt(0) == '#') {
                                        continue;
                                    }
                                    try {
                                        final Class interceptorClass = Class.forName(line, true, classLoader).asSubclass(EJBClientInterceptor.class);
                                        list.add(EJBClientInterceptorInformation.forClass(interceptorClass));
                                    } catch (ClassNotFoundException e) {
                                        // skip
                                    }
                                }
                            }
                        }
                    }
                } while (resources.hasMoreElements());
                if (list.isEmpty()) {
                    return InterceptorList.EMPTY;
                } else {
                    return new InterceptorList(list.toArray(EJBClientInterceptorInformation.NO_INTERCEPTORS));
                }
            } else {
                return InterceptorList.EMPTY;
            }
        } catch (IOException e) {
            return InterceptorList.EMPTY;
        }
    }

    /**
     * Register a client interceptor with this client context.
     * 

* If the passed clientInterceptor is already added to this context, then this method just returns the * old {@link org.jboss.ejb.client.EJBClientInterceptor.Registration}. * *

* Note: If an interceptor is added or removed after a proxy is used, this will not affect the proxy interceptor list. *

* * @param priority the absolute priority of this interceptor (lower runs earlier; higher runs later) * @param clientInterceptor the interceptor to register * @return a handle which may be used to later remove this registration * * @deprecated Please use EJBClientContext.Builder to manipulate the EJBClientInterceptors. */ @Deprecated public EJBClientInterceptor.Registration registerInterceptor(final int priority, final EJBClientInterceptor clientInterceptor) throws IllegalArgumentException { Assert.checkNotNullParam("clientInterceptor", clientInterceptor); final EJBClientInterceptor.Registration newRegistration = new EJBClientInterceptor.Registration(this, clientInterceptor, priority); EJBClientInterceptor.Registration[] oldRegistrations, newRegistrations; do { oldRegistrations = registrations; for (EJBClientInterceptor.Registration oldRegistration : oldRegistrations) { if (oldRegistration.getInterceptor() == clientInterceptor) { if (oldRegistration.compareTo(newRegistration) == 0) { // This means that a client interceptor which has already been added to this context, // is being added with the same priority. In such cases, this new registration request // is effectively a no-op and we just return the old registration return oldRegistration; } } } final int length = oldRegistrations.length; newRegistrations = Arrays.copyOf(oldRegistrations, length + 1); newRegistrations[length] = newRegistration; Arrays.sort(newRegistrations); } while (!registrationsUpdater.compareAndSet(this, oldRegistrations, newRegistrations)); return newRegistration; } /** * Removes the EJBClientInterceptor from current registrations. It is used by EJBClientInterceptor.Registration itself. *

* Note: If an interceptor is added or removed after a proxy is used, this will not affect the proxy interceptor list. *

* * @param registration the EJBClientInterceptor registration handler * * @deprecated Please use EJBClientContext.Builder to manipulate the EJBClientInterceptors. */ @Deprecated void removeInterceptor(final EJBClientInterceptor.Registration registration) { EJBClientInterceptor.Registration[] oldRegistrations, newRegistrations; do { oldRegistrations = registrations; newRegistrations = null; final int length = oldRegistrations.length; final int newLength = length - 1; if (length == 1) { if (oldRegistrations[0] == registration) { newRegistrations = NO_INTERCEPTORS; } } else { for (int i = 0; i < length; i++) { if (oldRegistrations[i] == registration) { if (i == newLength) { newRegistrations = Arrays.copyOf(oldRegistrations, newLength); break; } else { newRegistrations = new EJBClientInterceptor.Registration[newLength]; if (i > 0) System.arraycopy(oldRegistrations, 0, newRegistrations, 0, i); System.arraycopy(oldRegistrations, i + 1, newRegistrations, i, newLength - i); break; } } } } if (newRegistrations == null) { return; } } while (!registrationsUpdater.compareAndSet(this, oldRegistrations, newRegistrations)); } /** * Get the context manager. Simply calls the {@code static} method {@link #getContextManager()}. * * @return the context manager (not {@code null}) */ public ContextManager getInstanceContextManager() { return getContextManager(); } /** * Get the context manager. * * @return the context manager (not {@code null}) */ public static ContextManager getContextManager() { return CONTEXT_MANAGER; } /** * Get the configured invocation timeout. A value of zero indicates that invocations never time out. * * @return the configured invocation timeout */ public long getInvocationTimeout() { return invocationTimeout; } /** * Get the pre-configured connections for this context. This information may not be used by some transport providers * and mainly exists for legacy compatibility purposes. * * @return the pre-configured connections for this context (not {@code null}) */ public List getConfiguredConnections() { return configuredConnections; } /** * Get the initially configured clusters for this context. The collection will not reflect any topology updates * received from peers. * * @return the initially configured clusters for this context */ public Collection getInitialConfiguredClusters() { return configuredClusters.values(); } /** * Get the maximum connected cluster nodes setting, for connection-based protocols which support eager connection. * * @return the maximum connected cluster nodes count */ public int getMaximumConnectedClusterNodes() { return maximumConnectedClusterNodes; } /** * Get a copy of this context with the given interceptor(s) added. If the array is {@code null} or empty, the * current context is returned as-is. * * @param interceptors the interceptor(s) to add * @return the new context (not {@code null}) */ public EJBClientContext withAddedInterceptors(EJBClientInterceptor... interceptors) { if (interceptors == null) { return this; } final int length = interceptors.length; if (length == 0) { return this; } log.tracef("Creating new EJBClientContext from %s with added interceptors including %s", this, interceptors[0]); final Builder builder = new Builder(this); boolean construct = false; for (EJBClientInterceptor interceptor : interceptors) { if (interceptor != null) { builder.addInterceptor(interceptor); construct = true; } } return construct ? builder.build() : this; } /** * Get a copy of this context with the given transport provider(s) added. If the array is {@code null} or empty, the * current context is returned as-is. * * @param transportProviders the transport providers(s) to add * @return the new context (not {@code null}) */ public EJBClientContext withAddedTransportProviders(EJBTransportProvider... transportProviders) { if (transportProviders == null) { return this; } final int length = transportProviders.length; if (length == 0) { return this; } log.tracef("Creating new EJBClientContext from %s with added transport providers including %s", this, transportProviders[0]); final Builder builder = new Builder(this); boolean construct = false; for (EJBTransportProvider transportProvider : transportProviders) { if (transportProvider != null) { builder.addTransportProvider(transportProvider); construct = true; } } return construct ? builder.build() : this; } EJBReceiver getTransportProvider(final String scheme) { for (EJBTransportProvider transportProvider : transportProviders) { if (transportProvider.supportsProtocol(scheme)) { return transportProvider.getReceiver(receiverContext, scheme); } } return null; } Discovery getDiscovery() { return DISCOVERY_SUPPLIER.get(); } InterceptorList getInterceptors(final Class invokedProxy, final Method method) { return proxyInfoValue.get(invokedProxy).getInterceptors(method); } InterceptorList getInterceptors(final Class invokedProxy) { return proxyInfoValue.get(invokedProxy).getClassInterceptors(); } /** * Resolve the receiver for the given destination. If there is no handler then an exception is raised. * * @param destination the destination URI * @param locator the locator to use for error reports (must not be {@code null}) * @return the resolved receiver (not {@code null}) */ EJBReceiver resolveReceiver(final URI destination, final EJBLocator locator) { if (destination == null) { throw Logs.INVOCATION.noDestinationEstablished(locator); } final String scheme = destination.getScheme(); for (EJBTransportProvider transportProvider : transportProviders) { if (transportProvider.supportsProtocol(scheme)) { final EJBReceiver receiver = transportProvider.getReceiver(receiverContext, scheme); if (receiver != null) return receiver; } } throw Logs.INVOCATION.noEJBReceiverAvailable(destination); } ClusterNodeSelector getClusterNodeSelector() { return clusterNodeSelector; } DeploymentNodeSelector getDeploymentNodeSelector() { return deploymentNodeSelector; } public void close() { for (EJBTransportProvider transportProvider : transportProviders) { try { transportProvider.close(receiverContext); } catch (Exception e) { log.exceptionDuringTransportProviderClose(e); } } } static final class ClassInterceptor { private final String className; private final EJBClientInterceptorInformation interceptor; ClassInterceptor(final String className, final EJBClientInterceptorInformation interceptor) { this.className = className; this.interceptor = interceptor; } String getClassName() { return className; } EJBClientInterceptorInformation getInterceptor() { return interceptor; } } static final class MethodInterceptor { private final String className; private final EJBMethodLocator methodLocator; private final EJBClientInterceptorInformation interceptor; MethodInterceptor(final String className, final EJBMethodLocator methodLocator, final EJBClientInterceptorInformation interceptor) { this.className = className; this.methodLocator = methodLocator; this.interceptor = interceptor; } String getClassName() { return className; } EJBMethodLocator getMethodLocator() { return methodLocator; } EJBClientInterceptorInformation getInterceptor() { return interceptor; } } /** * A builder for EJB client contexts. */ public static final class Builder { List globalInterceptors; List classInterceptors; List methodInterceptors; List transportProviders; List clientConnections; List clientClusters; ClusterNodeSelector clusterNodeSelector = ClusterNodeSelector.DEFAULT_PREFER_LOCAL; DeploymentNodeSelector deploymentNodeSelector = DeploymentNodeSelector.RANDOM_PREFER_LOCAL; long invocationTimeout; int maximumConnectedClusterNodes = 10; int defaultCompression = -1; /** * Construct a new instance. */ public Builder() { } Builder(final EJBClientContext clientContext) { globalInterceptors = Arrays.stream(clientContext.globalInterceptors.getInformation()).collect(Collectors.toCollection(ArrayList::new)); classInterceptors = new ArrayList<>(); for (Map.Entry entry : clientContext.getConfiguredPerClassInterceptors().entrySet()) { final String className = entry.getKey(); for (EJBClientInterceptorInformation information : entry.getValue().getInformation()) { classInterceptors.add(new ClassInterceptor(className, information)); } } methodInterceptors = new ArrayList<>(); for (Map.Entry> entry : clientContext.getConfiguredPerMethodInterceptors().entrySet()) { final String className = entry.getKey(); for (Map.Entry entry1 : entry.getValue().entrySet()) { final EJBMethodLocator methodLocator = entry1.getKey(); for (EJBClientInterceptorInformation information : entry1.getValue().getInformation()) { methodInterceptors.add(new MethodInterceptor(className, methodLocator, information)); } } } transportProviders = new ArrayList<>(); Collections.addAll(transportProviders, clientContext.transportProviders); clientConnections = new ArrayList<>(); clientConnections.addAll(clientContext.getConfiguredConnections()); clientClusters = new ArrayList<>(); clientClusters.addAll(clientContext.configuredClusters.values()); clusterNodeSelector = clientContext.clusterNodeSelector; deploymentNodeSelector = clientContext.deploymentNodeSelector; invocationTimeout = clientContext.invocationTimeout; } public Builder addInterceptor(EJBClientInterceptor interceptor) { Assert.checkNotNullParam("interceptor", interceptor); if (globalInterceptors == null) { globalInterceptors = new ArrayList<>(); } globalInterceptors.add(EJBClientInterceptorInformation.forInstance(interceptor)); return this; } public Builder addInterceptor(Class interceptorClass) { Assert.checkNotNullParam("interceptorClass", interceptorClass); if (globalInterceptors == null) { globalInterceptors = new ArrayList<>(); } globalInterceptors.add(EJBClientInterceptorInformation.forClass(interceptorClass)); return this; } public Builder addClassInterceptor(String className, EJBClientInterceptor interceptor) { Assert.checkNotNullParam("className", className); Assert.checkNotNullParam("interceptor", interceptor); if (classInterceptors == null) { classInterceptors = new ArrayList<>(); } classInterceptors.add(new ClassInterceptor(className, EJBClientInterceptorInformation.forInstance(interceptor))); return this; } public Builder addClassInterceptor(String className, Class interceptorClass) { Assert.checkNotNullParam("className", className); Assert.checkNotNullParam("interceptorClass", interceptorClass); if (classInterceptors == null) { classInterceptors = new ArrayList<>(); } classInterceptors.add(new ClassInterceptor(className, EJBClientInterceptorInformation.forClass(interceptorClass))); return this; } public Builder addMethodInterceptor(String className, EJBMethodLocator methodLocator, EJBClientInterceptor interceptor) { Assert.checkNotNullParam("className", className); Assert.checkNotNullParam("methodLocator", methodLocator); Assert.checkNotNullParam("interceptor", interceptor); if (methodInterceptors == null) { methodInterceptors = new ArrayList<>(); } methodInterceptors.add(new MethodInterceptor(className, methodLocator, EJBClientInterceptorInformation.forInstance(interceptor))); return this; } public Builder addMethodInterceptor(String className, EJBMethodLocator methodLocator, Class interceptorClass) { Assert.checkNotNullParam("className", className); Assert.checkNotNullParam("methodLocator", methodLocator); Assert.checkNotNullParam("interceptorClass", interceptorClass); if (methodInterceptors == null) { methodInterceptors = new ArrayList<>(); } methodInterceptors.add(new MethodInterceptor(className, methodLocator, EJBClientInterceptorInformation.forClass(interceptorClass))); return this; } public Builder addTransportProvider(EJBTransportProvider provider) { Assert.checkNotNullParam("provider", provider); if (transportProviders == null) { transportProviders = new ArrayList<>(); } transportProviders.add(provider); return this; } public Builder addClientConnection(EJBClientConnection connection) { Assert.checkNotNullParam("connection", connection); if (clientConnections == null) { clientConnections = new ArrayList<>(); } clientConnections.add(connection); return this; } public Builder addClientCluster(EJBClientCluster cluster) { Assert.checkNotNullParam("cluster", cluster); if (clientClusters == null) { clientClusters = new ArrayList<>(); } clientClusters.add(cluster); return this; } public Builder setClusterNodeSelector(final ClusterNodeSelector clusterNodeSelector) { Assert.checkNotNullParam("clusterNodeSelector", clusterNodeSelector); this.clusterNodeSelector = clusterNodeSelector; return this; } public Builder setDeploymentNodeSelector(final DeploymentNodeSelector deploymentNodeSelector) { Assert.checkNotNullParam("deploymentNodeSelector", deploymentNodeSelector); this.deploymentNodeSelector = deploymentNodeSelector; return this; } public Builder setInvocationTimeout(final long invocationTimeout) { Assert.checkMinimumParameter("invocationTimeout", 0L, invocationTimeout); this.invocationTimeout = invocationTimeout; return this; } public Builder setMaximumConnectedClusterNodes(final int maximumConnectedClusterNodes) { Assert.checkMinimumParameter("maximumConnectedClusterNodes", 0, maximumConnectedClusterNodes); this.maximumConnectedClusterNodes = maximumConnectedClusterNodes; return this; } public void setDefaultCompression(int defaultCompression) { Assert.checkMinimumParameter("defaultCompression", -1, defaultCompression); Assert.checkMaximumParameter("defaultCompression", 9, defaultCompression); this.defaultCompression = defaultCompression; } public EJBClientContext build() { return new EJBClientContext(this); } } /** * Get the current client context for this thread. * * @return the current client context */ public static EJBClientContext getCurrent() { final EJBClientContext clientContext = GETTER.get(); if (clientContext == null) { throw Logs.MAIN.noEJBClientContextAvailable(); } return clientContext; } /** * Get the current client context for this thread. Delegates to {@link #getCurrent()}. * * @return the current client context for this thread */ public static EJBClientContext requireCurrent() { return getCurrent(); } StatefulEJBLocator createSession(final StatelessEJBLocator statelessLocator, final AuthenticationContext authenticationContext, final NamingProvider namingProvider) throws Exception { EJBSessionCreationInvocationContext context = createSessionCreationInvocationContext(statelessLocator, authenticationContext); return createSession(context, statelessLocator, namingProvider); } EJBSessionCreationInvocationContext createSessionCreationInvocationContext(StatelessEJBLocator statelessLocator, AuthenticationContext authenticationContext) { EJBClientContext.InterceptorList interceptorList = getInterceptors(statelessLocator.getViewType()); return new EJBSessionCreationInvocationContext(statelessLocator, this, authenticationContext, interceptorList); } StatefulEJBLocator createSession(final EJBSessionCreationInvocationContext context, final StatelessEJBLocator statelessLocator, final NamingProvider namingProvider) throws Exception { // Special hook for naming; let's replace this sometime soon. if (namingProvider != null) context.putAttachment(Keys.NAMING_PROVIDER_ATTACHMENT_KEY, namingProvider); if (Logs.INVOCATION.isDebugEnabled()) { Logs.INVOCATION.debugf("Calling createSession(locator = %s)", statelessLocator); } SessionID sessionID = null; for (int i = 0; i < MAX_SESSION_RETRIES; i++) { Throwable t; try { sessionID = context.proceedInitial(); break; } catch (RequestSendFailedException r) { if (! r.canBeRetried()) { throw r; } t = r; } catch (Exception | Error o) { if (! context.shouldRetry()) { throw o; } t = o; } catch (Throwable o) { if (! context.shouldRetry()) { Exception e = new RequestSendFailedException(o.getClass().getSimpleName() + ": " + o.getMessage(), o.getCause()); e.setStackTrace(o.getStackTrace()); throw e; } t = o; } if (i == MAX_SESSION_RETRIES - 1) { throw new RequestSendFailedException(t.getMessage() + " (maximum retries exceeded)", t); } if (Logs.INVOCATION.isDebugEnabled()) { Logs.INVOCATION.debugf("Retrying invocation (attempt %d): %s", i + 1, statelessLocator); } } final Affinity affinity = context.getLocator().getAffinity(); if (Logs.INVOCATION.isDebugEnabled()) { Logs.INVOCATION.debugf("Session created: session id: %s , affinity: %s", sessionID, affinity); } return statelessLocator.withSessionAndAffinity(sessionID, affinity); } InterceptorList getClassPathInterceptors() { return classPathInterceptors; } InterceptorList getGlobalInterceptors() { return globalInterceptors.combine(registeredInterceptors()); } private InterceptorList registeredInterceptors() { final EJBClientInterceptor.Registration[] currentRegistrations = this.registrations.clone(); ArrayList al = new ArrayList<>(); for (EJBClientInterceptor.Registration r: currentRegistrations) { al.add(EJBClientInterceptorInformation.forInstance(r.getInterceptor())); } return InterceptorList.ofList(al); } Map getConfiguredPerClassInterceptors() { return configuredPerClassInterceptors; } Map> getConfiguredPerMethodInterceptors() { return configuredPerMethodInterceptors; } public int getDefaultCompression() { return defaultCompression; } static T withSuppressed(T original, Collection suppressed) { if (suppressed != null) { for (Throwable throwable : suppressed) { original.addSuppressed(throwable); } } return original; } static T withSuppressed(T original, Collection suppressed1, Collection suppressed2) { if (suppressed1 != null) { for (Throwable throwable : suppressed1) { original.addSuppressed(throwable); } } if (suppressed2 != null) { for (Throwable throwable : suppressed2) { original.addSuppressed(throwable); } } return original; } static final class InterceptorList { static final InterceptorList EMPTY = new InterceptorList(EJBClientInterceptorInformation.NO_INTERCEPTORS); private final EJBClientInterceptorInformation[] information; private final int hashCode; InterceptorList(final EJBClientInterceptorInformation[] information) { Arrays.sort(information); this.information = information; hashCode = Arrays.hashCode(information); } EJBClientInterceptorInformation[] getInformation() { return information; } public boolean equals(final Object obj) { return obj instanceof InterceptorList && Arrays.equals(information, ((InterceptorList) obj).information); } public int hashCode() { return hashCode; } private static InterceptorList ofList(final ArrayList value) { if (value.isEmpty()) { return EMPTY; } else if (value.size() == 1) { return value.get(0).getSingletonList(); } else { return new InterceptorList(value.toArray(EJBClientInterceptorInformation.NO_INTERCEPTORS)); } } InterceptorList combine(final InterceptorList other) { return information.length == 0 ? other : other.information.length == 0 ? this : new InterceptorList(concat(information, other.information)); } private static EJBClientInterceptorInformation[] concat(final EJBClientInterceptorInformation[] a, final EJBClientInterceptorInformation[] b) { final EJBClientInterceptorInformation[] c = Arrays.copyOf(a, a.length + b.length); System.arraycopy(b, 0, c, a.length, b.length); return c; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy