org.jppf.server.peer.PeerDiscoveryThread Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jppf-server Show documentation
Show all versions of jppf-server Show documentation
JPPF, the open source grid computing solution
The newest version!
/*
* JPPF.
* Copyright (C) 2005-2019 JPPF Team.
* http://www.jppf.org
*
* 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.jppf.server.peer;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import org.jppf.comm.discovery.*;
import org.jppf.utils.*;
import org.jppf.utils.concurrent.ThreadSynchronization;
import org.jppf.utils.configuration.JPPFProperties;
import org.slf4j.*;
/**
* Instances of this class discover peer drivers over the network.
* @author Laurent Cohen
*/
public class PeerDiscoveryThread extends ThreadSynchronization implements Runnable {
/**
* Logger for this class.
*/
private static Logger log = LoggerFactory.getLogger(PeerDiscoveryThread.class);
/**
* Determines whether the debug level is enabled in the logging configuration, without the cost of a method call.
*/
private static boolean debugEnabled = LoggingUtils.isDebugEnabled(log);
/**
* Interval for removal cleanup.
*/
private final long removalCleanupInternal;
/**
* Contains the set of retrieved connection information objects.
*/
private final Set infoSet = new HashSet<>();
/**
* Count of distinct retrieved connection information objects.
*/
private final AtomicInteger count = new AtomicInteger(0);
/**
* Connection information for this JPPF driver.
*/
private final JPPFConnectionInformation localInfo;
/**
* Defines a callback for objects wishing to be notified of discovery events.
*/
private final ConnectionHandler connectionHandler;
/**
* Holds a set of filters to include or exclude sets of IP addresses in the discovery process.
*/
private final IPFilter ipFilter;
/**
* Holds removed entries for a limited time.
*/
private final Map removalMap = new HashMap<>();
/**
* Last time a cleanup was performed.
*/
private long lastCleanupTime = 0L;
/**
* The driver configuration.
*/
private final TypedProperties config;
/**
* Default constructor.
* @param config the driver configuration.
* @param connectionHandler handler for adding new connection
* @param ipFilter for accepted IP addresses
* @param localInfo Connection information for this JPPF driver.
*/
public PeerDiscoveryThread(final TypedProperties config, final IPFilter ipFilter, final JPPFConnectionInformation localInfo, final ConnectionHandler connectionHandler) {
if (localInfo == null) throw new IllegalArgumentException("localInfo is null");
if (connectionHandler == null) throw new IllegalArgumentException("connectionHandler is null");
this.config = config;
this.connectionHandler = connectionHandler;
this.ipFilter = ipFilter;
this.localInfo = localInfo;
this.removalCleanupInternal = config.get(JPPFProperties.PEER_DISCOVERY_REMOVAL_CLEANUP_INTERVAL);
}
/**
* Lookup server configurations from UDP multicasts.
*/
@Override
public void run() {
JPPFMulticastReceiver receiver = null;
try {
receiver = new JPPFMulticastReceiver(ipFilter);
while (!isStopped()) {
final JPPFConnectionInformation info = receiver.receive();
synchronized(this) {
if (lastCleanupTime + removalCleanupInternal >= System.currentTimeMillis()) cleanRemovals();
}
if ((info != null) && !hasConnectionInformation(info) && !wasRecentlyRemoved(info)) {
if (debugEnabled) log.debug("Found peer connection information: " + info + ", infoSet=" + infoSet);
info.recoveryEnabled &= config.get(JPPFProperties.PEER_RECOVERY_ENABLED);
addConnectionInformation(info);
onNewConnection("Peer-" + count.incrementAndGet(), info);
}
}
} catch(final Exception e) {
log.error(e.getMessage(), e);
} finally {
if (receiver != null) receiver.setStopped(true);
}
}
/**
* Add a newly found connection.
* @param name for the connection
* @param info the peer's connection information.
*/
protected synchronized void onNewConnection(final String name, final JPPFConnectionInformation info) {
connectionHandler.onNewConnection(name, info);
}
/**
* Determine whether a connection information object is already discovered.
* @param info the connection information to lookup.
* @return true if the connection information is in the map, false otherwise.
*/
protected synchronized boolean hasConnectionInformation(final JPPFConnectionInformation info) {
//return infoSet.contains(info) || info.equals(localInfo) || isSelf(info);
return infoSet.contains(info) || isSelf(info);
}
/**
* Add the specified connection information to discovered map.
* @param info a {@link JPPFConnectionInformation} instance.
*/
public synchronized void addConnectionInformation(final JPPFConnectionInformation info) {
infoSet.add(info);
}
/**
* Remove a disconnected connection.
* @param info connection info of the peer to remove
* @return whether connection was successfully removed
*/
public synchronized boolean removeConnectionInformation(final JPPFConnectionInformation info) {
removalMap.put(info.uuid, System.currentTimeMillis());
return infoSet.remove(info);
}
/**
* Determine whether the specified connection information refers to this driver.
* This situation may arise if the host has multiple network interfaces, each with its own IP address.
* Making this distinction is important to prevent a driver from connecting to itself.
* @param info the peer's connection information.
* @return true if the host/port combination in the connection information can be resolved
* as the configuration for this driver.
*/
private boolean isSelf(final JPPFConnectionInformation info) {
/*
List ipAddresses = NetworkUtils.getIPV4Addresses();
ipAddresses.addAll(NetworkUtils.getIPV6Addresses());
for (InetAddress addr: ipAddresses) {
String ip = addr.getHostAddress();
if (info.host.equals(ip) && Arrays.equals(info.serverPorts, localInfo.serverPorts)) return true;
}
return false;
*/
return info.uuid.equals(localInfo.uuid);
}
/**
* Perform cleanup of entries to remove.
*/
private synchronized void cleanRemovals() {
final long now = System.currentTimeMillis();
final List toRemove = new ArrayList<>();
for (final Map.Entry entry: removalMap.entrySet()) {
if (entry.getValue() + removalCleanupInternal <= now) toRemove.add(entry.getKey());
}
for (String uuid: toRemove) removalMap.remove(uuid);
}
/**
* Determine whether the specified information was recently removed.
* @param info the peer's connection information.
* @return {@code true} if the information was recently removed, {@code false} otherwise.
*/
private synchronized boolean wasRecentlyRemoved(final JPPFConnectionInformation info) {
return (info.uuid == null) || removalMap.containsKey(info.uuid);
}
/**
* Defines a callback for objects wishing to be notified of discovery events.
*/
@FunctionalInterface
public interface ConnectionHandler {
/**
* Called when a new connection is discovered.
* @param name the name assigned to the connection.
* @param info the information required to connect to the driver.
*/
void onNewConnection(final String name, final JPPFConnectionInformation info);
}
/**
* Contains the set of retrieved connection information objects.
* @return the list of discovered connection information.
* @exclude
*/
public Set getInfoSet() {
return infoSet;
}
}