package com.netifera.platform.net.services.auth; import java.io.IOException; import java.util.Iterator; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import com.netifera.platform.net.services.credentials.Credential; import com.netifera.platform.net.sockets.CompletionHandler; import com.netifera.platform.net.sockets.TCPChannel; import com.netifera.platform.util.locators.TCPSocketLocator; public abstract class TCPCredentialsVerifier extends CredentialsVerifier { final private TCPSocketLocator locator; final private AtomicInteger connectionsCount = new AtomicInteger(0); public TCPCredentialsVerifier(TCPSocketLocator locator) { this.locator = locator; } protected abstract void authenticate(TCPChannel channel, Credential credential, long timeout, TimeUnit unit, CompletionHandler<Boolean,Credential> handler); private void spawnConnection() throws IOException, InterruptedException { Credential credential = nextCredentialOrNull(); if (credential == null) return; final TCPChannel channel = TCPChannel.open(); connectionsCount.incrementAndGet(); channel.connect(locator, 5, TimeUnit.SECONDS, credential, new CompletionHandler<Void,Credential>() { private void closeChannel() { connectionsCount.decrementAndGet(); try { channel.close(); } catch (IOException e) { } } public void cancelled(Credential attachment) { closeChannel(); } public void completed(Void result, Credential attachment) { authenticate(channel, attachment, 8, TimeUnit.SECONDS, new CompletionHandler<Boolean,Credential>() { public void cancelled(Credential attachment) { closeChannel(); } public void completed(Boolean result, Credential attachment) { if (result) { listener.authenticationSucceeded(attachment); // close because now we're logged in, cannot login again with other credential in this connection closeChannel(); } else { listener.authenticationFailed(attachment); // HACK to deal with socket engine not notifying back when some sockets are closed // once this is fixed, we could reuse connections and try to authenticate again with the next credential if (true) { closeChannel(); return; } // and try next credential, try to reuse the connection Credential credential = nextCredentialOrNull(); if (credential == null) { closeChannel(); return; } authenticate(channel, credential, 8, TimeUnit.SECONDS, this); } } public void failed(Throwable exc, Credential attachment) { closeChannel(); listener.authenticationError(attachment, exc); // retryCredential(attachment); } }); } public void failed(Throwable exc, Credential attachment) { closeChannel(); listener.authenticationError(attachment, exc); // retryCredential(attachment); } }); } @Override public void tryCredentials(Iterator<Credential> credentials, AuthenticationListener listener) throws IOException, InterruptedException { this.credentials = credentials; this.listener = listener; while (hasNextCredential() || connectionsCount.get() > 0) { while (connectionsCount.get()>10) Thread.sleep(500); if (hasNextCredential() && !Thread.currentThread().isInterrupted()) spawnConnection(); } } }