package com.limegroup.gnutella; import java.io.IOException; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.MulticastSocket; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.net.UnknownHostException; import java.util.Arrays; import java.util.Random; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.limewire.concurrent.ThreadExecutor; import org.limewire.core.api.connection.FirewallStatus; import org.limewire.core.api.connection.FirewallStatusEvent; import org.limewire.core.settings.ConnectionSettings; import org.limewire.core.settings.NetworkSettings; import org.limewire.i18n.I18nMarker; import org.limewire.inject.EagerSingleton; import org.limewire.io.IOUtils; import org.limewire.io.NetworkUtils; import org.limewire.lifecycle.Asynchronous; import org.limewire.lifecycle.Join; import org.limewire.lifecycle.Service; import org.limewire.lifecycle.ServiceRegistry; import org.limewire.listener.AsynchronousEventBroadcaster; import org.limewire.logging.Log; import org.limewire.logging.LogFactory; import org.limewire.net.AsyncConnectionDispatcher; import org.limewire.net.BlockingConnectionDispatcher; import org.limewire.net.ConnectionAcceptor; import org.limewire.net.ConnectionDispatcher; import org.limewire.nio.SocketFactory; import org.limewire.nio.channel.NIOMultiplexor; import org.limewire.nio.observer.AcceptObserver; import org.limewire.service.MessageService; import org.limewire.setting.SettingsGroupManager; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.name.Named; import com.limegroup.gnutella.filters.IPFilter; /** * Listens on ports, accepts incoming connections, and dispatches threads to * handle those connections. Currently supports Gnutella messaging, HTTP, and * chat connections over TCP; more may be supported in the future.<p> * This class has a special relationship with UDPService and should really be * the only class that intializes it. See setListeningPort() for more * info. */ @EagerSingleton public class AcceptorImpl implements ConnectionAcceptor, SocketProcessor, Acceptor, Service { private static final Log LOG = LogFactory.getLog(AcceptorImpl.class); public static final long DEFAULT_INCOMING_EXPIRE_TIME = 30 * 60 * 1000; // 30 minutes public static final long DEFAULT_WAIT_TIME_AFTER_REQUESTS = 30 * 1000; // 30 seconds public static final long DEFAULT_TIME_BETWEEN_VALIDATES = 10 * 60 * 1000; // 10 minutes // various time delays for checking of firewalled status. private long incomingExpireTime = DEFAULT_INCOMING_EXPIRE_TIME; private long waitTimeAfterRequests = DEFAULT_WAIT_TIME_AFTER_REQUESTS; private long timeBetweenValidates = DEFAULT_TIME_BETWEEN_VALIDATES; /** Task for validating incoming requests */ private final IncomingValidator incomingValidator = new IncomingValidator(); /** * The socket that listens for incoming connections. Can be changed to * listen to new ports. * * LOCKING: obtain _socketLock before modifying either. Notify _socketLock * when done. */ private volatile ServerSocket _socket=null; /** * The port of the server socket. */ private volatile int _port = 6346; /** * The real address of this host--assuming there's only one--used for pongs * and query replies. This value is ignored if FORCE_IP_ADDRESS is * true. This is initialized in three stages: * 1. Statically initialized to all zeroes. * 2. Initialized in the Acceptor thread to getLocalHost(). * 3. Initialized each time a connection is initialized to the local * address of that connection's socket. * * Why are all three needed? Step (3) is needed because (2) can often fail * due to a JDK bug #4073539, or if your address changes via DHCP. Step (2) * is needed because (3) ignores local addresses of 127.x.x.x. Step (1) is * needed because (2) can't occur in the main thread, as it may block * because the update checker is trying to resolve addresses. (See JDK bug * #4147517.) Note this may delay the time to create a listening socket by * a few seconds; big deal! * * LOCKING: obtain Acceptor.class' lock */ private byte[] _address = new byte[4]; /** * The external address. This is the address as visible from other peers. * * LOCKING: obtain Acceptor.class' lock */ private byte[] _externalAddress = new byte[4]; /** * Variable for whether or not we have accepted an incoming connection -- * used to determine firewall status. */ private volatile boolean _acceptedIncoming = false; /** * Keep track of the last time we re-validated. */ private volatile long _lastConnectBackTime = 0; /** * Whether or not this Acceptor was started. All connections accepted prior * to starting are dropped. */ private volatile boolean _started; private final Object ADDRESS_LOCK = new Object(); private final NetworkManager networkManager; private final Provider<UDPService> udpService; private final Provider<MulticastService> multicastService; private final Provider<ConnectionDispatcher> connectionDispatcher; private final ScheduledExecutorService backgroundExecutor; private final AsynchronousEventBroadcaster<FirewallStatusEvent> firewallBroadcaster; private final Provider<ConnectionManager> connectionManager; private final Provider<IPFilter> ipFilter; private final ConnectionServices connectionServices; private final Provider<UPnPManager> upnpManager; private final boolean upnpEnabled; @Inject public AcceptorImpl(NetworkManager networkManager, Provider<UDPService> udpService, Provider<MulticastService> multicastService, @Named("global") Provider<ConnectionDispatcher> connectionDispatcher, @Named("backgroundExecutor") ScheduledExecutorService backgroundExecutor, AsynchronousEventBroadcaster<FirewallStatusEvent> firewallBroadcaster, Provider<ConnectionManager> connectionManager, Provider<IPFilter> ipFilter, ConnectionServices connectionServices, Provider<UPnPManager> upnpManager) { this.networkManager = networkManager; this.udpService = udpService; this.multicastService = multicastService; this.connectionDispatcher = connectionDispatcher; this.backgroundExecutor = backgroundExecutor; this.firewallBroadcaster = firewallBroadcaster; this.connectionManager = connectionManager; this.ipFilter = ipFilter; this.connectionServices = connectionServices; this.upnpManager = upnpManager; // capture UPnP setting on construction, so start/stop can // work even if setting changes between the two. upnpEnabled = !ConnectionSettings.DISABLE_UPNP.getValue(); } /** Returns true if UPnP was enabled when Acceptor was constructed. */ private boolean isUPnPEnabled() { return upnpEnabled; } /* (non-Javadoc) * @see com.limegroup.gnutella.Acceptor#setAddress(java.net.InetAddress) */ public void setAddress(InetAddress address) { byte[] byteAddr = address.getAddress(); if( !NetworkUtils.isValidAddress(byteAddr) ) return; if( byteAddr[0] == 127 && ConnectionSettings.LOCAL_IS_PRIVATE.getValue()) { return; } boolean addrChanged = false; synchronized(ADDRESS_LOCK) { if( !Arrays.equals(_address, byteAddr) ) { _address = byteAddr; addrChanged = true; } } if(addrChanged) { LOG.infof("Setting address to {0}", address); networkManager.addressChanged(); } } /* (non-Javadoc) * @see com.limegroup.gnutella.Acceptor#setExternalAddress(java.net.InetAddress) */ public void setExternalAddress(InetAddress address) { byte[] byteAddr = address.getAddress(); if( byteAddr[0] == 127 && ConnectionSettings.LOCAL_IS_PRIVATE.getValue()) { return; } boolean addrChanged = false; synchronized(ADDRESS_LOCK) { if( !Arrays.equals(_externalAddress, byteAddr) ) { LOG.debugf("setting external address {0}", address); _externalAddress = byteAddr; addrChanged = true; } } if(addrChanged) networkManager.externalAddressChanged(); } /* (non-Javadoc) * @see com.limegroup.gnutella.Acceptor#init() */ public void bindAndStartUpnp() { int tempPort; // try a random port if we have not received an incoming connection // and have been running on the default port (6346) // and the user has not changed the settings boolean tryingRandom = NetworkSettings.PORT.isDefault() && !ConnectionSettings.EVER_ACCEPTED_INCOMING.getValue() && !ConnectionSettings.FORCE_IP_ADDRESS.getValue(); Random gen = null; if (tryingRandom) { gen = new Random(); tempPort = gen.nextInt(50000)+2000; } else tempPort = NetworkSettings.PORT.getValue(); //0. Get local address. This must be done here because it can // block under certain conditions. // See the notes for _address. try { if(isUPnPEnabled()) { if (LOG.isDebugEnabled()) LOG.debug("setting address to local address: " + NetworkUtils.getLocalAddress()); setAddress(NetworkUtils.getLocalAddress()); } else { if (LOG.isDebugEnabled()) LOG.debug("setting address to localhost: " + InetAddress.getLocalHost()); setAddress(InetAddress.getLocalHost()); } } catch (UnknownHostException e) { } catch (SecurityException e) { } // Create the server socket, bind it to a port, and listen for // incoming connections. If there are problems, we can continue // onward. //1. Try suggested port. int oldPort = tempPort; try { setListeningPort(tempPort); _port = tempPort; } catch (IOException e) { LOG.warn("can't set initial port", e); // 2. Try 20 different ports. int numToTry = 20; for (int i=0; i<numToTry; i++) { if(gen == null) gen = new Random(); tempPort = gen.nextInt(50000); tempPort += 2000;//avoid the first 2000 ports // do not try to bind to the multicast port. if (tempPort == ConnectionSettings.MULTICAST_PORT.getValue()) { numToTry++; continue; } try { setListeningPort(tempPort); _port = tempPort; break; } catch (IOException e2) { LOG.warn("can't set port", e2); } } // If we still don't have a socket, there's an error if(_socket == null) { MessageService.showError(I18nMarker.marktr("LimeWire was unable to set up a port to listen for incoming connections. Some features of LimeWire may not work as expected.")); } } if(LOG.isInfoEnabled()) LOG.info("Listening on port " + _port); if (_port != oldPort || tryingRandom) { NetworkSettings.PORT.setValue(_port); SettingsGroupManager.instance().save(); networkManager.portChanged(); } // Make sure UPnP gets setup. if(upnpManager.get().isNATPresent()) { setupUPnP(); } else { upnpManager.get().addListener(new UPnPListener() { public void natFound() { setupUPnP(); } }); } } private void setupUPnP() { // if we created a socket and have a NAT, and the user is not // explicitly forcing a port, create the mappings if (_socket != null && isUPnPEnabled()) { boolean natted = upnpManager.get().isNATPresent(); boolean validPort = NetworkUtils.isValidPort(_port); boolean forcedIP = ConnectionSettings.FORCE_IP_ADDRESS.getValue() && !ConnectionSettings.UPNP_IN_USE.getValue(); if(LOG.isDebugEnabled()) LOG.debug("Natted: " + natted + ", validPort: " + validPort + ", forcedIP: " + forcedIP); if(natted && validPort && !forcedIP) { int mappedPort = upnpManager.get().mapPort(_port, getAddress(false)); if(LOG.isDebugEnabled()) LOG.debug("UPNP port mapped: " + mappedPort); //if we created a mapping successfully, update the forced port if (mappedPort != 0 ) { // mark UPNP as being on so that if LimeWire shuts // down prematurely, we know the FORCE_IP was from UPnP // and that we can continue trying to use UPnP ConnectionSettings.FORCE_IP_ADDRESS.setValue(true); ConnectionSettings.FORCED_PORT.setValue(mappedPort); ConnectionSettings.UPNP_IN_USE.setValue(true); if (mappedPort != _port) networkManager.portChanged(); // we could get our external address from the NAT but its too slow // so we clear the last connect back times and re-validate cause our // status may have changed. resetLastConnectBackTime(); udpService.get().resetLastConnectBackTime(); if (!acceptedIncoming()) incomingValidator.run(); } } } } @Inject void register(ServiceRegistry registry) { registry.register(this); registry.register(new Service() { public String getServiceName() { return "UPnP"; } public void initialize() { } public void start() { bindAndStartUpnp(); } @Asynchronous (join = Join.TIMEOUT, timeout = 30, daemon = true) public void stop() { upnpManager.get().clearMappings(); } }).in("EarlyBackground"); } /* (non-Javadoc) * @see com.limegroup.gnutella.Acceptor#start() */ public void start() { multicastService.get().start(); udpService.get().start(); connectionDispatcher.get().addConnectionAcceptor(this, false, "CONNECT", "\n\n"); backgroundExecutor.scheduleWithFixedDelay(incomingValidator, timeBetweenValidates, timeBetweenValidates, TimeUnit.MILLISECONDS); _started = true; } public String getServiceName() { return org.limewire.i18n.I18nMarker.marktr("Connection Listener"); } public void initialize() { firewallBroadcaster.broadcast(new FirewallStatusEvent(FirewallStatus.FIREWALLED)); } public void stop() { try { setListeningPort(0); } catch(IOException ignored) {} shutdown(); } /* (non-Javadoc) * @see com.limegroup.gnutella.Acceptor#isAddressExternal() */ public boolean isAddressExternal() { if (!ConnectionSettings.LOCAL_IS_PRIVATE.getValue()) return true; synchronized(ADDRESS_LOCK) { return Arrays.equals(getAddress(true), _externalAddress); } } /* (non-Javadoc) * @see com.limegroup.gnutella.Acceptor#isBlocking() */ public boolean isBlocking() { return false; } /* (non-Javadoc) * @see com.limegroup.gnutella.Acceptor#getExternalAddress() */ public byte[] getExternalAddress() { synchronized(ADDRESS_LOCK) { return _externalAddress; } } /* (non-Javadoc) * @see com.limegroup.gnutella.Acceptor#getAddress(boolean) */ public byte[] getAddress(boolean preferForcedAddress) { if(preferForcedAddress) { if (ConnectionSettings.FORCE_IP_ADDRESS.getValue()) { String address = ConnectionSettings.FORCED_IP_ADDRESS_STRING.get(); try { InetAddress ia = InetAddress.getByName(address); byte[] addr = ia.getAddress(); return addr; } catch (UnknownHostException err) { // ignore and return _address } } else if (_acceptedIncoming) { // return valid external address as forced address if we // can accept incoming connections, to advertise the right // address to peers as a non-firewalled peer // this can happen when the firewall does port forwarding, // but the client is not explicitly configured to do it synchronized (ADDRESS_LOCK) { if (NetworkUtils.isValidAddress(_externalAddress)) { return _externalAddress; } } } } synchronized (ADDRESS_LOCK) { return _address; } } /* (non-Javadoc) * @see com.limegroup.gnutella.Acceptor#getConnectionDispatcher() */ public ConnectionDispatcher getConnectionDispatcher() { return connectionDispatcher.get(); } /* (non-Javadoc) * @see com.limegroup.gnutella.Acceptor#getPort(boolean) */ public int getPort(boolean preferForcedPort) { if(preferForcedPort && ConnectionSettings.FORCE_IP_ADDRESS.getValue()) return ConnectionSettings.FORCED_PORT.getValue(); return _port; } /* (non-Javadoc) * @see com.limegroup.gnutella.Acceptor#setListeningPort(int) */ public void setListeningPort(int port) throws IOException { //1. Special case: if unchanged, do nothing. if (_socket!=null && _port==port) return; //2. Special case if port==0. This ALWAYS works. //Note that we must close the socket BEFORE grabbing //the lock. Otherwise deadlock will occur since //the acceptor thread is listening to the socket //while holding the lock. Also note that port //will not have changed before we grab the lock. else if (port==0) { LOG.trace("shutting off service."); IOUtils.close(_socket); _socket=null; _port=0; //Shut off UDPService also! udpService.get().setListeningSocket(null); //Shut off MulticastServier too! multicastService.get().setListeningSocket(null); LOG.trace("service OFF."); return; } //3. Normal case. See note about locking above. /* Since we want the UDPService to bind to the same port as the * Acceptor, we need to be careful about this case. Essentially, we * need to confirm that the port can be bound by BOTH UDP and TCP * before actually acceping the port as valid. To effect this change, * we first attempt to bind the port for UDP traffic. If that fails, a * IOException will be thrown. If we successfully UDP bind the port * we keep that bound DatagramSocket around and try to bind the port to * TCP. If that fails, a IOException is thrown and the valid * DatagramSocket is closed. If that succeeds, we then 'commit' the * operation, setting our new TCP socket and UDP sockets. */ else { if(LOG.isDebugEnabled()) LOG.debug("changing port to " + port); DatagramSocket udpServiceSocket = udpService.get().newListeningSocket(port); LOG.trace("UDP Service is ready."); MulticastSocket mcastServiceSocket = null; try { InetAddress mgroup = InetAddress.getByName( ConnectionSettings.MULTICAST_ADDRESS.get() ); mcastServiceSocket = multicastService.get().newListeningSocket( ConnectionSettings.MULTICAST_PORT.getValue(), mgroup ); LOG.trace("multicast service setup"); } catch(IOException e) { LOG.warn("can't create multicast socket", e); } //a) Try new port. ServerSocket newSocket=null; try { newSocket = SocketFactory.newServerSocket(port, new SocketListener()); } catch (IOException e) { LOG.warn("can't create ServerSocket", e); udpServiceSocket.close(); throw e; } catch (IllegalArgumentException e) { LOG.warn("can't create ServerSocket", e); udpServiceSocket.close(); throw new IOException("could not create a listening socket"); } //b) Close old socket IOUtils.close(_socket); //c) Replace with new sock. _socket=newSocket; _port=port; LOG.trace("Acceptor ready.."); // Commit UDPService's new socket udpService.get().setListeningSocket(udpServiceSocket); // Commit the MulticastService's new socket // if we were able to get it if (mcastServiceSocket != null) { multicastService.get().setListeningSocket(mcastServiceSocket); } if(LOG.isDebugEnabled()) LOG.debug("listening UDP/TCP on " + _port); networkManager.portChanged(); } } /* (non-Javadoc) * @see com.limegroup.gnutella.Acceptor#acceptedIncoming() */ public boolean acceptedIncoming() { return _acceptedIncoming; } /** * For testing. */ protected void setAcceptedIncoming(boolean incoming) { _acceptedIncoming = incoming; } /** * Sets the new incoming status. * Returns whether or not the status changed. */ boolean setIncoming(boolean canReceiveIncoming) { synchronized(ADDRESS_LOCK) { if (canReceiveIncoming) incomingValidator.cancelReset(); if (_acceptedIncoming == canReceiveIncoming) return false; _acceptedIncoming = canReceiveIncoming; if(canReceiveIncoming) { firewallBroadcaster.broadcast(new FirewallStatusEvent(FirewallStatus.NOT_FIREWALLED)); } else { firewallBroadcaster.broadcast(new FirewallStatusEvent(FirewallStatus.FIREWALLED)); } } if(canReceiveIncoming) { ConnectionSettings.EVER_ACCEPTED_INCOMING.setValue(true); } return true; } /* (non-Javadoc) * @see com.limegroup.gnutella.Acceptor#acceptConnection(java.lang.String, java.net.Socket) */ public void acceptConnection(String word, Socket s) { checkFirewall(s.getInetAddress()); IOUtils.close(s); } /* (non-Javadoc) * @see com.limegroup.gnutella.Acceptor#checkFirewall(java.net.InetAddress) */ void checkFirewall(InetAddress address) { // we have accepted an incoming socket -- only record // that we've accepted incoming if it's definitely // not from our local subnet and we aren't connected to // the host already. boolean changed = false; if(isOutsideConnection(address)) { changed = setIncoming(true); } if(changed) networkManager.incomingStatusChanged(); } /** * Listens for new incoming sockets & starts a thread to * process them if necessary. */ private class SocketListener implements AcceptObserver { public void handleIOException(IOException iox) { LOG.warn("IOX while accepting", iox); } public void shutdown() { LOG.debug("shutdown one SocketListener"); } public void handleAccept(Socket client) { processSocket(client); } } /* (non-Javadoc) * @see com.limegroup.gnutella.SocketProcessor#processSocket(java.net.Socket) */ /* (non-Javadoc) * @see com.limegroup.gnutella.Acceptor#processSocket(java.net.Socket) */ public void processSocket(Socket client) { processSocket(client, null); } /* (non-Javadoc) * @see com.limegroup.gnutella.SocketProcessor#processSocket(java.net.Socket, java.lang.String) */ /* (non-Javadoc) * @see com.limegroup.gnutella.Acceptor#processSocket(java.net.Socket, java.lang.String) */ public void processSocket(Socket client, String allowedProtocol) { if (!_started) { IOUtils.close(client); return; } // If the client was closed before we were able to get the address, // then getInetAddress will return null. InetAddress address = client.getInetAddress(); if (address == null || !NetworkUtils.isValidAddress(address) || !NetworkUtils.isValidPort(client.getPort())) { IOUtils.close(client); LOG.warn("connection closed while accepting"); } else if (!ipFilter.get().allow(address.getAddress())) { if (LOG.isWarnEnabled()) LOG.warn("Ignoring banned host: " + address); IOUtils.close(client); } else { if (LOG.isDebugEnabled()) LOG.debug("Dispatching new client connecton: " + address); // Set our IP address of the local address of this socket. InetAddress localAddress = client.getLocalAddress(); setAddress(localAddress); try { client.setSoTimeout(Constants.TIMEOUT); } catch (SocketException se) { IOUtils.close(client); return; } // Dispatch asynchronously if possible. if (client instanceof NIOMultiplexor) {// supports non-blocking reads ((NIOMultiplexor) client).setReadObserver(new AsyncConnectionDispatcher(connectionDispatcher.get(), client, allowedProtocol, networkManager.isIncomingTLSEnabled())); } else { ThreadExecutor.startThread(new BlockingConnectionDispatcher(connectionDispatcher .get(), client, allowedProtocol), "ConnectionDispatchRunner"); } } } /** * Determines whether or not this INetAddress is found an outside source, so as to correctly set "acceptedIncoming" * to true. * * This ignores connections from private or local addresses, ignores those who may be on the same subnet, and * ignores those who we are already connected to. */ private boolean isOutsideConnection(InetAddress addr) { // short-circuit for tests. if(!ConnectionSettings.LOCAL_IS_PRIVATE.getValue()) return true; return !connectionServices.isConnectedTo(addr) && !NetworkUtils.isLocalAddress(addr); } /** * Resets the last connectback time. */ public void resetLastConnectBackTime() { _lastConnectBackTime = 0; // long ago } /* (non-Javadoc) * @see com.limegroup.gnutella.Acceptor#shutdown() */ public void shutdown() { shutdownUPnP(); } private void shutdownUPnP() { if(isUPnPEnabled() && upnpManager.get().isNATPresent() && upnpManager.get().mappingsExist() && ConnectionSettings.UPNP_IN_USE.getValue()) { // reset the forced port values - must happen before we save them to disk ConnectionSettings.FORCE_IP_ADDRESS.revertToDefault(); ConnectionSettings.FORCED_PORT.revertToDefault(); ConnectionSettings.UPNP_IN_USE.revertToDefault(); } } /** * (Re)validates acceptedIncoming. */ private class IncomingValidator implements Runnable { private final AtomicBoolean validating = new AtomicBoolean(false); private AtomicReference<Future<?>> futureRef = new AtomicReference<Future<?>>(); public void run() { if (validating.getAndSet(true)) { LOG.debug("Attempt to validate while already validating, aborting check"); return; } // clear and revalidate if we haven't done so in a while final long currTime = System.currentTimeMillis(); if (currTime - _lastConnectBackTime > incomingExpireTime) { LOG.debug("Time elapsed -- triggering TCP connectbacks"); // send a connectback request to a few peers and clear // _acceptedIncoming IF some requests were sent. if(connectionManager.get().sendTCPConnectBackRequests()) { LOG.debug("Sent TCP connectbacks, scheduling unset of accept-incoming"); _lastConnectBackTime = currTime; Runnable resetter = new Runnable() { public void run() { boolean changed = setIncoming(false); if(changed) networkManager.incomingStatusChanged(); } }; // Cancel any old future before we schedule this one Future<?> oldRef = futureRef.get(); if(oldRef != null) oldRef.cancel(false); futureRef.set(backgroundExecutor.schedule(resetter, waitTimeAfterRequests, TimeUnit.MILLISECONDS)); } } validating.set(false); } void cancelReset() { Future<?> resetter = futureRef.get(); if (resetter != null) { resetter.cancel(false); // unset the ref if it's still the current future futureRef.compareAndSet(resetter, null); } } } public long getIncomingExpireTime() { return incomingExpireTime; } /** * Only used for testing. */ void setIncomingExpireTime(long incomingExpireTime) { this.incomingExpireTime = incomingExpireTime; } public long getWaitTimeAfterRequests() { return waitTimeAfterRequests; } /** * Only used for testing. */ void setWaitTimeAfterRequests(long waitTimeAfterRequests) { this.waitTimeAfterRequests = waitTimeAfterRequests; } public long getTimeBetweenValidates() { return timeBetweenValidates; } void setTimeBetweenValidates(long timeBetweenValidates) { this.timeBetweenValidates = timeBetweenValidates; } }