Java tutorial
/** * @licence GNU General Public licence http://www.gnu.org/copyleft/gpl.html * @Copyright (C) 2012 Thom Wiggers * 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 * 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 org.marietjedroid.connect; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.net.URLEncoder; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Observable; import java.util.Queue; import java.util.concurrent.Semaphore; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.content.ContentBody; import org.apache.http.impl.client.DefaultHttpClient; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONTokener; /** * Handles most of the messaging aspect * * @author Thom * */ public abstract class MarietjeMessenger extends Observable { private HttpClient httpClient = new DefaultHttpClient(); final Lock lock = new ReentrantLock(); private Semaphore messageInSemaphore = new Semaphore(0); private Semaphore outSemaphore = new Semaphore(0); protected Boolean running = false; /** * Channel token */ private String token = null; private Queue<JSONObject> queueMessageIn = new LinkedList<JSONObject>(); private Queue<JSONObject> queueOut = new LinkedList<JSONObject>(); private Semaphore messagesInSemaphore = new Semaphore(1); private Integer nPending = 0; private final String host; private final String path; private final int port; private MarietjeException exception; public MarietjeMessenger(String host, int port, String path, String token) { this(host, port, path); this.token = token; } public MarietjeMessenger(String host, int port, String path) { this.host = host; this.port = port; this.path = path; this.token = generateToken(); } /** * Generates a new token * * @return a new token */ protected static String generateToken() { SecureRandom random = new SecureRandom(); while (true) { String attempt = new BigInteger(130, random).toString(); attempt = new String(org.apache.commons.codec.binary.Base64.encodeBase64(attempt.getBytes())) .substring(0, 7); return attempt; } } /** * Sends a stream * * FIXME probably massively broken * * @param token * @param stream * @param blocking */ public void sendStream(String token, ContentBody stream, boolean blocking) { if (!this.token.equals(token)) throw new IllegalArgumentException("Wrong token!"); MultipartEntity multipartStream = new MultipartEntity(); multipartStream.addPart("stream", stream); final HttpPost post = new HttpPost(String.format("http://%s:%s%s", host, port, path)); // FIXME sowieso stuk post.setEntity(multipartStream); Thread t = new Thread(new Runnable() { public void run() { try { httpClient.execute(post); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); t.start(); if (blocking) { try { t.join(); } catch (InterruptedException e) { } } } /** * Sends a blocking stream, probably massively broken * * FIXME * * @param token * @param stream */ public void sendStream(String token, ContentBody stream) { this.sendStream(token, stream, true); } /** * @param token * @param message */ public void sendMessage(JSONObject message) { this.queueOut.add(message); this.outSemaphore.release(); } /** * Acts on incoming messages * * @author Thom * */ private class MessageDispatcher implements Runnable { public void run() { while (running) { ArrayList<JSONObject> msgs; synchronized (queueMessageIn) { msgs = new ArrayList<JSONObject>(queueMessageIn); queueMessageIn.clear(); } for (JSONObject msg : msgs) { try { handleMessage(token, msg); } catch (JSONException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } try { messageInSemaphore.acquire(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } /** * Keeps polling marietje * * @author Thom * */ private class Requester implements Runnable { /* * (non-Javadoc) * * @see java.lang.Runnable#run() */ public void run() { while (running) { sendMessages(); } } } private void sendMessages() { JSONObject[] data = null; // do we need to execute the wait code? // needed for the synchronized. /*Boolean wait = false; synchronized (nPending) { if ((queueOut.isEmpty() || token == null) && nPending > 0) wait = true; } if (wait) { try { outSemaphore.acquire(); } catch (InterruptedException e) { } if (!running) return; sendMessages(); }*/ synchronized (nPending) { nPending++; } synchronized (queueOut) { if (!queueOut.isEmpty()) { data = queueOut.toArray(new JSONObject[0]); queueOut.clear(); } } if (data != null) { try { doRequest(Arrays.asList(data)); } catch (MarietjeException e) { stop(); } } else { try { doRequest(null); } catch (MarietjeException e) { stop(); } } synchronized (nPending) { nPending--; } synchronized (outSemaphore) { if (outSemaphore.availablePermits() < 0) { System.out.println("permits fixen!"); outSemaphore.release(-outSemaphore.availablePermits()); } } } /** * Starts running the threads * * @throws MarietjeException */ public void run() throws MarietjeException { if (this.running) throw new IllegalStateException("Already running"); this.running = true; if (this.token != null) { this.doRequest(null); } Thread t1 = new Thread(new MessageDispatcher()); t1.setDaemon(true); t1.start(); Thread t2 = new Thread(new Requester()); t2.setDaemon(true); t2.start(); } /** * Sends a request and handles the response * * @param list * @throws MarietjeException */ private void doRequest(List<JSONObject> list) throws MarietjeException { if (list != null) list = new ArrayList<JSONObject>(list); else list = new ArrayList<JSONObject>(); HttpClient httpClient = new DefaultHttpClient(); if (this.token == null) { throw new IllegalStateException("token is null"); } JSONArray json = new JSONArray(); json.put(token); for (JSONObject m : list) json.put(m); HttpGet hp = null; try { System.out.println("JSON: " + json.toString()); String url = String.format("http://%s:%s%s?m=%s", host, port, path, URLEncoder.encode(json.toString(), "UTF-8")); System.out.println("url: " + url); hp = new HttpGet(url); } catch (UnsupportedEncodingException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } StringBuilder sb = new StringBuilder(); try { HttpResponse r = httpClient.execute(hp); InputStreamReader is = new InputStreamReader(r.getEntity().getContent()); BufferedReader br = new BufferedReader(is); String line; while ((line = br.readLine()) != null) { System.out.println("response: " + line); sb.append(line); } } catch (IOException e) { MarietjeException tr = new MarietjeException("Connection stuk!" + e.getMessage()); this.exception = tr; throw tr; } JSONArray d = null; try { d = new JSONArray(new JSONTokener(sb.toString())); } catch (JSONException e) { throw (exception = new MarietjeException("Ja, JSON kapot!")); } if (d == null || d.length() != 3) throw (exception = new MarietjeException("Unexpected length of response list")); String token = null; JSONArray msgs = null; try { token = d.getString(0); msgs = d.getJSONArray(1); // JSONArray stream = d.getJSONArray(2); } catch (JSONException e) { throw (exception = new MarietjeException("unexpected format of response list")); } synchronized (this.outSemaphore) { String oldToken = this.token; this.token = token; if (oldToken == null) { this.outSemaphore.release(); } } for (int i = 0; i < msgs.length(); i++) { try { System.out.println("adding msg to queue"); synchronized (queueMessageIn) { this.queueMessageIn.add(msgs.getJSONObject(i)); } this.messageInSemaphore.release(); } catch (JSONException e) { System.err.println("ontvangen json kapot"); e.printStackTrace(); } } // TODO Streams left out. } /** * Immediately sends the messages. * * @param jsonObject */ protected void sendPriorityMessage(JSONObject jsonObject) { this.sendMessage(jsonObject); sendMessages(); } /** * Handle an imcoming message * * @param token * @param message */ protected abstract void handleMessage(String token, JSONObject message) throws JSONException; /** * Retrieve a stream * * * @param streamId */ protected abstract void retrieveStream(int streamId); /** * Stop */ public void stop() { System.out.println("Stopping"); this.running = false; this.messageInSemaphore.release(); this.outSemaphore.release(); } public boolean isRunning() { return running; } /** * @return the exception */ public MarietjeException getException() { return exception; } }