/** * */ package com.trendrr.oss.networking.strest; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.trendrr.oss.concurrent.Sleep; import com.trendrr.oss.exceptions.TrendrrDisconnectedException; import com.trendrr.oss.exceptions.TrendrrException; import com.trendrr.oss.exceptions.TrendrrOverflowException; import com.trendrr.oss.exceptions.TrendrrIOException; import com.trendrr.oss.exceptions.TrendrrTimeoutException; import com.trendrr.oss.networking.SocketChannelWrapper; /** * Strest Client. * * This class is threadsafe, and handles low level details of the STREST protocol. * * The client is implemented using non-blocking sockets, there is a single io thread which is shared * across any StrestClient instances. * * Callbacks are executed in the io thread, so it is recommended that any heavy processing be done in a separate thread. * * * @author Dustin Norlander * @created Mar 14, 2011 * @deprecated use com.trendrr.oss.strest */ @Deprecated public class StrestClient { protected static Log log = LogFactory.getLog(StrestClient.class); protected SocketChannelWrapper socket = null; protected StrestMessageReader reader; protected String host = null; protected int port = 8008; protected ConcurrentHashMap<String, StrestRequestCallback> callbacks = new ConcurrentHashMap<String,StrestRequestCallback>(); protected AtomicBoolean connected = new AtomicBoolean(false); protected int maxWaitingForResponse = 0; //the maximum number of waiting callbacks. protected int maxQueuedWrites = 200; //the maximum number of writes that have queued up. public StrestClient(String host, int port) { this.host = host; this.port = port; } public synchronized void connect() throws IOException { if (this.connected.get()) { log.warn("Connect called, but already connected"); return; } SocketChannel channel; channel = SocketChannel.open(); boolean connected = channel.connect(new InetSocketAddress(this.host, this.port)); log.info("CONNECTED: " + connected); socket = new SocketChannelWrapper(channel); reader = new StrestMessageReader(); reader.start(this, socket); this.connected.set(true); } public boolean isConnected() { return this.connected.get(); } /** * closes the connection and cleans up any resources. * * all waiting callbacks will get a disconnected exception.. * */ public synchronized void close() { this.connected.set(false); try { socket.close(); } catch (Exception x) { log.info("Caught", x); } if (reader != null) { try { reader.stop(); } catch (Exception x) { log.info("Caught", x); } } reader = null; log.info("Announcing broken connection to callbacks: " + this.callbacks); for (StrestRequestCallback cb : this.callbacks.values()) { log.info("CONNECTION BROKEN! : " + cb); cb.error(new TrendrrDisconnectedException("Connection Broken")); } this.callbacks.clear(); } /** * sends the request asynchrounously. * * this method returns immediately, response is sent to the callback. * * @param request * @param callback * @throws Exception */ public synchronized void sendRequest(StrestRequest request, StrestRequestCallback callback){ try { if (this.socket == null || this.socket.isClosed()) { throw new IOException("Not connected"); } if (this.maxWaitingForResponse > 0 && this.maxWaitingForResponse <= this.callbacks.size()) { throw new TrendrrIOException(this.maxWaitingForResponse + " waiting for response, me thinks theres a network problem, or you need to slow down!"); } request.setHeaderIfAbsent(StrestHeaders.Names.STREST_TXN_ACCEPT, StrestHeaders.Values.MULTI); ByteBuffer buf = request.getBytesAsBuffer(); if (callback != null) this.callbacks.put(request.getHeader(StrestHeaders.Names.STREST_TXN_ID), callback); if (this.maxQueuedWrites > 0) { socket.write(buf, this.maxQueuedWrites); } else { socket.write(buf); } } catch (Exception e) { if (callback != null) { callback.error(e); } } } public StrestResponse sendRequest(StrestRequest request) throws TrendrrException { return this.sendRequest(request, 0l); } /** * sends a synchronious request, will wait for the result. * @param request * @return */ public StrestResponse sendRequest(StrestRequest request, long timeoutMillis) throws TrendrrTimeoutException, TrendrrException { try { if (!this.connected.get()) { throw new TrendrrDisconnectedException("Strest Client is not connected!"); } request.setHeaderIfAbsent(StrestHeaders.Names.STREST_TXN_ACCEPT, StrestHeaders.Values.SINGLE); StrestSynchronousRequest sr = new StrestSynchronousRequest(); this.sendRequest(request, sr); return sr.awaitResponse(timeoutMillis); } catch (TrendrrException x) { throw x; } catch (Throwable t) { if (t instanceof IOException) { IOException iox = (IOException)t; if (iox.getMessage().equalsIgnoreCase("not connected")) { throw new TrendrrDisconnectedException(iox); } } throw new TrendrrException(new Exception(t)); } } /* * incoming message from the reader. */ void incoming(StrestResponse response) { String txnId = response.getHeader(StrestHeaders.Names.STREST_TXN_ID); String txnStatus = response.getHeader(StrestHeaders.Names.STREST_TXN_STATUS); StrestRequestCallback cb = this.callbacks.get(txnId); if (cb == null) { //Server sent us a response to transaction that doesn't exist or is already closed! log.error("SERVER SENT Response to Transaction: " + txnId + " Which is either closed or doesn't exist!"); return; } try { cb.response(response); } catch (Exception x) { log.error("Caught", x); } if (!StrestHeaders.Values.CONTINUE.equalsIgnoreCase(txnStatus)) { this.callbacks.remove(txnId); cb.txnComplete(txnId); } } /* * an error occured in the reader. */ void error(TrendrrException e) { //UMM, what should we do here I wonder? // log.warn("Error from reader Caught", e); if (e instanceof TrendrrDisconnectedException) { log.warn("Closing connection!"); this.close(); } } /** * The maximum allowed callbacks to be waiting for a response. once this limit is reached * new requests will get an exception. Default is 0 which means unlimited * @return */ public int getMaxWaitingForResponse() { return maxWaitingForResponse; } public void setMaxWaitingForResponse(int maxWaitingForResponse) { this.maxWaitingForResponse = maxWaitingForResponse; } /** * Maximum number of queued writes. once this limit is reached exceptions will be thrown on write. * @return */ public int getMaxQueuedWrites() { return maxQueuedWrites; } public void setMaxQueuedWrites(int maxQueuedWrites) { this.maxQueuedWrites = maxQueuedWrites; } }