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

org.jboss.ejb.protocol.remote.NodeInformation Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
Show newest version
/*

 * 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.protocol.remote;

import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.jboss.ejb._private.Logs;
import org.jboss.ejb.client.EJBClientContext;
import org.jboss.ejb.client.EJBModuleIdentifier;
import org.jboss.logging.Logger;
import org.jboss.remoting3.Connection;
import org.wildfly.common.Assert;
import org.wildfly.common.net.CidrAddress;
import org.wildfly.common.net.CidrAddressTable;
import org.wildfly.common.net.Inet;
import org.wildfly.discovery.AttributeValue;
import org.wildfly.discovery.FilterSpec;
import org.wildfly.discovery.ServiceType;
import org.wildfly.discovery.ServiceURL;
import org.wildfly.discovery.spi.DiscoveryResult;

/**
 * @author David M. Lloyd
 */
final class NodeInformation {
    private final String nodeName;

    private final ConcurrentMap> modulesByConnection = new ConcurrentHashMap<>(1);
    private final ConcurrentMap clustersByName = new ConcurrentHashMap<>(1);
    private final ConcurrentMap addressesByConnection = new ConcurrentHashMap<>(1);

    private volatile List serviceURLCache;

    private volatile boolean invalid;

    NodeInformation(final String nodeName) {
        this.nodeName = nodeName;
    }

    String getNodeName() {
        return nodeName;
    }

    boolean discover(ServiceType serviceType, FilterSpec filterSpec, DiscoveryResult discoveryResult) {
        if (invalid) return false;
        boolean found = false;
        List serviceURLCache = getServiceURLCache();
        for (ServiceURL serviceURL : serviceURLCache) {
            if (serviceURL.satisfies(filterSpec) && serviceType.implies(serviceURL)) {
                found = true;
                discoveryResult.addMatch(serviceURL);
            }
        }
        return found;
    }

    ConcurrentMap getClustersByName() {
        return clustersByName;
    }

    // this structure mashes together the cluster topo reports from various nodes, and the mod avail report from this node
    static class TempInfo {
        Map clusters;
        Set modules;
        URI destination;

        TempInfo(URI destination) {
            this.destination = destination;
        }
    }

    private List getServiceURLCache() {
        List serviceURLCache = this.serviceURLCache;
        if (serviceURLCache == null) {
            synchronized (this) {
                serviceURLCache = this.serviceURLCache;
                if (serviceURLCache == null) {
                    // the final list to store
                    serviceURLCache = new ArrayList<>();
                    HashMap map = new HashMap<>();
                    // hostname representations encountered - these can vary
                    Set hostReps = new HashSet();
                    // populate standalone nodes
                    for (Map.Entry entry : addressesByConnection.entrySet()) {
                        final EJBClientChannel channel = entry.getKey();
                        final Connection peerConnection = channel.getChannel().getConnection();
                        final URI peerURI = buildURI(peerConnection.getProtocol(), peerConnection.getPeerAddress(InetSocketAddress.class));
                        // keep track of equivalent hostnames
                        hostReps.add(peerURI.getHost());
                        map.computeIfAbsent(peerURI, TempInfo::new);
                    }
                    // populate the modules
                    for (Map.Entry> entry : modulesByConnection.entrySet()) {
                        final EJBClientChannel channel = entry.getKey();
                        final Connection peerConnection = channel.getChannel().getConnection();
                        final URI peerURI = buildURI(peerConnection.getProtocol(), peerConnection.getPeerAddress(InetSocketAddress.class));
                        // keep track of equivalent hostnames
                        hostReps.add(peerURI.getHost());
                        TempInfo tempInfo = new TempInfo(peerURI);
                        tempInfo.modules = entry.getValue();
                        map.put(tempInfo.destination, tempInfo);
                    }
                    // populate the clusters next
                    for (final Map.Entry entry : clustersByName.entrySet()) {
                        final String clusterName = entry.getKey();
                        final ClusterNodeInformation clusterNodeInformation = entry.getValue();
                        for (Map.Entry> entry1 : clusterNodeInformation.getAddressTablesByProtocol().entrySet()) {
                            final String protocol = entry1.getKey();
                            final CidrAddressTable table = entry1.getValue();
                            for (CidrAddressTable.Mapping mapping : table) {
                                try {
                                    CidrAddress cidrAddress = mapping.getRange();
                                    final InetSocketAddress address = Inet.getResolved(mapping.getValue());
                                    final URI uri = buildURI(protocol, address);
                                    // keep track of equivalent hostnames
                                    hostReps.add(uri.getHost());
                                    TempInfo tempInfo = map.computeIfAbsent(uri, TempInfo::new);
                                    if (tempInfo.clusters == null) {
                                        tempInfo.clusters = new HashMap<>();
                                    }
                                    tempInfo.clusters.put(clusterName, cidrAddress);
                                } catch (UnknownHostException e) {
                                    Logs.MAIN.logf(Logger.Level.DEBUG, "Cannot resolve %s host during discovery attempt, skipping", mapping.getValue());
                                }
                            }
                        }
                    }

                    // merge information from equivalent URIs, if any (EJBCLIENT-349)
                    // due to mixed-mode hostnames, we can have module information and cluster information for "the same" URLs spread across multiple TempInfo structures
                    // here, we make sure that for each equivalent URL, they hold the same module and cluster information
                    for (URI destination : map.keySet()) {
                        Set equivalentURIs = getEquivalentURIs(destination, hostReps);
                        for (URI equivalentURI: equivalentURIs) {
                            // for each equivalent URI, check if we need to merge modules and clusters
                            TempInfo originalTempInfo = map.get(destination);
                            TempInfo equivalentTempInfo = map.get(equivalentURI);
                            if (originalTempInfo == null || equivalentTempInfo == null) {
                                continue;
                            }
                            // take information in each and combine
                            mergeEquivalentTempInfoStructures(originalTempInfo, equivalentTempInfo);
                        }
                    }

                    // populate the service URLs from the cross product (!) of clusters and modules
                    final AttributeValue nodeNameValue = AttributeValue.fromString(nodeName);
                    for (TempInfo info : map.values()) {
                        final ServiceURL.Builder builder = new ServiceURL.Builder();
                        builder.setUri(info.destination);
                        builder.setAbstractType(EJBClientContext.EJB_SERVICE_TYPE.getAbstractType());
                        builder.setAbstractTypeAuthority(EJBClientContext.EJB_SERVICE_TYPE.getAbstractTypeAuthority());
                        builder.addAttribute(EJBClientContext.FILTER_ATTR_NODE, nodeNameValue);
                        if (info.modules != null) for (EJBModuleIdentifier moduleIdentifier : info.modules) {
                            final String appName = moduleIdentifier.getAppName();
                            final String moduleName = moduleIdentifier.getModuleName();
                            final String distinctName = moduleIdentifier.getDistinctName();
                            if (distinctName.isEmpty()) {
                                if (appName.isEmpty()) {
                                    builder.addAttribute(EJBClientContext.FILTER_ATTR_EJB_MODULE, AttributeValue.fromString(moduleName));
                                } else {
                                    builder.addAttribute(EJBClientContext.FILTER_ATTR_EJB_MODULE, AttributeValue.fromString(appName + "/" + moduleName));
                                }
                            } else {
                                if (appName.isEmpty()) {
                                    builder.addAttribute(EJBClientContext.FILTER_ATTR_EJB_MODULE_DISTINCT, AttributeValue.fromString(moduleName + "/" + distinctName));
                                } else {
                                    builder.addAttribute(EJBClientContext.FILTER_ATTR_EJB_MODULE_DISTINCT, AttributeValue.fromString(appName + "/" + moduleName + "/" + distinctName));
                                }
                            }
                        }
                        // create a no-cluster mapping
                        serviceURLCache.add(builder.create());
                        if (info.clusters != null) for (Map.Entry entry : info.clusters.entrySet()) {
                            final String clusterName = entry.getKey();
                            builder.addAttribute(EJBClientContext.FILTER_ATTR_CLUSTER, AttributeValue.fromString(clusterName));
                            final CidrAddress cidrAddress = entry.getValue();
                            if (cidrAddress.getNetmaskBits() == 0) {
                                // historically we treat IPv4 and IPv6 any addresses as any
                                builder.removeAttribute(EJBClientContext.FILTER_ATTR_SOURCE_IP);
                            } else {
                                final AttributeValue value = AttributeValue.fromString(cidrAddress.toString());
                                builder.addAttribute(EJBClientContext.FILTER_ATTR_SOURCE_IP, value);
                            }
                            serviceURLCache.add(builder.create());
                        }
                    }

                    this.serviceURLCache = serviceURLCache;
                }
            }
        }
        return serviceURLCache;
    }


    /*
     * Take two TempInfo structures which are equivalent and merge their understanding of modules and clusters
     * Due to the way TempInfo structures are populated, these are the only combinations possible.
     */
    private void mergeEquivalentTempInfoStructures(TempInfo original, TempInfo equivalent) {
        if ((original.modules != null && equivalent.modules == null) && (original.clusters == null && equivalent.clusters != null)) {
            equivalent.modules = original.modules;
            original.clusters = equivalent.clusters;
        } else if ((original.modules == null && equivalent.modules != null) && (original.clusters != null && equivalent.clusters == null)) {
            original.modules = equivalent.modules;
            equivalent.clusters = original.clusters;
        }
    }

    /*
     * Given a URI and a set of equivalent hostnames, build a set of equivalent URIs
     */
    private Set getEquivalentURIs(URI uri, Set hostReps) {
        Set equivalentURIs = new HashSet();
        if (hostReps.size() == 1)
            return equivalentURIs;
        Set equivalentReps = new HashSet(hostReps);
        equivalentReps.remove(uri.getHost());
        for (String hostRep : equivalentReps) {
            URI equivalentURI = buildURI(uri.getScheme(), new InetSocketAddress(hostRep, uri.getPort()));
            equivalentURIs.add(equivalentURI);
        }
        return equivalentURIs;
    }

    private URI buildURI(final String protocol, final InetSocketAddress socketAddress) {
        final InetAddress address = socketAddress.getAddress();
        String hostName = Inet.getHostNameIfResolved(socketAddress);
        if (hostName == null) {
            if (address instanceof Inet6Address) {
                hostName = '[' + Inet.toOptimalString(address) + ']';
            } else {
                hostName = Inet.toOptimalString(address);
            }
        }
        try {
            return new URI(protocol, null, hostName, socketAddress.getPort(), null, null, null);
        } catch (URISyntaxException e) {
            Assert.unreachableCode();
            return null;
        }
    }

    boolean isInvalid() {
        return invalid;
    }

    void setInvalid(final boolean invalid) {
        this.invalid = invalid;
    }

    void addAddress(final String protocol, final String clusterName, final CidrAddress block, final InetSocketAddress destination) {
        synchronized (this) {
            serviceURLCache = null;
            clustersByName.computeIfAbsent(clusterName, name -> new ClusterNodeInformation())
                .getAddressTablesByProtocol()
                .computeIfAbsent(protocol, ignored -> new CidrAddressTable<>())
                .put(block, destination);
        }
    }


    void removeCluster(final String clusterName) {
        synchronized (this) {
            serviceURLCache = null;
            clustersByName.remove(clusterName);
        }
    }

    void addModules(final EJBClientChannel clientChannel, final EJBModuleIdentifier[] moduleList) {
        synchronized (this) {
            serviceURLCache = null;
            Collections.addAll(modulesByConnection.computeIfAbsent(clientChannel, ignored -> new HashSet<>()), moduleList);
        }
    }

    void removeModules(final EJBClientChannel clientChannel, final HashSet toRemove) {
        synchronized (this) {
            serviceURLCache = null;
            final Set set = modulesByConnection.get(clientChannel);
            if (set != null) {
                set.removeAll(toRemove);
            }
        }
    }

    void removeModule(final EJBClientChannel clientChannel, final EJBModuleIdentifier toRemove) {
        synchronized (this) {
            serviceURLCache = null;
            final Set set = modulesByConnection.get(clientChannel);
            if (set != null) {
                set.remove(toRemove);
            }
        }
    }

    void addAddress(EJBClientChannel clientChannel) {
        synchronized (this) {
            addressesByConnection.put(clientChannel, (InetSocketAddress) clientChannel.getChannel().getConnection().getPeerAddress());
            serviceURLCache = null;
        }
    }

    void removeConnection(EJBClientChannel clientChannel) {
        synchronized (this) {
            boolean moduleRemoved = modulesByConnection.remove(clientChannel) != null;
            boolean addressRemoved = addressesByConnection.remove(clientChannel) != null;
            if (moduleRemoved || addressRemoved) {
                serviceURLCache = null;
            }
        }
    }

    static final class ClusterNodeInformation {
        private final Map> addressTablesByProtocol = new HashMap<>();

        ClusterNodeInformation() {
        }

        Map> getAddressTablesByProtocol() {
            return addressTablesByProtocol;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy