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

com.davfx.ninio.ping.InternalPingServerReadyFactory Maven / Gradle / Ivy

package com.davfx.ninio.ping;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.davfx.ninio.common.Address;
import com.davfx.ninio.common.ClassThreadFactory;
import com.davfx.ninio.common.FailableCloseableByteBufferHandler;
import com.davfx.ninio.common.Queue;
import com.davfx.ninio.common.Ready;
import com.davfx.ninio.common.ReadyConnection;
import com.davfx.ninio.common.ReadyFactory;
import com.davfx.util.ConfigUtils;
import com.google.common.base.Charsets;
import com.google.common.primitives.Doubles;
import com.google.common.primitives.Longs;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;

public final class InternalPingServerReadyFactory implements ReadyFactory, AutoCloseable {
	private static final Config CONFIG = ConfigFactory.load();
	private static final double TIMEOUT = ConfigUtils.getDuration(CONFIG, "ping.timeout");
	private static final int MAX_SIMULTANEOUS_CLIENTS = CONFIG.getInt("ping.maxSimultaneousClients");
	
	private final SyncPing syncPing;
	private final ExecutorService clientExecutor = Executors.newFixedThreadPool(MAX_SIMULTANEOUS_CLIENTS, new ClassThreadFactory(InternalPingServerReadyFactory.class));
	private final Map> pinging = new HashMap<>();

	public InternalPingServerReadyFactory(SyncPing syncPing) {
		this.syncPing = syncPing;
	}
	
	@Override
	public void close() {
		clientExecutor.shutdown();
		synchronized (pinging) {
			pinging.clear();
		}
	}
	
	@Override
	public Ready create(final Queue queue) {
		return new Ready() {
			@Override
			public void connect(Address address, final ReadyConnection connection) {
				connection.connected(new FailableCloseableByteBufferHandler() {
					private volatile boolean closed = false;
					
					@Override
					public void close() {
						closed = true;
					}
					
					@Override
					public void failed(IOException e) {
						close();
					}
					
					@Override
					public void handle(Address address, ByteBuffer bb) {
						final long id = bb.getLong();
						int l = bb.getInt();
						byte[] hostBytes = new byte[l];
						bb.get(hostBytes);
						final String host = new String(hostBytes, Charsets.UTF_8);
					
						ping(host, new PingCallback() {
							@Override
							public void handle(double elapsed) {
								if (closed) {
									return;
								}

								final ByteBuffer s = ByteBuffer.allocate(Longs.BYTES + Doubles.BYTES);
								s.putLong(id);
								s.putDouble(elapsed);
								s.flip();
						
								queue.post(new Runnable() {
									public void run() {
										connection.handle(null, s);
									}
								});
							}
						});
					}
				});
			}
		};
	}
	
	private static interface PingCallback {
		void handle(double elapsed);
	}
	private void ping(final String host, PingCallback callback) {
		if (clientExecutor.isShutdown()) {
			return;
		}
		
		boolean launch = false;
		synchronized (pinging) {
			List callbacks = pinging.get(host);
			if (callbacks == null) {
				callbacks = new LinkedList<>();
				pinging.put(host, callbacks);
				launch = true;
			}
			callbacks.add(callback);
		}
		
		if (!launch) {
			return;
		}
		
		clientExecutor.execute(new Runnable() {
			@Override
			public void run() {
				long t = System.currentTimeMillis();
				boolean reachable = syncPing.isReachable(host, TIMEOUT);
				double elapsed = (System.currentTimeMillis() - t) / 1000d;
				if (!reachable) {
					elapsed = Double.NaN;
				}
				
				List callbacks;
				synchronized (pinging) {
					callbacks = pinging.remove(host);
				}
				if (callbacks == null) {
					return;
				}
				for (PingCallback callback : callbacks) {
					callback.handle(elapsed);
				}
			}
		});
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy