package com.genesys.wsclient;
import java.util.concurrent.Executor;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import com.genesys.wsclient.impl.Authentication;
import com.genesys.wsclient.impl.BasicAuthentication;
import com.genesys.wsclient.impl.CookieSession;
import com.genesys.wsclient.impl.CookieSessionImpl;
import com.genesys.wsclient.impl.NoAuthentication;
import com.genesys.wsclient.impl.NoCookieSession;
/**
* Entry point for using the Genesys Web Services Client Library.
*
* <p>This class serves as a factory of {@link GenesysRequest}s and {@link GenesysEventReceiver}s that:
* <ul>
* <li>Include user credentials for authorization.
* <li>Support cookies, which enable sticky sessions in load-balancing (JSESSIONID cookie).
* </ul>
*
* <p>In order to create an instance of this class, create an instance of {@link GenesysClient.Setup},
* setup its parameters, and call {@link Setup#create()}.
*
* <p>This is an example of a minimal setup:
* <pre>
* GenesysClient client = new GenesysClient.Setup()
* .serverUri("http://myserver:8080")
* .credentials("myusername", "mypassword")
* .create();
* </pre>
*
* <p>This class is thread-safe.
*/
public class GenesysClient implements AutoCloseable {
final HttpClient httpClient;
private final boolean disposeOfHttpClient;
final String serverUri;
final int requestTimeout;
final CookieSession cookieSession;
private final Authentication authentication;
final Executor asyncExecutor;
protected GenesysClient(Setup builder) {
this.httpClient = builder.httpClient;
this.disposeOfHttpClient = builder.disposeOfHttpClient;
this.serverUri = builder.serverUri;
this.requestTimeout = builder.requestTimeout;
this.cookieSession = builder.cookieSession;
this.authentication = builder.authentication;
this.asyncExecutor = builder.asyncExecutor;
}
/**
* Stops the underlying httpClient.
*
* <p>If you have explicitly set an httpClient using
* {@link Setup#httpClient(HttpClient)}, or {@link Setup#shareHttpClient(GenesysClient)},
* then the underlying httpClient won't be stopped.
*/
@Override
public void close() {
if (disposeOfHttpClient) {
try {
this.httpClient.stop();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
/**
* Creates an event receiver that uses this client.
*/
public GenesysEventReceiver.Setup setupEventReceiver() {
return new GenesysEventReceiver.Setup(this, cookieSession, authentication);
}
/**
* Creates a request that uses this client.
*
* @param uri
* <ul>
* <li>A string starting with "/" is considered a relative URI.
* <li>If starting with "http" it is considered an absolute URI.
* <li>No other cases are allowed.
* </ul>
*/
public GenesysRequest createRequest(String httpMethod, String uri) {
String absoluteUri;
if (uri.startsWith("/"))
absoluteUri = serverUri + uri;
else if (uri.startsWith("http"))
absoluteUri = uri;
else
throw new IllegalArgumentException(
"URI must be either complete (starting with \"http\"),"
+ " or an absolute path ((starting with \"/\"): " + uri);
return new GenesysRequest(
this, httpMethod, absoluteUri,
cookieSession, authentication)
.timeout(requestTimeout);
}
/**
* This class is not thread-safe. Therefore always do the whole setup
* and creation of a GenesysClient in the same thread, or use according
* multi-threading techniques.
*/
public static class Setup {
private String serverUri;
private HttpClient httpClient;
private boolean disposeOfHttpClient = true;
private int connectTimeout = 5000;
private int requestTimeout = 5000;
private Authentication authentication;
private CookieSession cookieSession = new CookieSessionImpl();
private Executor asyncExecutor;
public Setup() {}
public GenesysClient create() {
if (authentication == null)
throw new IllegalStateException(
"Credentials (username and password) are mandatory. " +
"If you want no credentials please use anonymous() explicitly.");
if (httpClient == null)
httpClient = createDefaultHttpClient(connectTimeout);
return new GenesysClient(this);
}
private static HttpClient createDefaultHttpClient(int connectTimeout) {
HttpClient httpClient = new HttpClient();
httpClient.setConnectTimeout(connectTimeout);
QueuedThreadPool threadPool = new QueuedThreadPool();
// This way the minimum number of threads necessary are created: 3
threadPool.setMinThreads(1);
threadPool.setDaemon(true);
threadPool.setName("GenesysClient");
httpClient.setThreadPool(threadPool);
try {
httpClient.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
return httpClient;
}
/**
* (Mandatory) Server URI used by this client.
*
* @param uri
* Server URI, without any trailing slashes. For example: <code>http://myserver:8080</code>
*/
public Setup serverUri(String uri) {
if (uri.endsWith("/"))
throw new IllegalArgumentException("Server URI must not end with a slash '/'");
this.serverUri = uri;
return this;
}
/**
* (Optional) A started HttpClient for this client to use.
*
* <p>If not set, a default HttpClient will be created and used.
*
* <p>If this method is used, the given HttpClient will not be owned by this class.
* This means that the client must stop the HttpClient in order to dispose of it.
*
* @see GenesysClient#close()
*/
public Setup httpClient(HttpClient httpClient) {
this.disposeOfHttpClient = httpClient == null;
this.httpClient = httpClient;
return this;
}
/**
* (Optional) Connect timeout.
*
* <p>This method has no effect if you set a custom HttpClient
* using {@link #httpClient(HttpClient)}.
*
* <p>Default value: 5000 ms.
*/
public Setup connectTimeoutMillis(int connectTimeout) {
this.connectTimeout = connectTimeout;
return this;
}
/**
* (Mandatory, unless {@link #anonymous()} is used)
* Credentials used for authenticating.
*/
public Setup credentials(String username, String password) {
if (username == null)
throw new NullPointerException("username is null");
if (password == null)
throw new NullPointerException("password is null");
this.authentication = new BasicAuthentication(username, password);
return this;
}
/**
* (Mandatory if you don't set {@link #credentials(String, String)})
*/
public Setup anonymous() {
this.authentication = NoAuthentication.instance;
return this;
}
/**
* (Optional) Default request timeout. Used for requests that don't specify a timeout.
*
* <p>Default value: 5000 ms.
*/
public Setup requestTimeout(int requestTimeout) {
this.requestTimeout = requestTimeout;
return this;
}
/**
* (Optional) Disables the use of cookies. Cookies are enabled by default.
*/
public Setup disableCookies() {
this.cookieSession = NoCookieSession.instance;
return this;
}
/**
* (Optional) Default executor for the notification of asynchronous requests and events.
*/
public Setup asyncExecutor(Executor asyncExecutor) {
this.asyncExecutor = asyncExecutor;
return this;
}
/**
* (Optional) Indicates to use the same httpClient as the given GenesysClient.
*
* <p>If this methods is used, then the given GenesysClient will continue to own the httpClient.
* This means that this GenesysClient will not close the httpClient when closed.
*/
public Setup shareHttpClient(GenesysClient client) {
this.disposeOfHttpClient = false;
return httpClient(client.httpClient);
}
/**
* (Optional) Indicates to use the same cookies as the given GenesysClient.
*/
public Setup shareCookies(GenesysClient client) {
this.cookieSession = client.cookieSession;
return this;
}
}
}