package org.radargun.http.service; import java.math.BigDecimal; import java.net.InetSocketAddress; import java.util.List; import java.util.concurrent.TimeUnit; import org.jboss.resteasy.client.jaxrs.BasicAuthentication; import org.jboss.resteasy.client.jaxrs.ResteasyClient; import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder.HostnameVerificationPolicy; import org.jboss.resteasy.client.jaxrs.engines.URLConnectionEngine; import org.radargun.Service; import org.radargun.config.Init; import org.radargun.config.Property; import org.radargun.logging.Log; import org.radargun.logging.LogFactory; import org.radargun.traits.Lifecycle; import org.radargun.traits.ProvidesTrait; import org.radargun.utils.Fuzzy; import org.radargun.utils.RESTAddressListConverter; import org.radargun.utils.TimeConverter; /** * @author Martin Gencur */ @Service(doc = "RestEasy REST client for general Web applications") public class RESTEasyService implements Lifecycle { private static final Log log = LogFactory.getLog(RESTEasyService.class); private ResteasyClient httpClient = null; @Property(doc = "The username to use on an authenticated server. Defaults to null.") private String username; @Property(doc = "The password of the username to use on an authenticated server. Defaults to null.") private String password; @Property(doc = "The content type used for put and get operations. Defaults to application/octet-stream.") private String contentType = "application/octet-stream"; @Property(doc = "Semicolon-separated list of server addresses.", converter = RESTAddressListConverter.class, optional = false) protected List<InetSocketAddress> servers; @Property(doc = "Ratio between the number of connections to individual servers. " + "Servers from the 'servers' list are indexed from 0. When the client " + "is first created it will choose a server to communicate with according " + "to this load balancing setting. The client will keep communicating with " + "this single server until redirected.", converter = Fuzzy.IntegerConverter.class) //By default clients are evenly spread across all servers. See {@link #init()}. protected Fuzzy<Integer> serversLoadBalance; @Property(doc = "Timeout for socket. Default is 30 seconds.", converter = TimeConverter.class) protected long socketTimeout = 30000; @Property(doc = "Timeout for connection. Default is 30 seconds.", converter = TimeConverter.class) protected long connectionTimeout = 30000; @Property(doc = "The size of the connection pool. Default is unlimited.") protected int maxConnections = 0; @Property(doc = "The number of connections to pool per url. Default is equal to <code>maxConnections</code>.") protected int maxConnectionsPerHost = 0; @ProvidesTrait public RESTEasyOperations createOperations() { return new RESTEasyOperations(this); } @ProvidesTrait public Lifecycle getLifecycle() { return this; } @Init public void init() { if (serversLoadBalance == null) { Fuzzy.Builder<Integer> builder = new Fuzzy.Builder<>(); for (int i=0; i!=servers.size(); i++) { builder.addWeighted(i, BigDecimal.ONE); } serversLoadBalance = builder.create(); } else { for (Integer serverIndex: serversLoadBalance.getProbabilityMap().keySet()) { if (serverIndex >= servers.size()) throw new IllegalStateException("Load balancing settings for the REST client include server index " + "which is not in the server list: " + serverIndex); } } } @Override public synchronized void start() { if (httpClient != null) { log.warn("Service already started"); return; } httpClient = new ResteasyClientBuilder().httpEngine(new URLConnectionEngine()) .establishConnectionTimeout(connectionTimeout, TimeUnit.MILLISECONDS) .socketTimeout(socketTimeout, TimeUnit.MILLISECONDS).connectionPoolSize(maxConnections) .maxPooledPerRoute(maxConnectionsPerHost).hostnameVerification(HostnameVerificationPolicy.ANY).build(); if (username != null) { BasicAuthentication auth = new BasicAuthentication(username, password); httpClient.register(auth); } } public String getContentType() { return contentType; } public ResteasyClient getHttpClient() { return httpClient; } public String getUsername() { return username; } public List<InetSocketAddress> getServers() { return servers; } public Fuzzy<Integer> getServersLoadBalance() { return serversLoadBalance; } public String getPassword() { return password; } @Override public synchronized void stop() { if (httpClient == null) { log.warn("Service not started"); return; } httpClient.close(); httpClient = null; } @Override public synchronized boolean isRunning() { return httpClient != null; } }