From 1122bdc9598606fbac165c5eca551cf55c15c048 Mon Sep 17 00:00:00 2001
From: "christian.oestreich" <christian.oestreich@bluestembrands.com>
Date: Wed, 1 Jul 2015 14:00:54 -0500
Subject: [PATCH 1/3] Adding sentinel support for config and pooling

---
 pom.xml                                       |   4 +
 .../java/net/greghaines/jesque/Config.java    | 322 +++++++++-------
 .../net/greghaines/jesque/ConfigBuilder.java  | 353 ++++++++++--------
 .../greghaines/jesque/utils/PoolUtils.java    | 244 ++++++------
 .../greghaines/jesque/TestConfigBuilder.java  | 348 +++++++++--------
 .../jesque/utils/TestPoolUtils.java           |  30 +-
 6 files changed, 721 insertions(+), 580 deletions(-)

diff --git a/pom.xml b/pom.xml
index 150181fd..57917828 100644
--- a/pom.xml
+++ b/pom.xml
@@ -80,6 +80,10 @@
             <url>http://animesh.org/</url>
             <timezone>Asia/Kolkata</timezone>
         </contributor>
+        <contributor>
+            <name>Christian Oestreich</name>
+            <url>https://github.com/ctoestreich</url>
+        </contributor>
         <contributor>
             <name>David Parkinson</name>
             <url>https://github.com/dparkinson</url>
diff --git a/src/main/java/net/greghaines/jesque/Config.java b/src/main/java/net/greghaines/jesque/Config.java
index 073b14e6..d1480d39 100644
--- a/src/main/java/net/greghaines/jesque/Config.java
+++ b/src/main/java/net/greghaines/jesque/Config.java
@@ -15,10 +15,11 @@
  */
 package net.greghaines.jesque;
 
-import java.io.Serializable;
-
 import net.greghaines.jesque.utils.JesqueUtils;
 
+import java.io.Serializable;
+import java.util.Set;
+
 /**
  * An immutable configuration bean for use with the rest of the project.
  *
@@ -27,145 +28,180 @@
  */
 public class Config implements Serializable {
 
-    private static final long serialVersionUID = -6638770587683679373L;
-
-    private final String host;
-    private final int port;
-    private final int timeout;
-    private final String password;
-    private final String namespace;
-    private final int database;
-
-    /**
-     * Using a ConfigBuilder is recommended...
-     *
-     * @param host
-     *            the Reds hostname
-     * @param port
-     *            the Redis port number
-     * @param timeout
-     *            the Redis connection timeout
-     * @param password
-     *            the Redis database password
-     * @param namespace
-     *            the Redis namespace to prefix keys with
-     * @param database
-     *            the Redis database to use
-     * @see ConfigBuilder
-     */
-    public Config(final String host, final int port, final int timeout, final String password, final String namespace,
-            final int database) {
-        if (host == null || "".equals(host)) {
-            throw new IllegalArgumentException("host must not be null or empty: " + host);
-        }
-        if (port < 1 || port > 65535) {
-            throw new IllegalArgumentException("post must be a valid port in the range 1-65535: " + port);
-        }
-        if (timeout < 0) {
-            throw new IllegalArgumentException("timeout must not be negative: " + timeout);
-        }
-        if (namespace == null) {
-            throw new IllegalArgumentException("namespace must not be null");
-        }
-        if (database < 0) {
-            throw new IllegalArgumentException("database must not be negative: " + database);
-        }
-        this.host = host;
-        this.port = port;
-        this.timeout = timeout;
-        this.password = password;
-        this.namespace = namespace;
-        this.database = database;
-    }
-
-    /**
-     * @return the Redis hostname
-     */
-    public String getHost() {
-        return this.host;
-    }
-
-    /**
-     * @return the Redis port number
-     */
-    public int getPort() {
-        return this.port;
-    }
-
-    /**
-     * @return the Redis connection timeout
-     */
-    public int getTimeout() {
-        return this.timeout;
-    }
-
-    /**
-     * @return the Redis password
-     */
-    public String getPassword() {
-        return this.password;
-    }
-
-    /**
-     * @return the Redis namespace to prefix keys with
-     */
-    public String getNamespace() {
-        return this.namespace;
-    }
-
-    /**
-     * @return the Redis database to use
-     */
-    public int getDatabase() {
-        return this.database;
-    }
-
-    /**
-     * @return the Redis protocol URI this Config will connect to
-     */
-    public String getURI() {
-        return "redis://" + this.host + ":" + this.port + "/" + this.database;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public String toString() {
-        return "<" + getURI() + " namespace=" + this.namespace + " timeout=" + this.timeout + ">";
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + this.database;
-        result = prime * result + ((this.host == null) ? 0 : this.host.hashCode());
-        result = prime * result + ((this.namespace == null) ? 0 : this.namespace.hashCode());
-        result = prime * result + this.port;
-        result = prime * result + this.timeout;
-        return result;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean equals(final Object obj) {
-        boolean equal = false;
-        if (this == obj) {
-            equal = true;
-        } else if (obj instanceof Config) {
-            final Config other = (Config) obj;
-            equal = ((this.database == other.database)
-                    && (this.port == other.port)
-                    && (this.timeout == other.timeout)
-                    && JesqueUtils.nullSafeEquals(this.host, other.host)
-                    && JesqueUtils.nullSafeEquals(this.namespace, other.namespace));
-        }
-        return equal;
-    }
+	private static final long serialVersionUID = -6638770587683679373L;
+
+	private final String host;
+	private final int port;
+	private final int timeout;
+	private final String password;
+	private final String namespace;
+	private final int database;
+	private final Set<String> sentinels;
+	private final String masterName;
+
+	/**
+	 * Using a ConfigBuilder is recommended...
+	 *
+	 * @param host      the Reds hostname
+	 * @param port      the Redis port number
+	 * @param timeout   the Redis connection timeout
+	 * @param password  the Redis database password
+	 * @param namespace the Redis namespace to prefix keys with
+	 * @param database  the Redis database to use
+	 * @see ConfigBuilder
+	 */
+	public Config(final String host, final int port, final int timeout, final String password, final String namespace, final int database) {
+		if (host == null || "".equals(host)) {
+			throw new IllegalArgumentException("host must not be null or empty: " + host);
+		}
+		if (port < 1 || port > 65535) {
+			throw new IllegalArgumentException("post must be a valid port in the range 1-65535: " + port);
+		}
+		if (timeout < 0) {
+			throw new IllegalArgumentException("timeout must not be negative: " + timeout);
+		}
+		if (namespace == null) {
+			throw new IllegalArgumentException("namespace must not be null");
+		}
+		if (database < 0) {
+			throw new IllegalArgumentException("database must not be negative: " + database);
+		}
+		this.host = host;
+		this.port = port;
+		this.timeout = timeout;
+		this.password = password;
+		this.namespace = namespace;
+		this.database = database;
+		this.sentinels = null;
+		this.masterName = null;
+	}
+
+	/**
+	 * Using a ConfigBuilder is recommended...
+	 *
+	 * @param sentinels  the Redis set of sentinels
+	 * @param masterName the Redis master name
+	 * @param timeout    the Redis connection timeout
+	 * @param password   the Redis database password
+	 * @param namespace  the Redis namespace to prefix keys with
+	 * @param database   the Redis database to use
+	 * @see ConfigBuilder
+	 */
+	public Config(final Set<String> sentinels, final String masterName, final int timeout, final String password, final String namespace, final int database) {
+		if (sentinels == null || sentinels.size() < 1) {
+			throw new IllegalArgumentException("sentinels must not be null or empty: " + sentinels);
+		}
+		if (masterName == null || "".equals(masterName)) {
+			throw new IllegalArgumentException("master must not be null or empty: " + masterName);
+		}
+		if (timeout < 0) {
+			throw new IllegalArgumentException("timeout must not be negative: " + timeout);
+		}
+		if (namespace == null) {
+			throw new IllegalArgumentException("namespace must not be null");
+		}
+		if (database < 0) {
+			throw new IllegalArgumentException("database must not be negative: " + database);
+		}
+		this.sentinels = sentinels;
+		this.masterName = masterName;
+		this.timeout = timeout;
+		this.password = password;
+		this.namespace = namespace;
+		this.database = database;
+		this.host = ConfigBuilder.DEFAULT_HOST;
+		this.port = ConfigBuilder.DEFAULT_PORT;
+	}
+
+	/**
+	 * @return the Redis hostname
+	 */
+	public String getHost() {
+		return this.host;
+	}
+
+	/**
+	 * @return the Redis port number
+	 */
+	public int getPort() {
+		return this.port;
+	}
+
+	/**
+	 * @return the Redis connection timeout
+	 */
+	public int getTimeout() {
+		return this.timeout;
+	}
+
+	/**
+	 * @return the Redis password
+	 */
+	public String getPassword() {
+		return this.password;
+	}
+
+	/**
+	 * @return the Redis namespace to prefix keys with
+	 */
+	public String getNamespace() {
+		return this.namespace;
+	}
+
+	/**
+	 * @return the Redis database to use
+	 */
+	public int getDatabase() {
+		return this.database;
+	}
+
+	public Set<String> getSentinels() { return this.sentinels; }
+
+	public String getMasterName() { return this.masterName; }
+
+	/**
+	 * @return the Redis protocol URI this Config will connect to
+	 */
+	public String getURI() {
+		return "redis://" + this.host + ":" + this.port + "/" + this.database;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toString() {
+		return "<" + getURI() + " namespace=" + this.namespace + " timeout=" + this.timeout + ">";
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + this.database;
+		result = prime * result + ((this.host == null) ? 0 : this.host.hashCode());
+		result = prime * result + ((this.namespace == null) ? 0 : this.namespace.hashCode());
+		result = prime * result + this.port;
+		result = prime * result + this.timeout;
+		return result;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean equals(final Object obj) {
+		boolean equal = false;
+		if (this == obj) {
+			equal = true;
+		} else if (obj instanceof Config) {
+			final Config other = (Config) obj;
+			equal = ((this.database == other.database) && (this.port == other.port) && (this.timeout == other.timeout) && JesqueUtils.nullSafeEquals(this.host, other.host)
+					&& JesqueUtils.nullSafeEquals(this.namespace, other.namespace));
+		}
+		return equal;
+	}
 }
diff --git a/src/main/java/net/greghaines/jesque/ConfigBuilder.java b/src/main/java/net/greghaines/jesque/ConfigBuilder.java
index e3d416eb..312f7121 100644
--- a/src/main/java/net/greghaines/jesque/ConfigBuilder.java
+++ b/src/main/java/net/greghaines/jesque/ConfigBuilder.java
@@ -16,6 +16,8 @@
 package net.greghaines.jesque;
 
 import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * A fluent-style builder for {@link Config}s.
@@ -25,155 +27,204 @@
  */
 public class ConfigBuilder implements Serializable {
 
-    private static final long serialVersionUID = 730947307298353317L;
-
-    /** localhost */
-    public static final String DEFAULT_HOST = "localhost";
-    /** 6379 */
-    public static final int DEFAULT_PORT = 6379;
-    /** 5 seconds */
-    public static final int DEFAULT_TIMEOUT = 5000;
-    /** null */
-    public static final String DEFAULT_PASSWORD = null;
-    /** All resque clients use "resque" by default */
-    public static final String DEFAULT_NAMESPACE = "resque";
-    /** 0 */
-    public static final int DEFAULT_DATABASE = 0;
-
-    /**
-     * @return a Config with all the default values set
-     */
-    public static Config getDefaultConfig() {
-        return new ConfigBuilder().build();
-    }
-
-    private String host = DEFAULT_HOST;
-    private int port = DEFAULT_PORT;
-    private int timeout = DEFAULT_TIMEOUT;
-    private String password = DEFAULT_PASSWORD;
-    private String namespace = DEFAULT_NAMESPACE;
-    private int database = DEFAULT_DATABASE;
-
-    /**
-     * No-arg constructor.
-     */
-    public ConfigBuilder() {
-        // Do nothing
-    }
-
-    /**
-     * Create a new ConfigBuilder using an existing Config as the starting
-     * point.
-     *
-     * @param startingPoint
-     *            the Config instance to copy the values from
-     */
-    public ConfigBuilder(final Config startingPoint) {
-        if (startingPoint == null) {
-            throw new IllegalArgumentException("startingPoint must not be null");
-        }
-        this.host = startingPoint.getHost();
-        this.port = startingPoint.getPort();
-        this.timeout = startingPoint.getTimeout();
-        this.namespace = startingPoint.getNamespace();
-        this.database = startingPoint.getDatabase();
-    }
-
-    /**
-     * Configs created by this ConfigBuilder will have the given Redis hostname.
-     *
-     * @param host
-     *            the Redis hostname
-     * @return this ConfigBuilder
-     */
-    public ConfigBuilder withHost(final String host) {
-        if (host == null || "".equals(host)) {
-            throw new IllegalArgumentException("host must not be null or empty: " + host);
-        }
-        this.host = host;
-        return this;
-    }
-
-    /**
-     * Configs created by this ConfigBuilder will have the given Redis port
-     * number.
-     *
-     * @param port
-     *            the Redis port number
-     * @return this ConfigBuilder
-     */
-    public ConfigBuilder withPort(final int port) {
-        if (port < 1 || port > 65535) {
-            throw new IllegalArgumentException("post must be a valid port in the range 1-65535: " + port);
-        }
-        this.port = port;
-        return this;
-    }
-
-    /**
-     * Configs created by this ConfigBuilder will have the given Redis
-     * connection timeout.
-     *
-     * @param timeout
-     *            the Redis connection timeout
-     * @return this ConfigBuilder
-     */
-    public ConfigBuilder withTimeout(final int timeout) {
-        if (timeout < 0) {
-            throw new IllegalArgumentException("timeout must not be negative: " + timeout);
-        }
-        this.timeout = timeout;
-        return this;
-    }
-
-    /**
-     * Configs created by this ConfigBuilder will authenticate with the given
-     * Redis password.
-     *
-     * @param password
-     *            the Redis password
-     * @return this ConfigBuilder
-     */
-    public ConfigBuilder withPassword(final String password) {
-        this.password = password;
-        return this;
-    }
-
-    /**
-     * Configs created by this ConfigBuilder will have the given Redis namespace
-     * to prefix keys with.
-     *
-     * @param namespace
-     *            the Redis namespace to prefix keys with
-     * @return this ConfigBuilder
-     */
-    public ConfigBuilder withNamespace(final String namespace) {
-        if (namespace == null) {
-            throw new IllegalArgumentException("namespace must not be null");
-        }
-        this.namespace = namespace;
-        return this;
-    }
-
-    /**
-     * Configs created by this ConfigBuilder will use the given Redis database.
-     *
-     * @param database
-     *            the Redis database to use
-     * @return this ConfigBuilder
-     */
-    public ConfigBuilder withDatabase(final int database) {
-        if (database < 0) {
-            throw new IllegalArgumentException("database must not be negative: " + database);
-        }
-        this.database = database;
-        return this;
-    }
-
-    /**
-     * @return a new Config initialized with the current values
-     */
-    public Config build() {
-        return new Config(this.host, this.port, this.timeout, this.password, this.namespace, this.database);
-    }
+	private static final long serialVersionUID = 730947307298353317L;
+
+	/**
+	 * localhost
+	 */
+	public static final String DEFAULT_HOST = "localhost";
+	/**
+	 * 6379
+	 */
+	public static final int DEFAULT_PORT = 6379;
+	/**
+	 * 5 seconds
+	 */
+	public static final int DEFAULT_TIMEOUT = 5000;
+	/**
+	 * null
+	 */
+	public static final String DEFAULT_PASSWORD = null;
+	/**
+	 * All resque clients use "resque" by default
+	 */
+	public static final String DEFAULT_NAMESPACE = "resque";
+	/**
+	 * null
+	 **/
+	public static final HashSet<String> DEFAULT_SENTINELS = null;
+	/**
+	 * null
+	 **/
+	public static final String DEFAULT_MASTERNAME = null;
+	/**
+	 * 0
+	 */
+	public static final int DEFAULT_DATABASE = 0;
+
+	/**
+	 * @return a Config with all the default values set
+	 */
+	public static Config getDefaultConfig() {
+		return new ConfigBuilder().build();
+	}
+
+	private String host = DEFAULT_HOST;
+	private int port = DEFAULT_PORT;
+	private int timeout = DEFAULT_TIMEOUT;
+	private String password = DEFAULT_PASSWORD;
+	private String namespace = DEFAULT_NAMESPACE;
+	private Set<String> sentinels = DEFAULT_SENTINELS;
+	private String masterName = DEFAULT_MASTERNAME;
+	private int database = DEFAULT_DATABASE;
+
+	/**
+	 * No-arg constructor.
+	 */
+	public ConfigBuilder() {
+		// Do nothing
+	}
+
+	/**
+	 * Create a new ConfigBuilder using an existing Config as the starting
+	 * point.
+	 *
+	 * @param startingPoint the Config instance to copy the values from
+	 */
+	public ConfigBuilder(final Config startingPoint) {
+		if (startingPoint == null) {
+			throw new IllegalArgumentException("startingPoint must not be null");
+		}
+		this.host = startingPoint.getHost();
+		this.port = startingPoint.getPort();
+		this.timeout = startingPoint.getTimeout();
+		this.namespace = startingPoint.getNamespace();
+		this.database = startingPoint.getDatabase();
+		this.sentinels = startingPoint.getSentinels();
+		this.masterName = startingPoint.getMasterName();
+	}
+
+	/**
+	 * Configs created by this ConfigBuilder will have the given Redis hostname.
+	 *
+	 * @param host the Redis hostname
+	 * @return this ConfigBuilder
+	 */
+	public ConfigBuilder withHost(final String host) {
+		if (host == null || "".equals(host)) {
+			throw new IllegalArgumentException("host must not be null or empty: " + host);
+		}
+		this.host = host;
+		return this;
+	}
+
+	/**
+	 * Configs created by this ConfigBuilder will have the given Redis port
+	 * number.
+	 *
+	 * @param port the Redis port number
+	 * @return this ConfigBuilder
+	 */
+	public ConfigBuilder withPort(final int port) {
+		if (port < 1 || port > 65535) {
+			throw new IllegalArgumentException("post must be a valid port in the range 1-65535: " + port);
+		}
+		this.port = port;
+		return this;
+	}
+
+	/**
+	 * Configs created by this ConfigBuilder will have the given Redis
+	 * connection timeout.
+	 *
+	 * @param timeout the Redis connection timeout
+	 * @return this ConfigBuilder
+	 */
+	public ConfigBuilder withTimeout(final int timeout) {
+		if (timeout < 0) {
+			throw new IllegalArgumentException("timeout must not be negative: " + timeout);
+		}
+		this.timeout = timeout;
+		return this;
+	}
+
+	/**
+	 * Configs created by this ConfigBuilder will authenticate with the given
+	 * Redis password.
+	 *
+	 * @param password the Redis password
+	 * @return this ConfigBuilder
+	 */
+	public ConfigBuilder withPassword(final String password) {
+		this.password = password;
+		return this;
+	}
+
+	/**
+	 * Configs created by this ConfigBuilder will have the given Redis namespace
+	 * to prefix keys with.
+	 *
+	 * @param namespace the Redis namespace to prefix keys with
+	 * @return this ConfigBuilder
+	 */
+	public ConfigBuilder withNamespace(final String namespace) {
+		if (namespace == null) {
+			throw new IllegalArgumentException("namespace must not be null");
+		}
+		this.namespace = namespace;
+		return this;
+	}
+
+	/**
+	 * Configs created by this ConfigBuilder will use the given Redis database.
+	 *
+	 * @param database the Redis database to use
+	 * @return this ConfigBuilder
+	 */
+	public ConfigBuilder withDatabase(final int database) {
+		if (database < 0) {
+			throw new IllegalArgumentException("database must not be negative: " + database);
+		}
+		this.database = database;
+		return this;
+	}
+
+	/**
+	 * Configs created by this ConfigBuilder will use the given Redis sentinels.
+	 *
+	 * @param sentinels the Redis set of sentinels
+	 * @return this ConfigBuilder
+	 */
+	public ConfigBuilder withSentinels(final Set<String> sentinels) {
+		if (sentinels == null || sentinels.size() < 1) {
+			throw new IllegalArgumentException("sentinels is null or empty: " + sentinels);
+		}
+		this.sentinels = sentinels;
+		return this;
+	}
+
+	/**
+	 * Configs created by this ConfigBuilder will use the given Redis master name.
+	 *
+	 * @param masterName the Redis set of sentinels
+	 * @return this ConfigBuilder
+	 */
+	public ConfigBuilder withMasterName(final String masterName) {
+		if (masterName == null || "".equals(masterName)) {
+			throw new IllegalArgumentException("masterName is null or empty: " + masterName);
+		}
+		this.masterName = masterName;
+		return this;
+	}
+
+	/**
+	 * @return a new Config initialized with the current values
+	 */
+	public Config build() {
+		if (this.sentinels != null && this.sentinels.size() > 0 && masterName != null && !"".equals(masterName)) {
+			return new Config(this.sentinels, this.masterName, this.timeout, this.password, this.namespace, this.database);
+		} else {
+			return new Config(this.host, this.port, this.timeout, this.password, this.namespace, this.database);
+		}
+	}
 }
diff --git a/src/main/java/net/greghaines/jesque/utils/PoolUtils.java b/src/main/java/net/greghaines/jesque/utils/PoolUtils.java
index 7526d3a6..2806e2eb 100644
--- a/src/main/java/net/greghaines/jesque/utils/PoolUtils.java
+++ b/src/main/java/net/greghaines/jesque/utils/PoolUtils.java
@@ -16,149 +16,137 @@
 package net.greghaines.jesque.utils;
 
 import net.greghaines.jesque.Config;
-
 import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
-
 import redis.clients.jedis.Jedis;
 import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisSentinelPool;
 import redis.clients.util.Pool;
 
 /**
  * Convenience methods for doing work with pooled resources.
- * 
+ *
  * @author Greg Haines
  */
 public final class PoolUtils {
 
-    /**
-     * Perform the given work with a resource from the given pool.
-     * 
-     * @param pool
-     *            the resource pool
-     * @param work
-     *            the work to perform
-     * @param <T>
-     *            the resource type
-     * @param <V>
-     *            the result type
-     * @return the result of the given work
-     * @throws Exception
-     *             if something went wrong
-     */
-    public static <T, V> V doWorkInPool(final Pool<T> pool, final PoolWork<T, V> work) throws Exception {
-        if (pool == null) {
-            throw new IllegalArgumentException("pool must not be null");
-        }
-        if (work == null) {
-            throw new IllegalArgumentException("work must not be null");
-        }
-        final V result;
-        final T poolResource = pool.getResource();
-        try {
-            result = work.doWork(poolResource);
-        } finally {
-            pool.returnResource(poolResource);
-        }
-        return result;
-    }
+	/**
+	 * Perform the given work with a resource from the given pool.
+	 *
+	 * @param pool the resource pool
+	 * @param work the work to perform
+	 * @param <T>  the resource type
+	 * @param <V>  the result type
+	 * @return the result of the given work
+	 * @throws Exception if something went wrong
+	 */
+	public static <T, V> V doWorkInPool(final Pool<T> pool, final PoolWork<T, V> work) throws Exception {
+		if (pool == null) {
+			throw new IllegalArgumentException("pool must not be null");
+		}
+		if (work == null) {
+			throw new IllegalArgumentException("work must not be null");
+		}
+		final V result;
+		final T poolResource = pool.getResource();
+		try {
+			result = work.doWork(poolResource);
+		} finally {
+			pool.returnResource(poolResource);
+		}
+		return result;
+	}
 
-    /**
-     * Perform the given work with a resource from the given pool. Wraps any
-     * thrown checked exceptions in a RuntimeException.
-     * 
-     * @param pool
-     *            the resource pool
-     * @param work
-     *            the work to perform
-     * @param <T>
-     *            the resource type
-     * @param <V>
-     *            the result type
-     * @return the result of the given work
-     */
-    public static <T, V> V doWorkInPoolNicely(final Pool<T> pool, final PoolWork<T, V> work) {
-        final V result;
-        try {
-            result = doWorkInPool(pool, work);
-        } catch (RuntimeException re) {
-            throw re;
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-        return result;
-    }
+	/**
+	 * Perform the given work with a resource from the given pool. Wraps any
+	 * thrown checked exceptions in a RuntimeException.
+	 *
+	 * @param pool the resource pool
+	 * @param work the work to perform
+	 * @param <T>  the resource type
+	 * @param <V>  the result type
+	 * @return the result of the given work
+	 */
+	public static <T, V> V doWorkInPoolNicely(final Pool<T> pool, final PoolWork<T, V> work) {
+		final V result;
+		try {
+			result = doWorkInPool(pool, work);
+		} catch (RuntimeException re) {
+			throw re;
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+		return result;
+	}
 
-    /**
-     * @return a GenericObjectPoolConfig configured with: maxActive=-1,
-     *         maxIdle=10, minIdle=1, testOnBorrow=true,
-     *         blockWhenExhausted=false
-     */
-    public static GenericObjectPoolConfig getDefaultPoolConfig() {
-        final GenericObjectPoolConfig cfg = new GenericObjectPoolConfig();
-        cfg.setMaxTotal(-1); // Infinite
-        cfg.setMaxIdle(10);
-        cfg.setMinIdle(1);
-        cfg.setTestOnBorrow(true);
-        cfg.setBlockWhenExhausted(false);
-        return cfg;
-    }
+	/**
+	 * @return a GenericObjectPoolConfig configured with: maxActive=-1,
+	 * maxIdle=10, minIdle=1, testOnBorrow=true,
+	 * blockWhenExhausted=false
+	 */
+	public static GenericObjectPoolConfig getDefaultPoolConfig() {
+		final GenericObjectPoolConfig cfg = new GenericObjectPoolConfig();
+		cfg.setMaxTotal(-1); // Infinite
+		cfg.setMaxIdle(10);
+		cfg.setMinIdle(1);
+		cfg.setTestOnBorrow(true);
+		cfg.setBlockWhenExhausted(false);
+		return cfg;
+	}
 
-    /**
-     * A simple helper method that creates a pool of connections to Redis using
-     * the supplied Config and the default pool config.
-     * 
-     * @param jesqueConfig
-     *            the config used to create the pooled Jedis connection
-     * @return a configured Pool of Jedis connections
-     */
-    public static Pool<Jedis> createJedisPool(final Config jesqueConfig) {
-        return createJedisPool(jesqueConfig, getDefaultPoolConfig());
-    }
+	/**
+	 * A simple helper method that creates a pool of connections to Redis using
+	 * the supplied Config and the default pool config.
+	 *
+	 * @param jesqueConfig the config used to create the pooled Jedis connection
+	 * @return a configured Pool of Jedis connections
+	 */
+	public static Pool<Jedis> createJedisPool(final Config jesqueConfig) {
+		return createJedisPool(jesqueConfig, getDefaultPoolConfig());
+	}
 
-    /**
-     * A simple helper method that creates a pool of connections to Redis using
-     * the supplied configurations.
-     * 
-     * @param jesqueConfig
-     *            the config used to create the pooled Jedis connections
-     * @param poolConfig
-     *            the config used to create the pool
-     * @return a configured Pool of Jedis connections
-     */
-    public static Pool<Jedis> createJedisPool(final Config jesqueConfig, final GenericObjectPoolConfig poolConfig) {
-        if (jesqueConfig == null) {
-            throw new IllegalArgumentException("jesqueConfig must not be null");
-        }
-        if (poolConfig == null) {
-            throw new IllegalArgumentException("poolConfig must not be null");
-        }
-        return new JedisPool(poolConfig, jesqueConfig.getHost(), jesqueConfig.getPort(), jesqueConfig.getTimeout(), jesqueConfig.getPassword());
-    }
+	/**
+	 * A simple helper method that creates a pool of connections to Redis using
+	 * the supplied configurations.
+	 *
+	 * @param jesqueConfig the config used to create the pooled Jedis connections
+	 * @param poolConfig   the config used to create the pool
+	 * @return a configured Pool of Jedis connections
+	 */
+	public static Pool<Jedis> createJedisPool(final Config jesqueConfig, final GenericObjectPoolConfig poolConfig) {
+		if (jesqueConfig == null) {
+			throw new IllegalArgumentException("jesqueConfig must not be null");
+		}
+		if (poolConfig == null) {
+			throw new IllegalArgumentException("poolConfig must not be null");
+		}
+		if (jesqueConfig.getMasterName() != null && !"".equals(jesqueConfig.getMasterName()) && jesqueConfig.getSentinels() != null
+				&& jesqueConfig.getSentinels().size() > 0) {
+			return new JedisSentinelPool(jesqueConfig.getMasterName(), jesqueConfig.getSentinels(), poolConfig, jesqueConfig.getTimeout(),
+					jesqueConfig.getPassword());
+		} else {
+			return new JedisPool(poolConfig, jesqueConfig.getHost(), jesqueConfig.getPort(), jesqueConfig.getTimeout(), jesqueConfig.getPassword());
+		}
+	}
 
-    /**
-     * A unit of work that utilizes a pooled resource.
-     * 
-     * @author Greg Haines
-     * 
-     * @param <T>
-     *            the kind of pooled resource used
-     * @param <V>
-     *            the kind of result returned
-     */
-    public interface PoolWork<T, V> {
-        /**
-         * Do work with a pooled resource and return a result.
-         * 
-         * @param poolResource
-         *            the pooled resource
-         * @return the result of the work done
-         * @throws Exception
-         *             in case something goes wrong
-         */
-        V doWork(T poolResource) throws Exception;
-    }
+	/**
+	 * A unit of work that utilizes a pooled resource.
+	 *
+	 * @param <T> the kind of pooled resource used
+	 * @param <V> the kind of result returned
+	 * @author Greg Haines
+	 */
+	public interface PoolWork<T, V> {
+		/**
+		 * Do work with a pooled resource and return a result.
+		 *
+		 * @param poolResource the pooled resource
+		 * @return the result of the work done
+		 * @throws Exception in case something goes wrong
+		 */
+		V doWork(T poolResource) throws Exception;
+	}
 
-    private PoolUtils() {
-        // Utility class
-    }
+	private PoolUtils() {
+		// Utility class
+	}
 }
diff --git a/src/test/java/net/greghaines/jesque/TestConfigBuilder.java b/src/test/java/net/greghaines/jesque/TestConfigBuilder.java
index f4053ce0..b490e433 100644
--- a/src/test/java/net/greghaines/jesque/TestConfigBuilder.java
+++ b/src/test/java/net/greghaines/jesque/TestConfigBuilder.java
@@ -3,163 +3,197 @@
 import org.junit.Assert;
 import org.junit.Test;
 
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
 public class TestConfigBuilder {
 
-    @Test
-    public void testGetDefaultConfig() {
-        final Config config = ConfigBuilder.getDefaultConfig();
-        Assert.assertNotNull(config);
-        Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
-    }
-
-    @Test
-    public void testConstructor_NoArg() {
-        final Config config = new ConfigBuilder().build();
-        Assert.assertNotNull(config);
-        Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
-    }
-
-    @Test
-    public void testConstructor_Cloning() {
-        final Config orig = new ConfigBuilder().withNamespace("foo")
-                .withDatabase(10).withPassword("bar").withPort(123)
-                .withHost("abc.com").withTimeout(10000).build();
-        final Config copy = new ConfigBuilder(orig).build();
-        TestUtils.assertFullyEquals(orig, copy);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testConstructor_Cloning_Null() {
-        new ConfigBuilder(null);
-    }
-
-    @Test
-    public void testWithHost() {
-        final String myHost = "foobar";
-        final Config config = new ConfigBuilder().withHost(myHost).build();
-        Assert.assertNotNull(config);
-        Assert.assertEquals(myHost, config.getHost());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testWithHost_Null() {
-        new ConfigBuilder().withHost(null);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testWithHost_Empty() {
-        new ConfigBuilder().withHost("");
-    }
-
-    @Test
-    public void testWithPort() {
-        final int myPort = 1234;
-        final Config config = new ConfigBuilder().withPort(myPort).build();
-        Assert.assertNotNull(config);
-        Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
-        Assert.assertEquals(myPort, config.getPort());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testWithPort_Low() {
-        new ConfigBuilder().withPort(0);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testWithPort_High() {
-        new ConfigBuilder().withPort(Integer.MAX_VALUE);
-    }
-
-    @Test
-    public void testWithTimeout() {
-        final int myTimeout = 77777;
-        final Config config = new ConfigBuilder().withTimeout(myTimeout).build();
-        Assert.assertNotNull(config);
-        Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
-        Assert.assertEquals(myTimeout, config.getTimeout());
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testWithTimeout_Negative() {
-        new ConfigBuilder().withTimeout(-1);
-    }
-
-    @Test
-    public void testWithDatabase() {
-        final int myDB = 2;
-        final Config config = new ConfigBuilder().withDatabase(myDB).build();
-        Assert.assertNotNull(config);
-        Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
-        Assert.assertEquals(myDB, config.getDatabase());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testWithDatabase_Negative() {
-        new ConfigBuilder().withDatabase(-1);
-    }
-
-    @Test
-    public void testWithPassword() {
-        final String myPassword = "s3r3t5!";
-        final Config config = new ConfigBuilder().withPassword(myPassword).build();
-        Assert.assertNotNull(config);
-        Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
-        Assert.assertEquals(myPassword, config.getPassword());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
-    }
-
-    @Test
-    public void testWithNamespace() {
-        final String myNamespace = "foo";
-        final Config config = new ConfigBuilder().withNamespace(myNamespace).build();
-        Assert.assertNotNull(config);
-        Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
-        Assert.assertEquals(myNamespace, config.getNamespace());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
-        Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testWithNamespace_Null() {
-        new ConfigBuilder().withNamespace(null);
-    }
-
-    @Test
-    public void testWithNamespace_Empty() {
-        final String myNamespace = "";
-        final Config config = new ConfigBuilder().withNamespace(myNamespace).build();
-        Assert.assertEquals(myNamespace, config.getNamespace());
-    }
+	@Test
+	public void testGetDefaultConfig() {
+		final Config config = ConfigBuilder.getDefaultConfig();
+		Assert.assertNotNull(config);
+		Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
+	}
+
+	@Test
+	public void testConstructor_NoArg() {
+		final Config config = new ConfigBuilder().build();
+		Assert.assertNotNull(config);
+		Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
+	}
+
+	@Test
+	public void testConstructor_Cloning() {
+		final Config orig = new ConfigBuilder().withNamespace("foo").withDatabase(10).withPassword("bar").withPort(123).withHost("abc.com").withTimeout(10000).build();
+		final Config copy = new ConfigBuilder(orig).build();
+		TestUtils.assertFullyEquals(orig, copy);
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testConstructor_Cloning_Null() {
+		new ConfigBuilder(null);
+	}
+
+	@Test
+	public void testWithHost() {
+		final String myHost = "foobar";
+		final Config config = new ConfigBuilder().withHost(myHost).build();
+		Assert.assertNotNull(config);
+		Assert.assertEquals(myHost, config.getHost());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testWithHost_Null() {
+		new ConfigBuilder().withHost(null);
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testWithHost_Empty() {
+		new ConfigBuilder().withHost("");
+	}
+
+	@Test
+	public void testWithPort() {
+		final int myPort = 1234;
+		final Config config = new ConfigBuilder().withPort(myPort).build();
+		Assert.assertNotNull(config);
+		Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
+		Assert.assertEquals(myPort, config.getPort());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testWithPort_Low() {
+		new ConfigBuilder().withPort(0);
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testWithPort_High() {
+		new ConfigBuilder().withPort(Integer.MAX_VALUE);
+	}
+
+	@Test
+	public void testWithTimeout() {
+		final int myTimeout = 77777;
+		final Config config = new ConfigBuilder().withTimeout(myTimeout).build();
+		Assert.assertNotNull(config);
+		Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
+		Assert.assertEquals(myTimeout, config.getTimeout());
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testWithTimeout_Negative() {
+		new ConfigBuilder().withTimeout(-1);
+	}
+
+	@Test
+	public void testWithDatabase() {
+		final int myDB = 2;
+		final Config config = new ConfigBuilder().withDatabase(myDB).build();
+		Assert.assertNotNull(config);
+		Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
+		Assert.assertEquals(myDB, config.getDatabase());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testWithDatabase_Negative() {
+		new ConfigBuilder().withDatabase(-1);
+	}
+
+	@Test
+	public void testWithPassword() {
+		final String myPassword = "s3r3t5!";
+		final Config config = new ConfigBuilder().withPassword(myPassword).build();
+		Assert.assertNotNull(config);
+		Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
+		Assert.assertEquals(myPassword, config.getPassword());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
+	}
+
+	@Test
+	public void testWithNamespace() {
+		final String myNamespace = "foo";
+		final Config config = new ConfigBuilder().withNamespace(myNamespace).build();
+		Assert.assertNotNull(config);
+		Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
+		Assert.assertEquals(myNamespace, config.getNamespace());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
+	}
+
+	@Test
+	public void testWithMasterNameAndSentinels() {
+		final String myMasterName = "foo";
+		final Set<String> mySentinels = new HashSet<>(Arrays.asList("a", "b"));
+		final Config config = new ConfigBuilder().withSentinels(mySentinels).withMasterName(myMasterName).build();
+		Assert.assertNotNull(config);
+		Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
+		Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
+		Assert.assertEquals(mySentinels, config.getSentinels());
+		Assert.assertEquals(myMasterName, config.getMasterName());
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testWithNamespace_Null() {
+		new ConfigBuilder().withNamespace(null);
+	}
+
+	@Test
+	public void testWithNamespace_Empty() {
+		final String myNamespace = "";
+		final Config config = new ConfigBuilder().withNamespace(myNamespace).build();
+		Assert.assertEquals(myNamespace, config.getNamespace());
+	}
 }
diff --git a/src/test/java/net/greghaines/jesque/utils/TestPoolUtils.java b/src/test/java/net/greghaines/jesque/utils/TestPoolUtils.java
index 7218ed8b..5ec1a13b 100644
--- a/src/test/java/net/greghaines/jesque/utils/TestPoolUtils.java
+++ b/src/test/java/net/greghaines/jesque/utils/TestPoolUtils.java
@@ -10,10 +10,17 @@
 import org.jmock.lib.legacy.ClassImposteriser;
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisSentinelPool;
 import redis.clients.util.Pool;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+
 public class TestPoolUtils {
     
     private Mockery mockCtx;
@@ -100,7 +107,28 @@ public void testCreateJedisPool_NullConfig() {
     @Test
     public void testCreateJedisPool() {
         final Config config = new ConfigBuilder().build();
-        Assert.assertNotNull(PoolUtils.createJedisPool(config));
+        Pool pool = PoolUtils.createJedisPool(config);
+        Assert.assertNotNull(pool);
+        Assert.assertTrue(pool instanceof JedisPool);
+    }
+
+    /**
+     * This will need a sentinel running with the following config
+
+     sentinel monitor mymaster 127.0.0.1 6379 1
+     sentinel down-after-milliseconds mymaster 6000
+     sentinel failover-timeout mymaster 180000
+     sentinel parallel-syncs mymaster 1
+
+     You should also have a redis-server running to act as the master [mymaster]
+     */
+    @Test
+    @Ignore("Will only work with sentinel running and travis-ci sentinel support is sketchy at best")
+    public void testCreateJedisSentinelPool() {
+        final Config config = new ConfigBuilder().withMasterName("mymaster").withSentinels(new HashSet<String>(Collections.singletonList("localhost:26379"))).build();
+        Pool pool = PoolUtils.createJedisPool(config);
+        Assert.assertNotNull(pool);
+        Assert.assertTrue(pool instanceof JedisSentinelPool);
     }
     
     @Test(expected = IllegalArgumentException.class)

From d4858f504a082fcd5b5ed9b91e4ce834e06863d0 Mon Sep 17 00:00:00 2001
From: "christian.oestreich" <christian.oestreich@bluestembrands.com>
Date: Thu, 2 Jul 2015 13:51:22 -0500
Subject: [PATCH 2/3] Adding = and # for new fields

---
 src/main/java/net/greghaines/jesque/Config.java | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/main/java/net/greghaines/jesque/Config.java b/src/main/java/net/greghaines/jesque/Config.java
index d1480d39..ec7eebd0 100644
--- a/src/main/java/net/greghaines/jesque/Config.java
+++ b/src/main/java/net/greghaines/jesque/Config.java
@@ -182,8 +182,10 @@ public int hashCode() {
 		final int prime = 31;
 		int result = 1;
 		result = prime * result + this.database;
-		result = prime * result + ((this.host == null) ? 0 : this.host.hashCode());
-		result = prime * result + ((this.namespace == null) ? 0 : this.namespace.hashCode());
+		result = prime * result + (this.host.hashCode());
+		result = prime * result + (this.namespace.hashCode());
+		result = prime * result + ((this.sentinels == null) ? 0 : this.sentinels.hashCode());
+		result = prime * result + ((this.masterName == null) ? 0 : this.masterName.hashCode());
 		result = prime * result + this.port;
 		result = prime * result + this.timeout;
 		return result;
@@ -200,7 +202,7 @@ public boolean equals(final Object obj) {
 		} else if (obj instanceof Config) {
 			final Config other = (Config) obj;
 			equal = ((this.database == other.database) && (this.port == other.port) && (this.timeout == other.timeout) && JesqueUtils.nullSafeEquals(this.host, other.host)
-					&& JesqueUtils.nullSafeEquals(this.namespace, other.namespace));
+					&& JesqueUtils.nullSafeEquals(this.namespace, other.namespace)) && JesqueUtils.nullSafeEquals(this.sentinels, other.sentinels) && JesqueUtils.nullSafeEquals(this.masterName, other.masterName);
 		}
 		return equal;
 	}

From e2dee03f3209f2cc37b5af4b2b611eaf64e5940b Mon Sep 17 00:00:00 2001
From: "christian.oestreich" <christian.oestreich@bluestembrands.com>
Date: Mon, 6 Jul 2015 07:06:11 -0500
Subject: [PATCH 3/3] Converting tabs back to white spaces

---
 .../java/net/greghaines/jesque/Config.java    | 356 +++++++--------
 .../net/greghaines/jesque/ConfigBuilder.java  | 412 +++++++++---------
 .../greghaines/jesque/utils/PoolUtils.java    | 228 +++++-----
 .../greghaines/jesque/TestConfigBuilder.java  | 374 ++++++++--------
 4 files changed, 677 insertions(+), 693 deletions(-)

diff --git a/src/main/java/net/greghaines/jesque/Config.java b/src/main/java/net/greghaines/jesque/Config.java
index ec7eebd0..6d0eb9b2 100644
--- a/src/main/java/net/greghaines/jesque/Config.java
+++ b/src/main/java/net/greghaines/jesque/Config.java
@@ -28,182 +28,182 @@
  */
 public class Config implements Serializable {
 
-	private static final long serialVersionUID = -6638770587683679373L;
-
-	private final String host;
-	private final int port;
-	private final int timeout;
-	private final String password;
-	private final String namespace;
-	private final int database;
-	private final Set<String> sentinels;
-	private final String masterName;
-
-	/**
-	 * Using a ConfigBuilder is recommended...
-	 *
-	 * @param host      the Reds hostname
-	 * @param port      the Redis port number
-	 * @param timeout   the Redis connection timeout
-	 * @param password  the Redis database password
-	 * @param namespace the Redis namespace to prefix keys with
-	 * @param database  the Redis database to use
-	 * @see ConfigBuilder
-	 */
-	public Config(final String host, final int port, final int timeout, final String password, final String namespace, final int database) {
-		if (host == null || "".equals(host)) {
-			throw new IllegalArgumentException("host must not be null or empty: " + host);
-		}
-		if (port < 1 || port > 65535) {
-			throw new IllegalArgumentException("post must be a valid port in the range 1-65535: " + port);
-		}
-		if (timeout < 0) {
-			throw new IllegalArgumentException("timeout must not be negative: " + timeout);
-		}
-		if (namespace == null) {
-			throw new IllegalArgumentException("namespace must not be null");
-		}
-		if (database < 0) {
-			throw new IllegalArgumentException("database must not be negative: " + database);
-		}
-		this.host = host;
-		this.port = port;
-		this.timeout = timeout;
-		this.password = password;
-		this.namespace = namespace;
-		this.database = database;
-		this.sentinels = null;
-		this.masterName = null;
-	}
-
-	/**
-	 * Using a ConfigBuilder is recommended...
-	 *
-	 * @param sentinels  the Redis set of sentinels
-	 * @param masterName the Redis master name
-	 * @param timeout    the Redis connection timeout
-	 * @param password   the Redis database password
-	 * @param namespace  the Redis namespace to prefix keys with
-	 * @param database   the Redis database to use
-	 * @see ConfigBuilder
-	 */
-	public Config(final Set<String> sentinels, final String masterName, final int timeout, final String password, final String namespace, final int database) {
-		if (sentinels == null || sentinels.size() < 1) {
-			throw new IllegalArgumentException("sentinels must not be null or empty: " + sentinels);
-		}
-		if (masterName == null || "".equals(masterName)) {
-			throw new IllegalArgumentException("master must not be null or empty: " + masterName);
-		}
-		if (timeout < 0) {
-			throw new IllegalArgumentException("timeout must not be negative: " + timeout);
-		}
-		if (namespace == null) {
-			throw new IllegalArgumentException("namespace must not be null");
-		}
-		if (database < 0) {
-			throw new IllegalArgumentException("database must not be negative: " + database);
-		}
-		this.sentinels = sentinels;
-		this.masterName = masterName;
-		this.timeout = timeout;
-		this.password = password;
-		this.namespace = namespace;
-		this.database = database;
-		this.host = ConfigBuilder.DEFAULT_HOST;
-		this.port = ConfigBuilder.DEFAULT_PORT;
-	}
-
-	/**
-	 * @return the Redis hostname
-	 */
-	public String getHost() {
-		return this.host;
-	}
-
-	/**
-	 * @return the Redis port number
-	 */
-	public int getPort() {
-		return this.port;
-	}
-
-	/**
-	 * @return the Redis connection timeout
-	 */
-	public int getTimeout() {
-		return this.timeout;
-	}
-
-	/**
-	 * @return the Redis password
-	 */
-	public String getPassword() {
-		return this.password;
-	}
-
-	/**
-	 * @return the Redis namespace to prefix keys with
-	 */
-	public String getNamespace() {
-		return this.namespace;
-	}
-
-	/**
-	 * @return the Redis database to use
-	 */
-	public int getDatabase() {
-		return this.database;
-	}
-
-	public Set<String> getSentinels() { return this.sentinels; }
-
-	public String getMasterName() { return this.masterName; }
-
-	/**
-	 * @return the Redis protocol URI this Config will connect to
-	 */
-	public String getURI() {
-		return "redis://" + this.host + ":" + this.port + "/" + this.database;
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public String toString() {
-		return "<" + getURI() + " namespace=" + this.namespace + " timeout=" + this.timeout + ">";
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public int hashCode() {
-		final int prime = 31;
-		int result = 1;
-		result = prime * result + this.database;
-		result = prime * result + (this.host.hashCode());
-		result = prime * result + (this.namespace.hashCode());
-		result = prime * result + ((this.sentinels == null) ? 0 : this.sentinels.hashCode());
-		result = prime * result + ((this.masterName == null) ? 0 : this.masterName.hashCode());
-		result = prime * result + this.port;
-		result = prime * result + this.timeout;
-		return result;
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public boolean equals(final Object obj) {
-		boolean equal = false;
-		if (this == obj) {
-			equal = true;
-		} else if (obj instanceof Config) {
-			final Config other = (Config) obj;
-			equal = ((this.database == other.database) && (this.port == other.port) && (this.timeout == other.timeout) && JesqueUtils.nullSafeEquals(this.host, other.host)
-					&& JesqueUtils.nullSafeEquals(this.namespace, other.namespace)) && JesqueUtils.nullSafeEquals(this.sentinels, other.sentinels) && JesqueUtils.nullSafeEquals(this.masterName, other.masterName);
-		}
-		return equal;
-	}
+    private static final long serialVersionUID = -6638770587683679373L;
+
+    private final String host;
+    private final int port;
+    private final int timeout;
+    private final String password;
+    private final String namespace;
+    private final int database;
+    private final Set<String> sentinels;
+    private final String masterName;
+
+    /**
+     * Using a ConfigBuilder is recommended...
+     *
+     * @param host      the Reds hostname
+     * @param port      the Redis port number
+     * @param timeout   the Redis connection timeout
+     * @param password  the Redis database password
+     * @param namespace the Redis namespace to prefix keys with
+     * @param database  the Redis database to use
+     * @see ConfigBuilder
+     */
+    public Config(final String host, final int port, final int timeout, final String password, final String namespace, final int database) {
+        if (host == null || "".equals(host)) {
+            throw new IllegalArgumentException("host must not be null or empty: " + host);
+        }
+        if (port < 1 || port > 65535) {
+            throw new IllegalArgumentException("post must be a valid port in the range 1-65535: " + port);
+        }
+        if (timeout < 0) {
+            throw new IllegalArgumentException("timeout must not be negative: " + timeout);
+        }
+        if (namespace == null) {
+            throw new IllegalArgumentException("namespace must not be null");
+        }
+        if (database < 0) {
+            throw new IllegalArgumentException("database must not be negative: " + database);
+        }
+        this.host = host;
+        this.port = port;
+        this.timeout = timeout;
+        this.password = password;
+        this.namespace = namespace;
+        this.database = database;
+        this.sentinels = null;
+        this.masterName = null;
+    }
+
+    /**
+     * Using a ConfigBuilder is recommended...
+     *
+     * @param sentinels  the Redis set of sentinels
+     * @param masterName the Redis master name
+     * @param timeout    the Redis connection timeout
+     * @param password   the Redis database password
+     * @param namespace  the Redis namespace to prefix keys with
+     * @param database   the Redis database to use
+     * @see ConfigBuilder
+     */
+    public Config(final Set<String> sentinels, final String masterName, final int timeout, final String password, final String namespace, final int database) {
+        if (sentinels == null || sentinels.size() < 1) {
+            throw new IllegalArgumentException("sentinels must not be null or empty: " + sentinels);
+        }
+        if (masterName == null || "".equals(masterName)) {
+            throw new IllegalArgumentException("master must not be null or empty: " + masterName);
+        }
+        if (timeout < 0) {
+            throw new IllegalArgumentException("timeout must not be negative: " + timeout);
+        }
+        if (namespace == null) {
+            throw new IllegalArgumentException("namespace must not be null");
+        }
+        if (database < 0) {
+            throw new IllegalArgumentException("database must not be negative: " + database);
+        }
+        this.sentinels = sentinels;
+        this.masterName = masterName;
+        this.timeout = timeout;
+        this.password = password;
+        this.namespace = namespace;
+        this.database = database;
+        this.host = ConfigBuilder.DEFAULT_HOST;
+        this.port = ConfigBuilder.DEFAULT_PORT;
+    }
+
+    /**
+     * @return the Redis hostname
+     */
+    public String getHost() {
+        return this.host;
+    }
+
+    /**
+     * @return the Redis port number
+     */
+    public int getPort() {
+        return this.port;
+    }
+
+    /**
+     * @return the Redis connection timeout
+     */
+    public int getTimeout() {
+        return this.timeout;
+    }
+
+    /**
+     * @return the Redis password
+     */
+    public String getPassword() {
+        return this.password;
+    }
+
+    /**
+     * @return the Redis namespace to prefix keys with
+     */
+    public String getNamespace() {
+        return this.namespace;
+    }
+
+    /**
+     * @return the Redis database to use
+     */
+    public int getDatabase() {
+        return this.database;
+    }
+
+    public Set<String> getSentinels() { return this.sentinels; }
+
+    public String getMasterName() { return this.masterName; }
+
+    /**
+     * @return the Redis protocol URI this Config will connect to
+     */
+    public String getURI() {
+        return "redis://" + this.host + ":" + this.port + "/" + this.database;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        return "<" + getURI() + " namespace=" + this.namespace + " timeout=" + this.timeout + ">";
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + this.database;
+        result = prime * result + (this.host.hashCode());
+        result = prime * result + (this.namespace.hashCode());
+        result = prime * result + ((this.sentinels == null) ? 0 : this.sentinels.hashCode());
+        result = prime * result + ((this.masterName == null) ? 0 : this.masterName.hashCode());
+        result = prime * result + this.port;
+        result = prime * result + this.timeout;
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        boolean equal = false;
+        if (this == obj) {
+            equal = true;
+        } else if (obj instanceof Config) {
+            final Config other = (Config) obj;
+            equal = ((this.database == other.database) && (this.port == other.port) && (this.timeout == other.timeout) && JesqueUtils.nullSafeEquals(this.host, other.host)
+                    && JesqueUtils.nullSafeEquals(this.namespace, other.namespace)) && JesqueUtils.nullSafeEquals(this.sentinels, other.sentinels) && JesqueUtils.nullSafeEquals(this.masterName, other.masterName);
+        }
+        return equal;
+    }
 }
diff --git a/src/main/java/net/greghaines/jesque/ConfigBuilder.java b/src/main/java/net/greghaines/jesque/ConfigBuilder.java
index 312f7121..59c3e5ca 100644
--- a/src/main/java/net/greghaines/jesque/ConfigBuilder.java
+++ b/src/main/java/net/greghaines/jesque/ConfigBuilder.java
@@ -1,18 +1,18 @@
 /*
- * Copyright 2011 Greg Haines
- *
- * 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.
- */
+* Copyright 2011 Greg Haines
+*
+* 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 net.greghaines.jesque;
 
 import java.io.Serializable;
@@ -27,204 +27,188 @@
  */
 public class ConfigBuilder implements Serializable {
 
-	private static final long serialVersionUID = 730947307298353317L;
-
-	/**
-	 * localhost
-	 */
-	public static final String DEFAULT_HOST = "localhost";
-	/**
-	 * 6379
-	 */
-	public static final int DEFAULT_PORT = 6379;
-	/**
-	 * 5 seconds
-	 */
-	public static final int DEFAULT_TIMEOUT = 5000;
-	/**
-	 * null
-	 */
-	public static final String DEFAULT_PASSWORD = null;
-	/**
-	 * All resque clients use "resque" by default
-	 */
-	public static final String DEFAULT_NAMESPACE = "resque";
-	/**
-	 * null
-	 **/
-	public static final HashSet<String> DEFAULT_SENTINELS = null;
-	/**
-	 * null
-	 **/
-	public static final String DEFAULT_MASTERNAME = null;
-	/**
-	 * 0
-	 */
-	public static final int DEFAULT_DATABASE = 0;
-
-	/**
-	 * @return a Config with all the default values set
-	 */
-	public static Config getDefaultConfig() {
-		return new ConfigBuilder().build();
-	}
-
-	private String host = DEFAULT_HOST;
-	private int port = DEFAULT_PORT;
-	private int timeout = DEFAULT_TIMEOUT;
-	private String password = DEFAULT_PASSWORD;
-	private String namespace = DEFAULT_NAMESPACE;
-	private Set<String> sentinels = DEFAULT_SENTINELS;
-	private String masterName = DEFAULT_MASTERNAME;
-	private int database = DEFAULT_DATABASE;
-
-	/**
-	 * No-arg constructor.
-	 */
-	public ConfigBuilder() {
-		// Do nothing
-	}
-
-	/**
-	 * Create a new ConfigBuilder using an existing Config as the starting
-	 * point.
-	 *
-	 * @param startingPoint the Config instance to copy the values from
-	 */
-	public ConfigBuilder(final Config startingPoint) {
-		if (startingPoint == null) {
-			throw new IllegalArgumentException("startingPoint must not be null");
-		}
-		this.host = startingPoint.getHost();
-		this.port = startingPoint.getPort();
-		this.timeout = startingPoint.getTimeout();
-		this.namespace = startingPoint.getNamespace();
-		this.database = startingPoint.getDatabase();
-		this.sentinels = startingPoint.getSentinels();
-		this.masterName = startingPoint.getMasterName();
-	}
-
-	/**
-	 * Configs created by this ConfigBuilder will have the given Redis hostname.
-	 *
-	 * @param host the Redis hostname
-	 * @return this ConfigBuilder
-	 */
-	public ConfigBuilder withHost(final String host) {
-		if (host == null || "".equals(host)) {
-			throw new IllegalArgumentException("host must not be null or empty: " + host);
-		}
-		this.host = host;
-		return this;
-	}
-
-	/**
-	 * Configs created by this ConfigBuilder will have the given Redis port
-	 * number.
-	 *
-	 * @param port the Redis port number
-	 * @return this ConfigBuilder
-	 */
-	public ConfigBuilder withPort(final int port) {
-		if (port < 1 || port > 65535) {
-			throw new IllegalArgumentException("post must be a valid port in the range 1-65535: " + port);
-		}
-		this.port = port;
-		return this;
-	}
-
-	/**
-	 * Configs created by this ConfigBuilder will have the given Redis
-	 * connection timeout.
-	 *
-	 * @param timeout the Redis connection timeout
-	 * @return this ConfigBuilder
-	 */
-	public ConfigBuilder withTimeout(final int timeout) {
-		if (timeout < 0) {
-			throw new IllegalArgumentException("timeout must not be negative: " + timeout);
-		}
-		this.timeout = timeout;
-		return this;
-	}
-
-	/**
-	 * Configs created by this ConfigBuilder will authenticate with the given
-	 * Redis password.
-	 *
-	 * @param password the Redis password
-	 * @return this ConfigBuilder
-	 */
-	public ConfigBuilder withPassword(final String password) {
-		this.password = password;
-		return this;
-	}
-
-	/**
-	 * Configs created by this ConfigBuilder will have the given Redis namespace
-	 * to prefix keys with.
-	 *
-	 * @param namespace the Redis namespace to prefix keys with
-	 * @return this ConfigBuilder
-	 */
-	public ConfigBuilder withNamespace(final String namespace) {
-		if (namespace == null) {
-			throw new IllegalArgumentException("namespace must not be null");
-		}
-		this.namespace = namespace;
-		return this;
-	}
-
-	/**
-	 * Configs created by this ConfigBuilder will use the given Redis database.
-	 *
-	 * @param database the Redis database to use
-	 * @return this ConfigBuilder
-	 */
-	public ConfigBuilder withDatabase(final int database) {
-		if (database < 0) {
-			throw new IllegalArgumentException("database must not be negative: " + database);
-		}
-		this.database = database;
-		return this;
-	}
-
-	/**
-	 * Configs created by this ConfigBuilder will use the given Redis sentinels.
-	 *
-	 * @param sentinels the Redis set of sentinels
-	 * @return this ConfigBuilder
-	 */
-	public ConfigBuilder withSentinels(final Set<String> sentinels) {
-		if (sentinels == null || sentinels.size() < 1) {
-			throw new IllegalArgumentException("sentinels is null or empty: " + sentinels);
-		}
-		this.sentinels = sentinels;
-		return this;
-	}
-
-	/**
-	 * Configs created by this ConfigBuilder will use the given Redis master name.
-	 *
-	 * @param masterName the Redis set of sentinels
-	 * @return this ConfigBuilder
-	 */
-	public ConfigBuilder withMasterName(final String masterName) {
-		if (masterName == null || "".equals(masterName)) {
-			throw new IllegalArgumentException("masterName is null or empty: " + masterName);
-		}
-		this.masterName = masterName;
-		return this;
-	}
-
-	/**
-	 * @return a new Config initialized with the current values
-	 */
-	public Config build() {
-		if (this.sentinels != null && this.sentinels.size() > 0 && masterName != null && !"".equals(masterName)) {
-			return new Config(this.sentinels, this.masterName, this.timeout, this.password, this.namespace, this.database);
-		} else {
-			return new Config(this.host, this.port, this.timeout, this.password, this.namespace, this.database);
-		}
-	}
+    private static final long serialVersionUID = 730947307298353317L;
+
+    //localhost
+    public static final String DEFAULT_HOST = "localhost";
+    // 6379
+    public static final int DEFAULT_PORT = 6379;
+    // 5 seconds
+    public static final int DEFAULT_TIMEOUT = 5000;
+    // null
+    public static final String DEFAULT_PASSWORD = null;
+    // All resque clients use "resque" by default
+    public static final String DEFAULT_NAMESPACE = "resque";
+    // 0
+    public static final int DEFAULT_DATABASE = 0;
+    // []
+    public static final HashSet<String> DEFAULT_SENTINELS = null;
+    // null
+    public static final String DEFAULT_MASTERNAME = null;
+
+    /**
+     * @return a Config with all the default values set
+     */
+    public static Config getDefaultConfig() {
+        return new ConfigBuilder().build();
+    }
+
+    private String host = DEFAULT_HOST;
+    private int port = DEFAULT_PORT;
+    private int timeout = DEFAULT_TIMEOUT;
+    private String password = DEFAULT_PASSWORD;
+    private String namespace = DEFAULT_NAMESPACE;
+    private Set<String> sentinels = DEFAULT_SENTINELS;
+    private String masterName = DEFAULT_MASTERNAME;
+    private int database = DEFAULT_DATABASE;
+
+    /**
+     * No-arg constructor.
+     */
+    public ConfigBuilder() {
+        // Do nothing
+    }
+
+    /**
+     * Create a new ConfigBuilder using an existing Config as the starting
+     * point.
+     *
+     * @param startingPoint the Config instance to copy the values from
+     */
+    public ConfigBuilder(final Config startingPoint) {
+        if (startingPoint == null) {
+            throw new IllegalArgumentException("startingPoint must not be null");
+        }
+        this.host = startingPoint.getHost();
+        this.port = startingPoint.getPort();
+        this.timeout = startingPoint.getTimeout();
+        this.namespace = startingPoint.getNamespace();
+        this.database = startingPoint.getDatabase();
+        this.sentinels = startingPoint.getSentinels();
+        this.masterName = startingPoint.getMasterName();
+    }
+
+    /**
+     * Configs created by this ConfigBuilder will have the given Redis hostname.
+     *
+     * @param host the Redis hostname
+     * @return this ConfigBuilder
+     */
+    public ConfigBuilder withHost(final String host) {
+        if (host == null || "".equals(host)) {
+            throw new IllegalArgumentException("host must not be null or empty: " + host);
+        }
+        this.host = host;
+        return this;
+    }
+
+    /**
+     * Configs created by this ConfigBuilder will have the given Redis port
+     * number.
+     *
+     * @param port the Redis port number
+     * @return this ConfigBuilder
+     */
+    public ConfigBuilder withPort(final int port) {
+        if (port < 1 || port > 65535) {
+            throw new IllegalArgumentException("post must be a valid port in the range 1-65535: " + port);
+        }
+        this.port = port;
+        return this;
+    }
+
+    /**
+     * Configs created by this ConfigBuilder will have the given Redis
+     * connection timeout.
+     *
+     * @param timeout the Redis connection timeout
+     * @return this ConfigBuilder
+     */
+    public ConfigBuilder withTimeout(final int timeout) {
+        if (timeout < 0) {
+            throw new IllegalArgumentException("timeout must not be negative: " + timeout);
+        }
+        this.timeout = timeout;
+        return this;
+    }
+
+    /**
+     * Configs created by this ConfigBuilder will authenticate with the given
+     * Redis password.
+     *
+     * @param password the Redis password
+     * @return this ConfigBuilder
+     */
+    public ConfigBuilder withPassword(final String password) {
+        this.password = password;
+        return this;
+    }
+
+    /**
+     * Configs created by this ConfigBuilder will have the given Redis namespace
+     * to prefix keys with.
+     *
+     * @param namespace the Redis namespace to prefix keys with
+     * @return this ConfigBuilder
+     */
+    public ConfigBuilder withNamespace(final String namespace) {
+        if (namespace == null) {
+            throw new IllegalArgumentException("namespace must not be null");
+        }
+        this.namespace = namespace;
+        return this;
+    }
+
+    /**
+     * Configs created by this ConfigBuilder will use the given Redis database.
+     *
+     * @param database the Redis database to use
+     * @return this ConfigBuilder
+     */
+    public ConfigBuilder withDatabase(final int database) {
+        if (database < 0) {
+            throw new IllegalArgumentException("database must not be negative: " + database);
+        }
+        this.database = database;
+        return this;
+    }
+
+    /**
+     * Configs created by this ConfigBuilder will use the given Redis sentinels.
+     *
+     * @param sentinels the Redis set of sentinels
+     * @return this ConfigBuilder
+     */
+    public ConfigBuilder withSentinels(final Set<String> sentinels) {
+        if (sentinels == null || sentinels.size() < 1) {
+            throw new IllegalArgumentException("sentinels is null or empty: " + sentinels);
+        }
+        this.sentinels = sentinels;
+        return this;
+    }
+
+    /**
+     * Configs created by this ConfigBuilder will use the given Redis master name.
+     *
+     * @param masterName the Redis set of sentinels
+     * @return this ConfigBuilder
+     */
+    public ConfigBuilder withMasterName(final String masterName) {
+        if (masterName == null || "".equals(masterName)) {
+            throw new IllegalArgumentException("masterName is null or empty: " + masterName);
+        }
+        this.masterName = masterName;
+        return this;
+    }
+
+    /**
+     * @return a new Config initialized with the current values
+     */
+    public Config build() {
+        if (this.sentinels != null && this.sentinels.size() > 0 && masterName != null && !"".equals(masterName)) {
+            return new Config(this.sentinels, this.masterName, this.timeout, this.password, this.namespace, this.database);
+        } else {
+            return new Config(this.host, this.port, this.timeout, this.password, this.namespace, this.database);
+        }
+    }
 }
diff --git a/src/main/java/net/greghaines/jesque/utils/PoolUtils.java b/src/main/java/net/greghaines/jesque/utils/PoolUtils.java
index 2806e2eb..a83b2821 100644
--- a/src/main/java/net/greghaines/jesque/utils/PoolUtils.java
+++ b/src/main/java/net/greghaines/jesque/utils/PoolUtils.java
@@ -29,124 +29,124 @@
  */
 public final class PoolUtils {
 
-	/**
-	 * Perform the given work with a resource from the given pool.
-	 *
-	 * @param pool the resource pool
-	 * @param work the work to perform
-	 * @param <T>  the resource type
-	 * @param <V>  the result type
-	 * @return the result of the given work
-	 * @throws Exception if something went wrong
-	 */
-	public static <T, V> V doWorkInPool(final Pool<T> pool, final PoolWork<T, V> work) throws Exception {
-		if (pool == null) {
-			throw new IllegalArgumentException("pool must not be null");
-		}
-		if (work == null) {
-			throw new IllegalArgumentException("work must not be null");
-		}
-		final V result;
-		final T poolResource = pool.getResource();
-		try {
-			result = work.doWork(poolResource);
-		} finally {
-			pool.returnResource(poolResource);
-		}
-		return result;
-	}
+    /**
+     * Perform the given work with a resource from the given pool.
+     *
+     * @param pool the resource pool
+     * @param work the work to perform
+     * @param <T>  the resource type
+     * @param <V>  the result type
+     * @return the result of the given work
+     * @throws Exception if something went wrong
+     */
+    public static <T, V> V doWorkInPool(final Pool<T> pool, final PoolWork<T, V> work) throws Exception {
+        if (pool == null) {
+            throw new IllegalArgumentException("pool must not be null");
+        }
+        if (work == null) {
+            throw new IllegalArgumentException("work must not be null");
+        }
+        final V result;
+        final T poolResource = pool.getResource();
+        try {
+            result = work.doWork(poolResource);
+        } finally {
+            pool.returnResource(poolResource);
+        }
+        return result;
+    }
 
-	/**
-	 * Perform the given work with a resource from the given pool. Wraps any
-	 * thrown checked exceptions in a RuntimeException.
-	 *
-	 * @param pool the resource pool
-	 * @param work the work to perform
-	 * @param <T>  the resource type
-	 * @param <V>  the result type
-	 * @return the result of the given work
-	 */
-	public static <T, V> V doWorkInPoolNicely(final Pool<T> pool, final PoolWork<T, V> work) {
-		final V result;
-		try {
-			result = doWorkInPool(pool, work);
-		} catch (RuntimeException re) {
-			throw re;
-		} catch (Exception e) {
-			throw new RuntimeException(e);
-		}
-		return result;
-	}
+    /**
+     * Perform the given work with a resource from the given pool. Wraps any
+     * thrown checked exceptions in a RuntimeException.
+     *
+     * @param pool the resource pool
+     * @param work the work to perform
+     * @param <T>  the resource type
+     * @param <V>  the result type
+     * @return the result of the given work
+     */
+    public static <T, V> V doWorkInPoolNicely(final Pool<T> pool, final PoolWork<T, V> work) {
+        final V result;
+        try {
+            result = doWorkInPool(pool, work);
+        } catch (RuntimeException re) {
+            throw re;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        return result;
+    }
 
-	/**
-	 * @return a GenericObjectPoolConfig configured with: maxActive=-1,
-	 * maxIdle=10, minIdle=1, testOnBorrow=true,
-	 * blockWhenExhausted=false
-	 */
-	public static GenericObjectPoolConfig getDefaultPoolConfig() {
-		final GenericObjectPoolConfig cfg = new GenericObjectPoolConfig();
-		cfg.setMaxTotal(-1); // Infinite
-		cfg.setMaxIdle(10);
-		cfg.setMinIdle(1);
-		cfg.setTestOnBorrow(true);
-		cfg.setBlockWhenExhausted(false);
-		return cfg;
-	}
+    /**
+     * @return a GenericObjectPoolConfig configured with: maxActive=-1,
+     * maxIdle=10, minIdle=1, testOnBorrow=true,
+     * blockWhenExhausted=false
+     */
+    public static GenericObjectPoolConfig getDefaultPoolConfig() {
+        final GenericObjectPoolConfig cfg = new GenericObjectPoolConfig();
+        cfg.setMaxTotal(-1); // Infinite
+        cfg.setMaxIdle(10);
+        cfg.setMinIdle(1);
+        cfg.setTestOnBorrow(true);
+        cfg.setBlockWhenExhausted(false);
+        return cfg;
+    }
 
-	/**
-	 * A simple helper method that creates a pool of connections to Redis using
-	 * the supplied Config and the default pool config.
-	 *
-	 * @param jesqueConfig the config used to create the pooled Jedis connection
-	 * @return a configured Pool of Jedis connections
-	 */
-	public static Pool<Jedis> createJedisPool(final Config jesqueConfig) {
-		return createJedisPool(jesqueConfig, getDefaultPoolConfig());
-	}
+    /**
+     * A simple helper method that creates a pool of connections to Redis using
+     * the supplied Config and the default pool config.
+     *
+     * @param jesqueConfig the config used to create the pooled Jedis connection
+     * @return a configured Pool of Jedis connections
+     */
+    public static Pool<Jedis> createJedisPool(final Config jesqueConfig) {
+        return createJedisPool(jesqueConfig, getDefaultPoolConfig());
+    }
 
-	/**
-	 * A simple helper method that creates a pool of connections to Redis using
-	 * the supplied configurations.
-	 *
-	 * @param jesqueConfig the config used to create the pooled Jedis connections
-	 * @param poolConfig   the config used to create the pool
-	 * @return a configured Pool of Jedis connections
-	 */
-	public static Pool<Jedis> createJedisPool(final Config jesqueConfig, final GenericObjectPoolConfig poolConfig) {
-		if (jesqueConfig == null) {
-			throw new IllegalArgumentException("jesqueConfig must not be null");
-		}
-		if (poolConfig == null) {
-			throw new IllegalArgumentException("poolConfig must not be null");
-		}
-		if (jesqueConfig.getMasterName() != null && !"".equals(jesqueConfig.getMasterName()) && jesqueConfig.getSentinels() != null
-				&& jesqueConfig.getSentinels().size() > 0) {
-			return new JedisSentinelPool(jesqueConfig.getMasterName(), jesqueConfig.getSentinels(), poolConfig, jesqueConfig.getTimeout(),
-					jesqueConfig.getPassword());
-		} else {
-			return new JedisPool(poolConfig, jesqueConfig.getHost(), jesqueConfig.getPort(), jesqueConfig.getTimeout(), jesqueConfig.getPassword());
-		}
-	}
+    /**
+     * A simple helper method that creates a pool of connections to Redis using
+     * the supplied configurations.
+     *
+     * @param jesqueConfig the config used to create the pooled Jedis connections
+     * @param poolConfig   the config used to create the pool
+     * @return a configured Pool of Jedis connections
+     */
+    public static Pool<Jedis> createJedisPool(final Config jesqueConfig, final GenericObjectPoolConfig poolConfig) {
+        if (jesqueConfig == null) {
+            throw new IllegalArgumentException("jesqueConfig must not be null");
+        }
+        if (poolConfig == null) {
+            throw new IllegalArgumentException("poolConfig must not be null");
+        }
+        if (jesqueConfig.getMasterName() != null && !"".equals(jesqueConfig.getMasterName()) && jesqueConfig.getSentinels() != null
+                && jesqueConfig.getSentinels().size() > 0) {
+            return new JedisSentinelPool(jesqueConfig.getMasterName(), jesqueConfig.getSentinels(), poolConfig, jesqueConfig.getTimeout(),
+                    jesqueConfig.getPassword());
+        } else {
+            return new JedisPool(poolConfig, jesqueConfig.getHost(), jesqueConfig.getPort(), jesqueConfig.getTimeout(), jesqueConfig.getPassword());
+        }
+    }
 
-	/**
-	 * A unit of work that utilizes a pooled resource.
-	 *
-	 * @param <T> the kind of pooled resource used
-	 * @param <V> the kind of result returned
-	 * @author Greg Haines
-	 */
-	public interface PoolWork<T, V> {
-		/**
-		 * Do work with a pooled resource and return a result.
-		 *
-		 * @param poolResource the pooled resource
-		 * @return the result of the work done
-		 * @throws Exception in case something goes wrong
-		 */
-		V doWork(T poolResource) throws Exception;
-	}
+    /**
+     * A unit of work that utilizes a pooled resource.
+     *
+     * @param <T> the kind of pooled resource used
+     * @param <V> the kind of result returned
+     * @author Greg Haines
+     */
+    public interface PoolWork<T, V> {
+        /**
+         * Do work with a pooled resource and return a result.
+         *
+         * @param poolResource the pooled resource
+         * @return the result of the work done
+         * @throws Exception in case something goes wrong
+         */
+        V doWork(T poolResource) throws Exception;
+    }
 
-	private PoolUtils() {
-		// Utility class
-	}
+    private PoolUtils() {
+        // Utility class
+    }
 }
diff --git a/src/test/java/net/greghaines/jesque/TestConfigBuilder.java b/src/test/java/net/greghaines/jesque/TestConfigBuilder.java
index b490e433..244716a0 100644
--- a/src/test/java/net/greghaines/jesque/TestConfigBuilder.java
+++ b/src/test/java/net/greghaines/jesque/TestConfigBuilder.java
@@ -9,191 +9,191 @@
 
 public class TestConfigBuilder {
 
-	@Test
-	public void testGetDefaultConfig() {
-		final Config config = ConfigBuilder.getDefaultConfig();
-		Assert.assertNotNull(config);
-		Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
-	}
-
-	@Test
-	public void testConstructor_NoArg() {
-		final Config config = new ConfigBuilder().build();
-		Assert.assertNotNull(config);
-		Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
-	}
-
-	@Test
-	public void testConstructor_Cloning() {
-		final Config orig = new ConfigBuilder().withNamespace("foo").withDatabase(10).withPassword("bar").withPort(123).withHost("abc.com").withTimeout(10000).build();
-		final Config copy = new ConfigBuilder(orig).build();
-		TestUtils.assertFullyEquals(orig, copy);
-	}
-
-	@Test(expected = IllegalArgumentException.class)
-	public void testConstructor_Cloning_Null() {
-		new ConfigBuilder(null);
-	}
-
-	@Test
-	public void testWithHost() {
-		final String myHost = "foobar";
-		final Config config = new ConfigBuilder().withHost(myHost).build();
-		Assert.assertNotNull(config);
-		Assert.assertEquals(myHost, config.getHost());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
-	}
-
-	@Test(expected = IllegalArgumentException.class)
-	public void testWithHost_Null() {
-		new ConfigBuilder().withHost(null);
-	}
-
-	@Test(expected = IllegalArgumentException.class)
-	public void testWithHost_Empty() {
-		new ConfigBuilder().withHost("");
-	}
-
-	@Test
-	public void testWithPort() {
-		final int myPort = 1234;
-		final Config config = new ConfigBuilder().withPort(myPort).build();
-		Assert.assertNotNull(config);
-		Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
-		Assert.assertEquals(myPort, config.getPort());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
-	}
-
-	@Test(expected = IllegalArgumentException.class)
-	public void testWithPort_Low() {
-		new ConfigBuilder().withPort(0);
-	}
-
-	@Test(expected = IllegalArgumentException.class)
-	public void testWithPort_High() {
-		new ConfigBuilder().withPort(Integer.MAX_VALUE);
-	}
-
-	@Test
-	public void testWithTimeout() {
-		final int myTimeout = 77777;
-		final Config config = new ConfigBuilder().withTimeout(myTimeout).build();
-		Assert.assertNotNull(config);
-		Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
-		Assert.assertEquals(myTimeout, config.getTimeout());
-	}
-
-	@Test(expected = IllegalArgumentException.class)
-	public void testWithTimeout_Negative() {
-		new ConfigBuilder().withTimeout(-1);
-	}
-
-	@Test
-	public void testWithDatabase() {
-		final int myDB = 2;
-		final Config config = new ConfigBuilder().withDatabase(myDB).build();
-		Assert.assertNotNull(config);
-		Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
-		Assert.assertEquals(myDB, config.getDatabase());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
-	}
-
-	@Test(expected = IllegalArgumentException.class)
-	public void testWithDatabase_Negative() {
-		new ConfigBuilder().withDatabase(-1);
-	}
-
-	@Test
-	public void testWithPassword() {
-		final String myPassword = "s3r3t5!";
-		final Config config = new ConfigBuilder().withPassword(myPassword).build();
-		Assert.assertNotNull(config);
-		Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
-		Assert.assertEquals(myPassword, config.getPassword());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
-	}
-
-	@Test
-	public void testWithNamespace() {
-		final String myNamespace = "foo";
-		final Config config = new ConfigBuilder().withNamespace(myNamespace).build();
-		Assert.assertNotNull(config);
-		Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
-		Assert.assertEquals(myNamespace, config.getNamespace());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
-	}
-
-	@Test
-	public void testWithMasterNameAndSentinels() {
-		final String myMasterName = "foo";
-		final Set<String> mySentinels = new HashSet<>(Arrays.asList("a", "b"));
-		final Config config = new ConfigBuilder().withSentinels(mySentinels).withMasterName(myMasterName).build();
-		Assert.assertNotNull(config);
-		Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
-		Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
-		Assert.assertEquals(mySentinels, config.getSentinels());
-		Assert.assertEquals(myMasterName, config.getMasterName());
-	}
-
-	@Test(expected = IllegalArgumentException.class)
-	public void testWithNamespace_Null() {
-		new ConfigBuilder().withNamespace(null);
-	}
-
-	@Test
-	public void testWithNamespace_Empty() {
-		final String myNamespace = "";
-		final Config config = new ConfigBuilder().withNamespace(myNamespace).build();
-		Assert.assertEquals(myNamespace, config.getNamespace());
-	}
+    @Test
+    public void testGetDefaultConfig() {
+        final Config config = ConfigBuilder.getDefaultConfig();
+        Assert.assertNotNull(config);
+        Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
+    }
+
+    @Test
+    public void testConstructor_NoArg() {
+        final Config config = new ConfigBuilder().build();
+        Assert.assertNotNull(config);
+        Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
+    }
+
+    @Test
+    public void testConstructor_Cloning() {
+        final Config orig = new ConfigBuilder().withNamespace("foo").withDatabase(10).withPassword("bar").withPort(123).withHost("abc.com").withTimeout(10000).build();
+        final Config copy = new ConfigBuilder(orig).build();
+        TestUtils.assertFullyEquals(orig, copy);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testConstructor_Cloning_Null() {
+        new ConfigBuilder(null);
+    }
+
+    @Test
+    public void testWithHost() {
+        final String myHost = "foobar";
+        final Config config = new ConfigBuilder().withHost(myHost).build();
+        Assert.assertNotNull(config);
+        Assert.assertEquals(myHost, config.getHost());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testWithHost_Null() {
+        new ConfigBuilder().withHost(null);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testWithHost_Empty() {
+        new ConfigBuilder().withHost("");
+    }
+
+    @Test
+    public void testWithPort() {
+        final int myPort = 1234;
+        final Config config = new ConfigBuilder().withPort(myPort).build();
+        Assert.assertNotNull(config);
+        Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
+        Assert.assertEquals(myPort, config.getPort());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testWithPort_Low() {
+        new ConfigBuilder().withPort(0);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testWithPort_High() {
+        new ConfigBuilder().withPort(Integer.MAX_VALUE);
+    }
+
+    @Test
+    public void testWithTimeout() {
+        final int myTimeout = 77777;
+        final Config config = new ConfigBuilder().withTimeout(myTimeout).build();
+        Assert.assertNotNull(config);
+        Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
+        Assert.assertEquals(myTimeout, config.getTimeout());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testWithTimeout_Negative() {
+        new ConfigBuilder().withTimeout(-1);
+    }
+
+    @Test
+    public void testWithDatabase() {
+        final int myDB = 2;
+        final Config config = new ConfigBuilder().withDatabase(myDB).build();
+        Assert.assertNotNull(config);
+        Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
+        Assert.assertEquals(myDB, config.getDatabase());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testWithDatabase_Negative() {
+        new ConfigBuilder().withDatabase(-1);
+    }
+
+    @Test
+    public void testWithPassword() {
+        final String myPassword = "s3r3t5!";
+        final Config config = new ConfigBuilder().withPassword(myPassword).build();
+        Assert.assertNotNull(config);
+        Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
+        Assert.assertEquals(myPassword, config.getPassword());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
+    }
+
+    @Test
+    public void testWithNamespace() {
+        final String myNamespace = "foo";
+        final Config config = new ConfigBuilder().withNamespace(myNamespace).build();
+        Assert.assertNotNull(config);
+        Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
+        Assert.assertEquals(myNamespace, config.getNamespace());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_SENTINELS, config.getSentinels());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_MASTERNAME, config.getMasterName());
+    }
+
+    @Test
+    public void testWithMasterNameAndSentinels() {
+        final String myMasterName = "foo";
+        final Set<String> mySentinels = new HashSet<>(Arrays.asList("a", "b"));
+        final Config config = new ConfigBuilder().withSentinels(mySentinels).withMasterName(myMasterName).build();
+        Assert.assertNotNull(config);
+        Assert.assertEquals(ConfigBuilder.DEFAULT_HOST, config.getHost());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_NAMESPACE, config.getNamespace());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_PASSWORD, config.getPassword());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_DATABASE, config.getDatabase());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_PORT, config.getPort());
+        Assert.assertEquals(ConfigBuilder.DEFAULT_TIMEOUT, config.getTimeout());
+        Assert.assertEquals(mySentinels, config.getSentinels());
+        Assert.assertEquals(myMasterName, config.getMasterName());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testWithNamespace_Null() {
+        new ConfigBuilder().withNamespace(null);
+    }
+
+    @Test
+    public void testWithNamespace_Empty() {
+        final String myNamespace = "";
+        final Config config = new ConfigBuilder().withNamespace(myNamespace).build();
+        Assert.assertEquals(myNamespace, config.getNamespace());
+    }
 }
