Back to project page DistributedMemory.
The source code is released under:
Apache License
If you think the Android project DistributedMemory listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package org.faudroids.distributedmemory.network; /* ww w .j a va 2s . co m*/ import android.os.Handler; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.util.LinkedList; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; import timber.log.Timber; final class StringConnectionHandler implements ConnectionHandler<String> { private final Socket socket; private final MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(); private final OutputThread outputThread; private final InputThread inputThread; StringConnectionHandler(Socket socket) throws IOException { this.socket = socket; this.outputThread = new OutputThread(socket.getOutputStream(), messageListenerAdapter); this.inputThread = new InputThread(socket.getInputStream(), messageListenerAdapter); } StringConnectionHandler(InetAddress inetAddress, int port) throws IOException { this.socket = new Socket(); this.socket.bind(null); this.socket.connect(new InetSocketAddress(inetAddress.getHostAddress(), port)); this.outputThread = new OutputThread(socket.getOutputStream(), messageListenerAdapter); this.inputThread = new InputThread(socket.getInputStream(), messageListenerAdapter); } @Override public void start() { outputThread.start(); inputThread.start(); } @Override public void stop() { outputThread.interrupt(); inputThread.interrupt(); try { socket.close(); } catch (IOException ioe) { Timber.w(ioe, "failed to close socket"); } } @Override public void sendMessage(String msg) { outputThread.sendMessage(msg); } @Override public void registerMessageListener(MessageListener listener, Handler handler) { messageListenerAdapter.registerTargetListener(listener, handler); } @Override public void unregisterMessageListener() { messageListenerAdapter.unregisterTargetListener(); } /** * Returns messages that were not delivered to any {@link org.faudroids.distributedmemory.network.ConnectionHandler.MessageListener} * due to none being registered via {@link #registerMessageListener(MessageListener, android.os.Handler)}. */ public List<String> getUndeliveredMessages() { return messageListenerAdapter.getUndeliveredMessages(); } /** * Returns whether there was a connection error that has not been delivered due to no listener * beeing registered. */ public boolean getUnsentConnectionError() { return messageListenerAdapter.getUnsentConnectionError(); } private static final class MessageListenerAdapter implements MessageListener<String> { private final List<String> unsentMessages = new LinkedList<>(); private boolean unsentConnectionError = false; private Handler handler = null; private MessageListener<String> targetListener = null; @Override public void onNewMessage(final String msg) { synchronized (this) { if (handler != null && targetListener != null) { handler.post(new Runnable() { @Override public void run() { targetListener.onNewMessage(msg); } }); } else { unsentMessages.add(msg); } } } @Override public void onConnectionError() { synchronized (this) { unsentMessages.clear(); if (handler != null && targetListener != null) { handler.post(new Runnable() { @Override public void run() { targetListener.onConnectionError(); } }); } else { unsentConnectionError = true; } } } public void registerTargetListener(MessageListener<String> targetListener, Handler handler) { synchronized (this) { this.targetListener = targetListener; this.handler = handler; } } public void unregisterTargetListener() { synchronized (this) { this.targetListener = null; this.handler = null; } } public List<String> getUndeliveredMessages() { synchronized (this) { List<String> messages = new LinkedList<>(unsentMessages); unsentMessages.clear(); return messages; } } public boolean getUnsentConnectionError() { boolean connectionError = unsentConnectionError; unsentConnectionError = false; return connectionError; } } private static final class OutputThread extends Thread { private final MessageListener messageListener; private final ObjectOutputStream objectOutputStream; private final BlockingQueue<String> messageQueue; private final BackoffStrategy strategy; public OutputThread(final OutputStream outputStream, final MessageListener messageListener) throws IOException { this.objectOutputStream = new ObjectOutputStream(outputStream); this.messageListener = messageListener; this.messageQueue = new LinkedBlockingDeque<>(); this.strategy = new BackoffStrategy() { @Override protected void doWork() throws Throwable { try { String msg = messageQueue.take(); objectOutputStream.writeObject(msg); objectOutputStream.flush(); } catch (InterruptedException ie) { interrupt(); } } }; } @Override public void run() { try { while (!isInterrupted()) { strategy.work(); } } catch (Throwable t) { try { objectOutputStream.close(); } catch (IOException ioe) { Timber.w("failed to close connection", ioe); } messageListener.onConnectionError(); } } public void sendMessage(String msg) { messageQueue.add(msg); } } private static final class InputThread extends Thread { private final MessageListener<String> messageListener; private final ObjectInputStream objectInputStream; private final BackoffStrategy strategy; public InputThread(final InputStream inputStream, final MessageListener messageListener) throws IOException { this.objectInputStream = new ObjectInputStream(inputStream); this.messageListener = messageListener; this.strategy = new BackoffStrategy() { @Override protected void doWork() throws Throwable { String msg = (String) objectInputStream.readObject(); messageListener.onNewMessage(msg); } }; } @Override public void run() { try { while (!isInterrupted()) { strategy.work(); } } catch (Throwable t) { try { objectInputStream.close(); } catch (IOException ioe) { Timber.w("failed to close connection", ioe); } messageListener.onConnectionError(); } } } /** * Implements a simple backoff strategy in case of errors. */ private static abstract class BackoffStrategy { private static final int MAX_RETRY = 3; private static final int INITIAL_BACKOFF = 20; private static final int BACKOFF_EXPONENT = 2; private int currentRetry = 0; private int currentBackoff = INITIAL_BACKOFF; public void work() throws Throwable { try { doWork(); // reset since doWork was successful currentRetry = 0; currentBackoff = 0; } catch (Throwable t) { Timber.d(t, "failed to perform work"); if (currentRetry > MAX_RETRY) throw new Exception("failed after " + MAX_RETRY + "retries", t); // backoff and retry ++currentRetry; currentBackoff *= BACKOFF_EXPONENT; try { Thread.sleep(currentBackoff); } catch (InterruptedException e) { throw new Exception(e); } } } protected abstract void doWork() throws Throwable; } }