diff --git a/src/net/java/sip/communicator/impl/dns/ParallelResolverImpl.java b/src/net/java/sip/communicator/impl/dns/ParallelResolverImpl.java index d0859f830..2e3f39369 100644 --- a/src/net/java/sip/communicator/impl/dns/ParallelResolverImpl.java +++ b/src/net/java/sip/communicator/impl/dns/ParallelResolverImpl.java @@ -10,6 +10,7 @@ import java.io.*; import java.net.*; import java.util.*; +import java.util.concurrent.*; import net.java.sip.communicator.service.dns.*; import net.java.sip.communicator.util.*; @@ -43,13 +44,13 @@ public class ParallelResolverImpl * class and its instances for logging output. */ private static final Logger logger = Logger - .getLogger(ParallelResolverImpl.class.getName()); + .getLogger(ParallelResolverImpl.class); /** * Indicates whether we are currently in a mode where all DNS queries are * sent to both the primary and the backup DNS servers. */ - private static boolean redundantMode = false; + private volatile static boolean redundantMode = false; /** * The currently configured number of milliseconds that we need to wait @@ -87,11 +88,15 @@ public class ParallelResolverImpl */ private ExtendedResolver backupResolver; + /** Thread pool that processes the backup queries. */ + private ExecutorService backupQueriesPool; + /** * Creates a new instance of this class. */ ParallelResolverImpl() { + backupQueriesPool = Executors.newCachedThreadPool(); DnsUtilActivator.getConfigurationService() .addPropertyChangeListener(this); initProperties(); @@ -117,9 +122,9 @@ private void initProperties() } catch(UnknownHostException exc) { - logger.warn("Oh! Seems like our primary DNS is down!" - + "Don't panic! We'll try to fall back to " - + customResolverIP); + logger + .warn("Seems like the primary DNS is down, trying fallback to " + + customResolverIP); } if(resolverAddress == null) @@ -178,7 +183,7 @@ private void setBackupServers(InetSocketAddress[] backupServers) //this shouldn't be thrown since we don't do any DNS querying //in here. this is why we take an InetSocketAddress as a param. throw new IllegalStateException("The impossible just happened: " - +"we could not initialize our backup DNS resolver"); + + "we could not initialize our backup DNS resolver"); } } @@ -193,23 +198,12 @@ private void setBackupServers(InetSocketAddress[] backupServers) public Message send(Message query) throws IOException { - ParallelResolution resolution = new ParallelResolution(query); - resolution.sendFirstQuery(); - //make a copy of the redundant mode variable in case we are currently - //completed a redemption that started earlier. - boolean redundantModeCopy; - - synchronized(redemptionLock) - { - redundantModeCopy = redundantMode; - } - //if we are not in redundant mode we should wait a bit and see how this //goes. if we get a reply we could return bravely. - if(!redundantModeCopy) + if(!redundantMode) { if(resolution.waitForResponse(currentDnsPatience)) { @@ -222,16 +216,17 @@ public Message send(Message query) { redundantMode = true; redemptionStatus = currentDnsRedemption; - logger.info("Primary DNS seems laggy as we got no " - +"response for " + currentDnsPatience + "ms. " - + "Enabling redundant mode."); + logger.info("Primary DNS seems laggy: " + + "no response for " + query.getQuestion().getName() + + "/" + Type.string(query.getQuestion().getType()) + + " after " + currentDnsPatience + "ms. " + + "Enabling redundant mode."); } } } //we are definitely in redundant mode now resolution.sendBackupQueries(); - resolution.waitForResponse(0); //check if it is time to end redundant mode. @@ -270,7 +265,7 @@ public Message send(Message query) */ public Object sendAsync(final Message query, final ResolverListener listener) { - return null; + throw new UnsupportedOperationException("Not implemented"); } /** @@ -448,7 +443,7 @@ private boolean isResponseSatisfactory(Message response) * our default and backup servers and returns as soon as we get one or until * our default resolver fails. */ - private class ParallelResolution extends Thread + private class ParallelResolution implements Runnable { /** * The query that we have sent to the default and backup DNS servers. @@ -459,7 +454,7 @@ private class ParallelResolution extends Thread * The field where we would store the first incoming response to our * query. */ - public Message response; + private volatile Message response; /** * The field where we would store the first error we receive from a DNS @@ -470,12 +465,12 @@ private class ParallelResolution extends Thread /** * Indicates whether we are still waiting for an answer from someone */ - private boolean done = false; + private volatile boolean done = false; /** * Indicates that a response was received from the primary resolver. */ - private boolean primaryResolverRespondedFirst = true; + private volatile boolean primaryResolverRespondedFirst = true; /** * Creates a {@link ParallelResolution} for the specified query @@ -485,7 +480,6 @@ private class ParallelResolution extends Thread */ public ParallelResolution(final Message query) { - super("ParallelResolutionThread"); this.query = query; } @@ -495,7 +489,7 @@ public ParallelResolution(final Message query) */ public void sendFirstQuery() { - start(); + ParallelResolverImpl.this.backupQueriesPool.execute(this); } /** @@ -509,19 +503,24 @@ public void run() { localResponse = defaultResolver.send(query); } + catch (SocketTimeoutException exc) + { + logger.info("Default DNS resolver timed out."); + this.exception = exc; + } catch (Throwable exc) { - logger.info("Exception occurred during parallel DNS resolving" + - exc, exc); + logger.info("Default DNS resolver failed", exc); this.exception = exc; } + + //if the backup resolvers had already replied we ignore the + //reply of the primary one whatever it was. + if(done) + return; + synchronized(this) { - //if the backup resolvers had already replied we ignore the - //reply of the primary one whatever it was. - if(done) - return; - //if there was a response we're only done if it is satisfactory if( localResponse != null && isResponseSatisfactory(localResponse)) @@ -529,6 +528,7 @@ && isResponseSatisfactory(localResponse)) response = localResponse; done = true; } + notify(); } } @@ -538,47 +538,58 @@ && isResponseSatisfactory(localResponse)) */ public void sendBackupQueries() { - logger.info("Send DNS queries to backup resolvers"); - //yes. a second thread in the thread ... it's ugly but it works //and i do want to keep code simple to read ... this whole parallel //resolving is complicated enough as it is. - new Thread(){ + backupQueriesPool.execute(new Runnable(){ @Override public void run() { - synchronized(ParallelResolution.this) + if (done) { - if (done) - return; - Message localResponse = null; - try - { - localResponse = backupResolver.send(query); - } - catch (Throwable exc) - { - logger.info("Exception occurred during backup " - +"DNS resolving" + exc); + return; + } - //keep this so that we can rethrow it - exception = exc; - } - //if the default resolver has already replied we - //ignore the reply of the backup ones. - if(done) - return; + Message localResponse = null; + try + { + logger.info("Sending query for " + + query.getQuestion().getName() + "/" + + Type.string(query.getQuestion().getType()) + + " to backup resolvers"); + localResponse = backupResolver.send(query); + } + catch (Throwable exc) + { + logger.info("Exception occurred during backup " + +"DNS resolving" + exc); + + //keep this so that we can rethrow it + exception = exc; + } + //if the default resolver has already replied we + //ignore the reply of the backup ones. + if(done) + { + return; + } + synchronized(ParallelResolution.this) + { //contrary to responses from the primary resolver, //in this case we don't care whether the response is //satisfying: if it isn't, there's nothing we can do - response = localResponse; - done = true; + if (response == null) + { + response = localResponse; + primaryResolverRespondedFirst = false; + } + done = true; ParallelResolution.this.notify(); } } - }.start(); + }); } /** @@ -636,6 +647,11 @@ public Message returnResponseOrThrowUp() { return response; } + else if (exception instanceof SocketTimeoutException) + { + logger.warn("DNS resolver timed out"); + throw (IOException) exception; + } else if (exception instanceof IOException) { logger.warn("IO exception while using DNS resolver", exception); @@ -664,6 +680,7 @@ else if (exception instanceof Error) @SuppressWarnings("serial") private final Set configNames = new HashSet(5) {{ + add(DnsUtilActivator.PNAME_BACKUP_RESOLVER_ENABLED); add(DnsUtilActivator.PNAME_BACKUP_RESOLVER); add(DnsUtilActivator.PNAME_BACKUP_RESOLVER_FALLBACK_IP); add(DnsUtilActivator.PNAME_BACKUP_RESOLVER_PORT);