Java tutorial
/* * LEDD Project * Copyright (C) 2015 LEDD Team * * This program 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 3 of the License, or * (at your option) any later version. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package com.idlegandalf.ledd.services; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import com.idlegandalf.ledd.components.AnswerTask; import com.idlegandalf.ledd.components.LedDDaemon; import com.idlegandalf.ledd.components.Sendable; import com.koushikdutta.async.AsyncServer; import com.koushikdutta.async.AsyncSocket; import com.koushikdutta.async.ByteBufferList; import com.koushikdutta.async.DataEmitter; import com.koushikdutta.async.Util; import com.koushikdutta.async.callback.ConnectCallback; import com.koushikdutta.async.callback.DataCallback; import com.thetransactioncompany.jsonrpc2.JSONRPC2ParseException; import com.thetransactioncompany.jsonrpc2.JSONRPC2Request; import com.thetransactioncompany.jsonrpc2.JSONRPC2Response; import org.json.JSONException; import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class ColorService extends Service { private static ScheduledThreadPoolExecutor poolExecutor = new ScheduledThreadPoolExecutor(5); private final IBinder mBinder = new ColorBinder(); private LinkedBlockingQueue<Sendable> queue; private Worker<Sendable> worker; @Override public int onStartCommand(Intent intent, int flags, int startId) { return -1; } @Override public IBinder onBind(Intent intent) { queue = new LinkedBlockingQueue<>(); worker = new Worker<>(queue); new Thread(worker).start(); return mBinder; } @Override public void onDestroy() { worker.stop(); poolExecutor.shutdownNow(); } private static class Worker<T> implements Runnable { private final LinkedBlockingQueue<T> workQueue; private HashMap<LedDDaemon, AsyncSocket> socketHashMap; private HashMap<String, Sendable> sendableHashMap; private HashMap<String, ScheduledFuture> timeoutHashMap; DataCallback dataCallback = new DataCallback() { @Override public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) { JSONRPC2Response reqIn = null; String dataStr = new String(bb.getAllByteArray()); if (!dataStr.isEmpty()) { try { reqIn = JSONRPC2Response.parse(dataStr); } catch (JSONRPC2ParseException e) { e.printStackTrace(); } } if (reqIn != null) { if (sendableHashMap.containsKey(reqIn.getID().toString())) { sendableHashMap.get(reqIn.getID().toString()).onResponse(reqIn); sendableHashMap.remove(reqIn.getID().toString()); if (timeoutHashMap.containsKey(reqIn.getID().toString()) && timeoutHashMap.get(reqIn.getID().toString()) != null) timeoutHashMap.get(reqIn.getID().toString()).cancel(false); timeoutHashMap.remove(reqIn.getID().toString()); } } } }; public Worker(LinkedBlockingQueue<T> workQueue) { this.workQueue = workQueue; this.socketHashMap = new HashMap<>(); this.sendableHashMap = new HashMap<>(); this.timeoutHashMap = new HashMap<>(); } @Override public void run() { while (!Thread.currentThread().isInterrupted()) { try { final T item = workQueue.take(); if (item instanceof Sendable) { final Sendable sendable = (Sendable) item; if (socketHashMap.containsKey(sendable.getRecipient())) { if (socketHashMap.get(sendable.getRecipient()) == null) { // if server is not running yet, readd item to queue workQueue.add(item); continue; } else if (!socketHashMap.get(sendable.getRecipient()).getServer().isRunning()) { // connection probably closed or was interrupted -> reconnect socketHashMap.remove(sendable.getRecipient()); workQueue.add(item); continue; } Util.writeAll(socketHashMap.get(sendable.getRecipient()), (sendable.getRequest().toString() + "\n").getBytes("UTF-8"), null); sendableHashMap.put((String) sendable.getRequest().getID(), sendable); if (!poolExecutor.isTerminating() && !poolExecutor.isTerminated()) timeoutHashMap.put((String) sendable.getRequest().getID(), poolExecutor.schedule(new Runnable() { @Override public void run() { sendable.onNoResponse(); timeoutHashMap.remove(sendable.getRequest().getID()); sendableHashMap.remove(sendable.getRequest().getID()); } }, 1000, TimeUnit.MILLISECONDS)); } else { socketHashMap.put(sendable.getRecipient(), null); AsyncServer.getDefault() .connectSocket(new InetSocketAddress(sendable.getRecipient().getAddress(), sendable.getRecipient().getPort()), new ConnectCallback() { @Override public void onConnectCompleted(Exception ex, final AsyncSocket socket) { if (ex == null) { socket.setDataCallback(dataCallback); socketHashMap.put(sendable.getRecipient(), socket); //if (!workQueue.contains(item)) -> needs equals implementation workQueue.add(item); } else { sendable.onConnectionFailed(ex.getMessage()); } } }); } } } catch (InterruptedException ex) { Thread.currentThread().interrupt(); break; } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } public void stop() { Thread.currentThread().interrupt(); } } public class ColorBinder extends Binder { public void queueSend(LedDDaemon rec, JSONRPC2Request request, AnswerTask answerTask) { Sendable sendable = null; try { sendable = new Sendable(request, answerTask, rec); } catch (JSONException e) { e.printStackTrace(); } if (sendable != null) { queue.add(sendable); } } } }