/* * Copyright (C) 2015 - Holy Lobster * * Nuntius is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Nuntius is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Nuntius. If not, see <http://www.gnu.org/licenses/>. */ package org.holylobster.nuntius.connection; import android.content.Context; import android.util.Log; import org.holylobster.nuntius.notifications.NotiHandler; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.Charset; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; public class Connection extends Thread { // Used for logging private final String TAG = this.getClass().getSimpleName(); private final BlockingQueue<Message> queue = new LinkedBlockingDeque<>(); private final Thread senderThread; private final Thread receiverThread; private final Socket socket; private final NotiHandler handler; private final String destination; boolean gracefulClose = false; public Connection(final Context context, final Socket socket, final NotiHandler handler) { this.socket = socket; this.handler = handler; this.destination = socket.getDestination(); senderThread = new Thread() { public void run() { try { OutputStream outputStream = new BufferedOutputStream(socket.getOutputStream()); while (checkConnected(socket) && !gracefulClose) { Message message = queue.take(); String json = message.toJSON(context); Log.i(TAG, "Sending message (size " + json.length() + ")"); outputStream.write(json.getBytes()); outputStream.write('\r'); outputStream.write('\n'); outputStream.flush(); } } catch (InterruptedException e) { Log.i(TAG, "Sender thread interrupted while waiting for a message"); } catch (IOException e) { Log.e(TAG, "Error in sender thread", e); } Log.i(TAG, "Sender thread is closing..."); cleanup(socket); } private boolean checkConnected(Socket socket) { return socket != null && socket.isConnected(); } }; receiverThread = new Thread() { public void run() { ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); try { InputStream inputStream = new BufferedInputStream(socket.getInputStream()); while (checkConnected(socket) && !gracefulClose) { int c; if ((c = inputStream.read()) == -1) { throw new IOException("End of input stream reached"); } baos.write((byte) c); if (c == '\n') { String data = new String(baos.toByteArray(), Charset.forName("UTF-8")); Log.i(TAG, "Read " + data.length() + " chars"); Log.d(TAG, "Read : " + data); try { IncomingMessage message = new IncomingMessage(data); handler.onMessageReceived(message); } catch (IOException e) { Log.e(TAG, "Unable to parse: " + data); } baos.reset(); } } } catch (IOException e) { Log.e(TAG, "Error in receiver thread", e); } Log.i(TAG, "Receiver thread is closing..."); cleanup(socket); } private boolean checkConnected(Socket socket) { return socket != null && socket.isConnected(); } }; receiverThread.start(); senderThread.start(); } private void cleanup(Socket socket) { Log.i(TAG, "Cleanup of connection resources..."); if (socket != null) { try { InputStream inputStream = socket.getInputStream(); try { inputStream.close(); } catch (IOException e) { } } catch (IOException e) { } try { OutputStream outputStream = socket.getOutputStream(); try { outputStream.flush(); outputStream.close(); } catch (IOException e) { } } catch (IOException e) { } try { socket.close(); } catch (IOException e) { Log.e(TAG, "Error closing socket", e); } } queue.clear(); Log.i(TAG, "Cleanup completed"); handler.onConnectionClosed(this); } public boolean enqueue(Message m) { return queue.offer(m); } public void close() { gracefulClose = true; // Wait for max 250 * 10 = 2.5s for threads to gracefully close for (int i = 0; i < 10 && (senderThread.isAlive() || receiverThread.isAlive()); i++) { try { Thread.sleep(250); } catch (InterruptedException e) { } } // Kill threads if the did not close in time if (senderThread.isAlive()) { senderThread.interrupt(); } if (receiverThread.isAlive()) { receiverThread.interrupt(); } cleanup(socket); } public String getDestination() { return destination; } }