From ba7132f3ce5629cff2cc4857fff7bd672511bee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kjetil=20=C3=98rbekk?= Date: Fri, 13 Jan 2012 10:18:02 +0100 Subject: Rename projects. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit – jsonrpc → same – master → same-android --- jsonrpc/pom.xml | 95 --------- .../java/com/orbekk/net/BroadcastListener.java | 53 ----- .../src/main/java/com/orbekk/net/Broadcaster.java | 77 ------- jsonrpc/src/main/java/com/orbekk/net/HttpUtil.java | 26 --- jsonrpc/src/main/java/com/orbekk/rpc/App.java | 47 ----- jsonrpc/src/main/java/com/orbekk/rpc/Client.java | 66 ------ .../src/main/java/com/orbekk/rpc/RpcHandler.java | 52 ----- .../java/com/orbekk/same/ConnectionManager.java | 10 - .../com/orbekk/same/ConnectionManagerImpl.java | 31 --- .../src/main/java/com/orbekk/same/SameService.java | 40 ---- .../main/java/com/orbekk/same/SameServiceImpl.java | 60 ------ .../src/main/java/com/orbekk/same/SameState.java | 233 --------------------- jsonrpc/src/main/java/com/orbekk/same/State.java | 78 ------- .../src/main/java/com/orbekk/same/UrlReceiver.java | 10 - jsonrpc/src/main/resources/log4j.properties | 7 - .../test/java/com/orbekk/same/SameStateTest.java | 105 ---------- master/AndroidManifest.xml | 17 -- master/pom.xml | 139 ------------ master/project.properties | 11 - master/src/main/java/com/orbekk/Broadcast.java | 56 ----- .../main/java/com/orbekk/HelloAndroidActivity.java | 49 ----- master/src/main/java/com/orbekk/PingServer.java | 51 ----- same-android/AndroidManifest.xml | 17 ++ same-android/pom.xml | 139 ++++++++++++ same-android/project.properties | 11 + .../src/main/java/com/orbekk/Broadcast.java | 56 +++++ .../main/java/com/orbekk/HelloAndroidActivity.java | 49 +++++ .../src/main/java/com/orbekk/PingServer.java | 51 +++++ same/pom.xml | 95 +++++++++ .../java/com/orbekk/net/BroadcastListener.java | 53 +++++ same/src/main/java/com/orbekk/net/Broadcaster.java | 77 +++++++ same/src/main/java/com/orbekk/net/HttpUtil.java | 26 +++ same/src/main/java/com/orbekk/rpc/App.java | 47 +++++ same/src/main/java/com/orbekk/rpc/Client.java | 66 ++++++ same/src/main/java/com/orbekk/rpc/RpcHandler.java | 52 +++++ .../java/com/orbekk/same/ConnectionManager.java | 10 + .../com/orbekk/same/ConnectionManagerImpl.java | 31 +++ .../src/main/java/com/orbekk/same/SameService.java | 40 ++++ .../main/java/com/orbekk/same/SameServiceImpl.java | 60 ++++++ same/src/main/java/com/orbekk/same/SameState.java | 233 +++++++++++++++++++++ same/src/main/java/com/orbekk/same/State.java | 78 +++++++ .../src/main/java/com/orbekk/same/UrlReceiver.java | 10 + same/src/main/resources/log4j.properties | 7 + .../test/java/com/orbekk/same/SameStateTest.java | 105 ++++++++++ 44 files changed, 1313 insertions(+), 1313 deletions(-) delete mode 100644 jsonrpc/pom.xml delete mode 100644 jsonrpc/src/main/java/com/orbekk/net/BroadcastListener.java delete mode 100644 jsonrpc/src/main/java/com/orbekk/net/Broadcaster.java delete mode 100644 jsonrpc/src/main/java/com/orbekk/net/HttpUtil.java delete mode 100644 jsonrpc/src/main/java/com/orbekk/rpc/App.java delete mode 100644 jsonrpc/src/main/java/com/orbekk/rpc/Client.java delete mode 100644 jsonrpc/src/main/java/com/orbekk/rpc/RpcHandler.java delete mode 100644 jsonrpc/src/main/java/com/orbekk/same/ConnectionManager.java delete mode 100644 jsonrpc/src/main/java/com/orbekk/same/ConnectionManagerImpl.java delete mode 100644 jsonrpc/src/main/java/com/orbekk/same/SameService.java delete mode 100644 jsonrpc/src/main/java/com/orbekk/same/SameServiceImpl.java delete mode 100644 jsonrpc/src/main/java/com/orbekk/same/SameState.java delete mode 100644 jsonrpc/src/main/java/com/orbekk/same/State.java delete mode 100644 jsonrpc/src/main/java/com/orbekk/same/UrlReceiver.java delete mode 100644 jsonrpc/src/main/resources/log4j.properties delete mode 100644 jsonrpc/src/test/java/com/orbekk/same/SameStateTest.java delete mode 100644 master/AndroidManifest.xml delete mode 100644 master/pom.xml delete mode 100644 master/project.properties delete mode 100644 master/src/main/java/com/orbekk/Broadcast.java delete mode 100644 master/src/main/java/com/orbekk/HelloAndroidActivity.java delete mode 100644 master/src/main/java/com/orbekk/PingServer.java create mode 100644 same-android/AndroidManifest.xml create mode 100644 same-android/pom.xml create mode 100644 same-android/project.properties create mode 100644 same-android/src/main/java/com/orbekk/Broadcast.java create mode 100644 same-android/src/main/java/com/orbekk/HelloAndroidActivity.java create mode 100644 same-android/src/main/java/com/orbekk/PingServer.java create mode 100644 same/pom.xml create mode 100644 same/src/main/java/com/orbekk/net/BroadcastListener.java create mode 100644 same/src/main/java/com/orbekk/net/Broadcaster.java create mode 100644 same/src/main/java/com/orbekk/net/HttpUtil.java create mode 100644 same/src/main/java/com/orbekk/rpc/App.java create mode 100644 same/src/main/java/com/orbekk/rpc/Client.java create mode 100644 same/src/main/java/com/orbekk/rpc/RpcHandler.java create mode 100644 same/src/main/java/com/orbekk/same/ConnectionManager.java create mode 100644 same/src/main/java/com/orbekk/same/ConnectionManagerImpl.java create mode 100644 same/src/main/java/com/orbekk/same/SameService.java create mode 100644 same/src/main/java/com/orbekk/same/SameServiceImpl.java create mode 100644 same/src/main/java/com/orbekk/same/SameState.java create mode 100644 same/src/main/java/com/orbekk/same/State.java create mode 100644 same/src/main/java/com/orbekk/same/UrlReceiver.java create mode 100644 same/src/main/resources/log4j.properties create mode 100644 same/src/test/java/com/orbekk/same/SameStateTest.java diff --git a/jsonrpc/pom.xml b/jsonrpc/pom.xml deleted file mode 100644 index 7d2bba7..0000000 --- a/jsonrpc/pom.xml +++ /dev/null @@ -1,95 +0,0 @@ - - 4.0.0 - - com.orbekk - jsonrpc - 0.0-SNAPSHOT - jar - - jsonrpc - http://maven.apache.org - - - UTF-8 - - - - - jsonrpc3j-webdav-maven-repo - jsonrpc4j maven repository - http://jsonrpc4j.googlecode.com/svn/maven/repo/ - default - - - - - - com.googlecode - jsonrpc4j - 0.18 - - - - org.codehaus.jackson - jackson-mapper-asl - 1.7.5 - - - - junit - junit - 4.10 - test - - - - org.slf4j - slf4j-api - 1.6.4 - - - - org.slf4j - slf4j-log4j12 - 1.6.4 - - - - javax.servlet - servlet-api - 2.5 - - - - javax.portlet - portlet-api - 2.0 - - - - org.eclipse.jetty - jetty-servlet - 8.0.0.M3 - - - - org.eclipse.jetty - jetty-server - 8.0.0.M3 - - - - - - - maven-compiler-plugin - 2.3.2 - - 1.6 - 1.6 - - - - - diff --git a/jsonrpc/src/main/java/com/orbekk/net/BroadcastListener.java b/jsonrpc/src/main/java/com/orbekk/net/BroadcastListener.java deleted file mode 100644 index c0b66e0..0000000 --- a/jsonrpc/src/main/java/com/orbekk/net/BroadcastListener.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.orbekk.net; - -import java.io.IOException; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.SocketException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class BroadcastListener { - private int port; - private Logger logger = LoggerFactory.getLogger(getClass()); - - public BroadcastListener(int port) { - this.port = port; - } - - public DatagramPacket listen() { - logger.debug("Waiting for broadcast on port " + port); - DatagramSocket socket; - try { - socket = new DatagramSocket(port); - } catch (SocketException e) { - logger.warn("Failed to create socket.", e.fillInStackTrace()); - return null; - } - try { - socket.setBroadcast(true); - } catch (SocketException e) { - logger.warn("Exception: {}", e); - } - byte[] buffer = new byte[2048]; - DatagramPacket packet = new DatagramPacket(buffer, buffer.length); - try { - socket.receive(packet); - } catch (IOException e) { - logger.warn("Exception when listening for broadcast: {}", e); - return null; - } - - String address = packet.getAddress().getHostAddress(); - logger.debug("Received broadcast from " + address + - ": " + new String(packet.getData())); - return packet; - } - - public static void main(String[] args) { - int port = Integer.parseInt(args[0]); - BroadcastListener listener = new BroadcastListener(port); - System.out.println("Received broadcast: " + listener.listen()); - } -} diff --git a/jsonrpc/src/main/java/com/orbekk/net/Broadcaster.java b/jsonrpc/src/main/java/com/orbekk/net/Broadcaster.java deleted file mode 100644 index 95e279c..0000000 --- a/jsonrpc/src/main/java/com/orbekk/net/Broadcaster.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.orbekk.net; - -import java.io.IOException; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.InetAddress; -import java.net.InterfaceAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.util.Enumeration; -import java.util.LinkedList; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -public class Broadcaster { - private Logger logger = LoggerFactory.getLogger(getClass()); - - public List getBroadcastAddresses() { - List broadcastAddresses = new LinkedList(); - - Enumeration interfaces; - try { - interfaces = NetworkInterface.getNetworkInterfaces(); - } catch (SocketException e) { - logger.warn("Network problem?", e.fillInStackTrace()); - return broadcastAddresses; - } - while (interfaces.hasMoreElements()) { - NetworkInterface iface = interfaces.nextElement(); - try { - if (iface.isLoopback()) { - logger.debug("Ignoring looback device " + iface.getName()); - continue; - } - for (InterfaceAddress address : iface.getInterfaceAddresses()) { - InetAddress broadcast = address.getBroadcast(); - if (broadcast != null) { - broadcastAddresses.add(broadcast); - } - } - } catch (SocketException e) { - logger.info("Ignoring interface " + iface.getName(), e.fillInStackTrace()); - } - } - return broadcastAddresses; - } - - public boolean sendBroadcast(int port, byte[] data) { - boolean successful = false; - for (InetAddress broadcastAddress : getBroadcastAddresses()) { - try { - DatagramSocket socket = new DatagramSocket(); - socket.setBroadcast(true); - DatagramPacket packet = new DatagramPacket(data, data.length, broadcastAddress, port); - socket.send(packet); - successful = true; - } catch (SocketException e) { - logger.warn("Failed to send broadcast to " + broadcastAddress + - ". ", e.fillInStackTrace()); - } catch (IOException e) { - logger.warn("Error when sending broadcast to " + - broadcastAddress + ".", e.fillInStackTrace()); - } - } - return successful; - } - - public static void main(String[] args) { - int port = Integer.parseInt(args[0]); - Broadcaster broadcaster = new Broadcaster(); - String message = "Broadcast from Java broadcaster."; - broadcaster.sendBroadcast(port, message.getBytes()); - } -} diff --git a/jsonrpc/src/main/java/com/orbekk/net/HttpUtil.java b/jsonrpc/src/main/java/com/orbekk/net/HttpUtil.java deleted file mode 100644 index b4bb887..0000000 --- a/jsonrpc/src/main/java/com/orbekk/net/HttpUtil.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.orbekk.net; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLConnection; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class HttpUtil { - private static final Logger logger = - LoggerFactory.getLogger(HttpUtil.class); - - public static void sendHttpRequest(String url) { - try { - URL pingUrl = new URL(url); - pingUrl.openStream(); - // URLConnection connection = pingUrl.openConnection(); - // connection.connect(); - } catch (MalformedURLException e) { - logger.warn("Unable to send ping to {}: {}.", url, e); - } catch (IOException e) { - logger.warn("Error when sending ping: {}", e); - } - } -} diff --git a/jsonrpc/src/main/java/com/orbekk/rpc/App.java b/jsonrpc/src/main/java/com/orbekk/rpc/App.java deleted file mode 100644 index 0e9dce3..0000000 --- a/jsonrpc/src/main/java/com/orbekk/rpc/App.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.orbekk.rpc; - -import com.googlecode.jsonrpc4j.JsonRpcServer; -import com.orbekk.same.ConnectionManagerImpl; -import com.orbekk.same.SameState; -import com.orbekk.same.SameService; -import com.orbekk.same.SameServiceImpl; -import org.eclipse.jetty.server.Server; - -public class App { - public static void main(String[] args) { - if (args.length < 3) { - System.err.println("Arguments: port networkName clientId"); - System.exit(1); - } - int port = Integer.parseInt(args[0]); - String networkName = args[1]; - String clientId = args[2]; - - ConnectionManagerImpl connections = new ConnectionManagerImpl(); - - SameState sameState = new SameState(networkName, clientId, - connections); - sameState.start(); - - SameServiceImpl service = new SameServiceImpl(sameState); - JsonRpcServer jsonServer = new JsonRpcServer(service, - SameService.class); - - Server server = new Server(port); - RpcHandler rpcHandler = new RpcHandler(jsonServer, sameState); - server.setHandler(rpcHandler); - - try { - server.start(); - } catch (Exception e) { - System.out.println("Could not start jetty server."); - e.printStackTrace(); - } - - try { - server.join(); - } catch (InterruptedException e) { - System.out.println("Interrupt"); - } - } -} diff --git a/jsonrpc/src/main/java/com/orbekk/rpc/Client.java b/jsonrpc/src/main/java/com/orbekk/rpc/Client.java deleted file mode 100644 index 225347a..0000000 --- a/jsonrpc/src/main/java/com/orbekk/rpc/Client.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.orbekk.rpc; - -import com.googlecode.jsonrpc4j.JsonRpcServer; -import com.orbekk.same.ConnectionManagerImpl; -import com.orbekk.same.SameState; -import com.orbekk.same.SameService; -import com.orbekk.same.SameServiceImpl; -import com.orbekk.net.HttpUtil; -import org.eclipse.jetty.server.Server; - -public class Client { - - public static void main(String[] args) { - if (args.length < 4) { - System.err.println("Arguments: port clientId thisNetworkName " + - "remoteNetworkAddr"); - System.exit(1); - } - int port = Integer.parseInt(args[0]); - String clientId = args[1]; - String networkName = args[2]; - String remoteAddr = args[3]; - - ConnectionManagerImpl connections = new ConnectionManagerImpl(); - - SameState sameState = new SameState(networkName, clientId, - connections); - sameState.start(); - - SameServiceImpl service = new SameServiceImpl(sameState); - JsonRpcServer jsonServer = new JsonRpcServer(service, - SameService.class); - - Server server = new Server(port); - RpcHandler rpcHandler = new RpcHandler(jsonServer, sameState); - server.setHandler(rpcHandler); - - try { - server.start(); - } catch (Exception e) { - System.out.println("Could not start jetty server."); - e.printStackTrace(); - } - - while (sameState.getUrl() == null) { - HttpUtil.sendHttpRequest(remoteAddr + "ping?port=" + port); - try { - Thread.sleep(500); - } catch (InterruptedException e) { - // Ignore interrupt in wait loop. - } - } - - SameService remoteService = connections.getConnection(remoteAddr); - remoteService.notifyNetwork("NoNetwork"); - remoteService.participateNetwork("FirstNetwork", - sameState.getClientId(), sameState.getUrl()); - - try { - server.join(); - } catch (InterruptedException e) { - System.out.println("Interrupt"); - } - - } -} diff --git a/jsonrpc/src/main/java/com/orbekk/rpc/RpcHandler.java b/jsonrpc/src/main/java/com/orbekk/rpc/RpcHandler.java deleted file mode 100644 index 0bafad4..0000000 --- a/jsonrpc/src/main/java/com/orbekk/rpc/RpcHandler.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.orbekk.rpc; - -import com.googlecode.jsonrpc4j.JsonRpcServer; -import com.orbekk.net.HttpUtil; -import com.orbekk.same.UrlReceiver; -import java.io.IOException; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class RpcHandler extends AbstractHandler { - private Logger logger = LoggerFactory.getLogger(getClass()); - private JsonRpcServer rpcServer; - private UrlReceiver urlReceiver; - - public RpcHandler(JsonRpcServer rpcServer, - UrlReceiver urlReceiver) { - this.rpcServer = rpcServer; - this.urlReceiver = urlReceiver; - } - - @Override - public synchronized void handle(String target, Request baseRequest, - HttpServletRequest request, HttpServletResponse response) - throws IOException, ServletException { - logger.info("Handling request to target: " + target); - - if (urlReceiver != null) { - String sameServiceUrl = "http://" + request.getLocalAddr() + - ":" + request.getLocalPort() + "/SameService.json"; - urlReceiver.setUrl(sameServiceUrl); - urlReceiver = null; - } - - if (target.equals("/ping")) { - int remotePort = Integer.parseInt(request.getParameter("port")); - String pongUrl = "http://" + request.getRemoteAddr() + ":" + - remotePort + "/pong"; - logger.info("Got ping. Sending pong to {}", pongUrl); - HttpUtil.sendHttpRequest(pongUrl); - } else if (target.equals("/pong")) { - logger.info("Received pong from {}", request.getRemoteAddr()); - } else { - rpcServer.handle(request, response); - } - baseRequest.setHandled(true); - } -} diff --git a/jsonrpc/src/main/java/com/orbekk/same/ConnectionManager.java b/jsonrpc/src/main/java/com/orbekk/same/ConnectionManager.java deleted file mode 100644 index 985f6f0..0000000 --- a/jsonrpc/src/main/java/com/orbekk/same/ConnectionManager.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.orbekk.same; - -/** - * An interface that returns a connection for a participant. - * - * When testing, this interface can be mocked to use local participants only. - */ -public interface ConnectionManager { - SameService getConnection(String url); -} diff --git a/jsonrpc/src/main/java/com/orbekk/same/ConnectionManagerImpl.java b/jsonrpc/src/main/java/com/orbekk/same/ConnectionManagerImpl.java deleted file mode 100644 index 841d5fa..0000000 --- a/jsonrpc/src/main/java/com/orbekk/same/ConnectionManagerImpl.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.orbekk.same; - -import com.googlecode.jsonrpc4j.JsonRpcHttpClient; -import com.googlecode.jsonrpc4j.ProxyUtil; -import java.net.MalformedURLException; -import java.net.URL; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ConnectionManagerImpl implements ConnectionManager { - - private Logger logger = LoggerFactory.getLogger(getClass()); - - public ConnectionManagerImpl() { - } - - @Override - public SameService getConnection(String url) { - SameService service = null; - try { - JsonRpcHttpClient client = new JsonRpcHttpClient(new URL(url)); - service = ProxyUtil.createProxy( - this.getClass().getClassLoader(), - SameService.class, - client); - } catch (MalformedURLException e) { - logger.warn("Unable to create client for {}, {}", url, e); - } - return service; - } -} diff --git a/jsonrpc/src/main/java/com/orbekk/same/SameService.java b/jsonrpc/src/main/java/com/orbekk/same/SameService.java deleted file mode 100644 index 8f239da..0000000 --- a/jsonrpc/src/main/java/com/orbekk/same/SameService.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.orbekk.same; - -import java.util.Map; - -public interface SameService { - /** - * A notification that 'networkName' exists. - * - * This is called by any participant of a network after a broadcast - * has been performed. - */ - void notifyNetwork(String networkName); - - /** - * A request from the callee to participate in 'networkName'. - */ - void participateNetwork(String networkName, String clientId, String url); - - /** - * Notification of participation in network. - */ - void notifyParticipation(String networkName, String masterId); - - /** - * New state. - * - * When sent to a non-master from the master, use 'newState' as the - * current state. - * - * When sent to a master, broadcast the new state to all clients. - */ - void setState(String newState); - - /** - * Notify all nodes of network participants. - * - * Only sent from master to non-master. - */ - void setParticipants(Map participants); -} diff --git a/jsonrpc/src/main/java/com/orbekk/same/SameServiceImpl.java b/jsonrpc/src/main/java/com/orbekk/same/SameServiceImpl.java deleted file mode 100644 index 27579b5..0000000 --- a/jsonrpc/src/main/java/com/orbekk/same/SameServiceImpl.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.orbekk.same; - -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SameServiceImpl implements SameService { - private Logger logger = LoggerFactory.getLogger(getClass()); - private SameState sameState; - - public SameServiceImpl(SameState sameState) { - this.sameState = sameState; - } - - @Override - public void notifyNetwork(String networkName) { - logger.info("Notification from network " + networkName); - } - - @Override - public void participateNetwork(String networkName, String clientId, - String url) { - if (!networkName.equals(sameState.getNetworkName())) { - logger.warn("Client tried to join {}, but network name is {}.", - networkName, sameState.getNetworkName()); - return; - } - if (clientId.equals("") || url.equals("")) { - logger.warn("Missing client info: ClientId({}), URL({}).", - clientId, url); - return; - } - sameState.addParticipant(clientId, url); - } - - @Override - public void notifyParticipation(String networkName, String masterId) { - logger.info("Joining network {}. Master is {}", networkName, masterId); - // int i = 1; - // for (Map.Entry e : participants.entrySet()) { - // String clientId = e.getKey(); - // String url = e.getValue(); - // logger.info(" {} participant {}: {}, {}", - // new Object[]{networkName, i, clientId, url}); - // i++; - // } - sameState.joinNetwork(networkName, masterId); - } - - @Override - public void setParticipants(Map participants) { - sameState.setParticipants(participants); - } - - @Override - public void setState(String newState) { - sameState.setState(newState); - } -} diff --git a/jsonrpc/src/main/java/com/orbekk/same/SameState.java b/jsonrpc/src/main/java/com/orbekk/same/SameState.java deleted file mode 100644 index c9cd216..0000000 --- a/jsonrpc/src/main/java/com/orbekk/same/SameState.java +++ /dev/null @@ -1,233 +0,0 @@ -package com.orbekk.same; - -import java.util.List; -import java.util.LinkedList; -import java.util.Map; -import java.util.HashMap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The implementation of a 'Same' state. - * - * This class manages the current state of the Same protocol. - */ -public class SameState extends Thread implements UrlReceiver { - private Logger logger = LoggerFactory.getLogger(getClass()); - private ConnectionManager connections; - - // TODO: Change the name of State. - private com.orbekk.same.State state = - new com.orbekk.same.State(); - - /** - * The client id of this participant. - */ - private String clientId; - - /** - * Stopping condition for this thread. - */ - private boolean stopped = false; - - private String _setState = null; - private Map _setParticipants = null; - - private Map pendingParticipants = - new HashMap(); - - public SameState(String networkName, String clientId, - ConnectionManager connections) { - state.setNetworkName(networkName); - this.clientId = clientId; - this.connections = connections; - state.setMasterId(clientId); - state.getParticipants().put(clientId, null); - } - - public String getMasterId() { - return state.getMasterId(); - } - - public synchronized Map getParticipants() { - return state.getParticipants(); - } - - /** - * Reset this SameService to an initial state. - * - * TODO: Implement fully. - */ - private synchronized void resetState() { - state = new com.orbekk.same.State(); - pendingParticipants.clear(); - } - - public synchronized void joinNetwork(String networkName, String masterId) { - resetState(); - state.setNetworkName(networkName); - state.setMasterId(masterId); - logger.info("Joined network {}.", networkName); - } - - public String getClientId() { - return clientId; - } - - public String getNetworkName() { - return state.getNetworkName(); - } - - public String getCurrentState() { - return state.getData(); - } - - /** - * TODO: Move to a separate library. - */ - public void librarySetNewState(String newState) { - connections.getConnection( - state.getParticipants().get(state.getMasterId())) - .setState(newState); - } - - public String getUrl() { - return state.getParticipants().get(clientId); - } - - @Override - public void setUrl(String url) { - logger.info("My URL is {}", url); - state.getParticipants().put(clientId, url); - } - - public synchronized void addParticipant(String clientId, String url) { - logger.info("PendingParticipant.add: {} ({})", clientId, url); - pendingParticipants.put(clientId, url); - notifyAll(); - } - - public synchronized void setParticipants(Map participants) { - logger.info("Pending operation: _setParticipants"); - _setParticipants = participants; - notifyAll(); - } - - public synchronized void setState(String newState) { - logger.info("Pending operation: _setState"); - _setState = newState; - notifyAll(); - } - - private synchronized void handleSetParticipants() { - if (_setParticipants != null) { - if (isMaster()) { - logger.error("{}: Master received setParticipants.", clientId); - } else { - logger.info("{}: New participants committed.", clientId); - state.getParticipants().clear(); - state.getParticipants().putAll(_setParticipants); - } - } - _setParticipants = null; - } - - public synchronized void handleSetState() { - if (_setState != null) { - if (isMaster()) { - broadcast(new ServiceOperation() { - @Override void run(SameService service) { - service.setState(_setState); - } - }); - } - state.setData(_setState); - _setState = null; - } - } - - private boolean isMaster() { - return state.getMasterId().equals(clientId); - } - - private synchronized void handleNewParticipants() { - if (!isMaster()) { - for (Map.Entry e : pendingParticipants.entrySet()) { - SameService master = connections.getConnection( - state.getParticipants().get(state.getMasterId())); - logger.info("Redirecting participant request to {}", - state.getMasterId()); - String clientId = e.getKey(); - String url = e.getValue(); - master.participateNetwork(state.getNetworkName(), clientId, - url); - } - } else { - state.getParticipants().putAll(pendingParticipants); - for (Map.Entry e : - pendingParticipants.entrySet()) { - String clientId = e.getKey(); - String url = e.getValue(); - logger.info("New participant: {} URL({})", clientId, url); - SameService remoteService = connections.getConnection(url); - remoteService.notifyParticipation(state.getNetworkName(), - state.getMasterId()); - broadcast(new ServiceOperation(){ - @Override void run(SameService service) { - service.setParticipants(state.getParticipants()); - } - }); - } - } - pendingParticipants.clear(); - } - - /** - * This method runs the pending commands to SameState. - * - * It should be called by the worker thread, but can be called directly - * for testing purposes to avoid threading in unit tests. - */ - synchronized void internalRun() { - handleNewParticipants(); - handleSetState(); - handleSetParticipants(); - } - - public synchronized void run() { - while (!stopped) { - internalRun(); - try { - wait(1000); - } catch (InterruptedException e) { - // Ignore interrupt in wait loop. - } - } - } - - public synchronized void stopSame() { - try { - stopped = true; - notifyAll(); - this.join(); - } catch (InterruptedException e) { - logger.warn("Got InterruptedException while waiting for SameState " + - "to finish. Ignoring."); - } - } - - public abstract static class ServiceOperation { - abstract void run(SameService service); - } - - public synchronized void broadcast(ServiceOperation operation) { - for (Map.Entry e : - state.getParticipants().entrySet()) { - String clientId = e.getKey(); - String url = e.getValue(); - if (!clientId.equals(this.clientId)) { - operation.run(connections.getConnection(url)); - } - } - } -} diff --git a/jsonrpc/src/main/java/com/orbekk/same/State.java b/jsonrpc/src/main/java/com/orbekk/same/State.java deleted file mode 100644 index 70ffa83..0000000 --- a/jsonrpc/src/main/java/com/orbekk/same/State.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.orbekk.same; - -import java.util.Map; -import java.util.HashMap; - -public class State { - private long stateIteration = 0; - private Map participants = new HashMap(); - private String networkName = ""; - private String masterId = ""; - private String data = ""; - - public long getStateIteration() { - return stateIteration; - } - - public void setStateIteration(long stateIteration) { - this.stateIteration = stateIteration; - } - - public Map getParticipants() { - return participants; - } - - public String getNetworkName() { - return networkName; - } - - public void setNetworkName(String networkName) { - this.networkName = networkName; - } - - public String getMasterId() { - return masterId; - } - - public void setMasterId(String masterId) { - this.masterId = masterId; - } - - public String getData() { - return data; - } - - public void setData(String data) { - this.data = data; - } - - @Override - public String toString() { - StringBuilder participantsString = new StringBuilder(); - participantsString.append("["); - boolean first = true; - for (Map.Entry e : participants.entrySet()) { - if (!first) { - participantsString.append(", "); - } - first = false; - participantsString.append(e.getKey()) - .append("(") - .append(e.getValue()) - .append(")"); - String clientId = e.getKey(); - String url = e.getValue(); - } - participantsString.append("]"); - - return String.format( - "State( \n" + - " stateIteration = %d,\n" + - " networkName = %s,\n" + - " masterId = %s,\n" + - " data = %s,\n" + - " participants = %s\n" + - ")", stateIteration, networkName, masterId, data, - participantsString); - } -} diff --git a/jsonrpc/src/main/java/com/orbekk/same/UrlReceiver.java b/jsonrpc/src/main/java/com/orbekk/same/UrlReceiver.java deleted file mode 100644 index 31a0276..0000000 --- a/jsonrpc/src/main/java/com/orbekk/same/UrlReceiver.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.orbekk.same; - -/** - * An interface to get notified of the URL to this computer. - * - * This interface is used to reliably obtain the URL of this host. - */ -public interface UrlReceiver { - void setUrl(String url); -} diff --git a/jsonrpc/src/main/resources/log4j.properties b/jsonrpc/src/main/resources/log4j.properties deleted file mode 100644 index 70f8a5f..0000000 --- a/jsonrpc/src/main/resources/log4j.properties +++ /dev/null @@ -1,7 +0,0 @@ -log4j.rootLogger=INFO, A1 -log4j.com.orbekk=DEBUG, A1 - -log4j.appender.A1=org.apache.log4j.ConsoleAppender -log4j.appender.A1.layout=org.apache.log4j.PatternLayout -log4j.appender.A1.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] - %m%n - diff --git a/jsonrpc/src/test/java/com/orbekk/same/SameStateTest.java b/jsonrpc/src/test/java/com/orbekk/same/SameStateTest.java deleted file mode 100644 index b16f5f9..0000000 --- a/jsonrpc/src/test/java/com/orbekk/same/SameStateTest.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.orbekk.same; - -import static org.junit.Assert.*; - -import java.util.Map; -import java.util.HashMap; -import org.junit.Test; -import org.junit.Before; - -public class SameStateTest { - private MockConnectionManager connections; - private SameState state1, state2, state3; - private SameService service1, service2, service3; - - public static class MockConnectionManager implements ConnectionManager { - public Map connections = - new HashMap(); - - @Override - public SameService getConnection(String url) { - return connections.get(url); - } - } - - public SameStateTest() { - } - - @Before public void setUp() { - connections = new MockConnectionManager(); - - state1 = new SameState("Network1", "Client1", connections); - state1.setUrl("test://client1"); - service1 = new SameServiceImpl(state1); - state2 = new SameState("Network2", "Client2", connections); - state2.setUrl("test://client2"); - service2 = new SameServiceImpl(state2); - state3 = new SameState("Network3", "Client3", connections); - state3.setUrl("test://client3"); - service3 = new SameServiceImpl(state3); - - connections.connections.put(state1.getUrl(), service1); - connections.connections.put(state2.getUrl(), service2); - connections.connections.put(state3.getUrl(), service3); - } - - public void joinNetwork() { - connections.getConnection(state1.getUrl()). - participateNetwork("Network1", state2.getClientId(), - state2.getUrl()); - connections.getConnection(state1.getUrl()). - participateNetwork("Network1", state3.getClientId(), - state3.getUrl()); - state1.internalRun(); - state2.internalRun(); - state3.internalRun(); - - assertTrue(state1.getParticipants().size() == 3); - assertTrue(state2.getParticipants().size() == 3); - assertTrue(state3.getParticipants().size() == 3); - } - - @Test public void testJoinNetwork() { - connections.getConnection(state1.getUrl()). - participateNetwork("Network1", state2.getClientId(), - state2.getUrl()); - assertTrue(state1.getParticipants().size() == 1); - assertTrue(state2.getParticipants().size() == 1); - - state1.internalRun(); - state2.internalRun(); - - assertTrue(state1.getParticipants().size() == 2); - assertTrue(state2.getParticipants().size() == 2); - assertEquals(state1.getNetworkName(), state2.getNetworkName()); - - connections.getConnection(state2.getUrl()). - participateNetwork("Network1", state3.getClientId(), - state3.getUrl()); - state2.internalRun(); - state1.internalRun(); - state3.internalRun(); - state2.internalRun(); - - assertTrue(state1.getParticipants().size() == 3); - assertTrue(state2.getParticipants().size() == 3); - assertTrue(state3.getParticipants().size() == 3); - assertEquals(state1.getNetworkName(), state2.getNetworkName()); - assertEquals(state2.getNetworkName(), state3.getNetworkName()); - - assertEquals("Client1", state1.getMasterId()); - assertEquals("Client1", state2.getMasterId()); - assertEquals("Client1", state3.getMasterId()); - } - - @Test public void setState() { - joinNetwork(); - state1.librarySetNewState("New state1"); - state1.internalRun(); - state2.internalRun(); - state3.internalRun(); - assertEquals("New state1", state1.getCurrentState()); - assertEquals("New state1", state2.getCurrentState()); - assertEquals("New state1", state2.getCurrentState()); - } -} diff --git a/master/AndroidManifest.xml b/master/AndroidManifest.xml deleted file mode 100644 index 6a596ed..0000000 --- a/master/AndroidManifest.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/master/pom.xml b/master/pom.xml deleted file mode 100644 index ad0039a..0000000 --- a/master/pom.xml +++ /dev/null @@ -1,139 +0,0 @@ - - - 4.0.0 - com.orbekk - master - 1.0-SNAPSHOT - apk - master - - - - com.google.android - android - 2.1.2 - provided - - - - - commons-logging - commons-logging - 1.1.1 - provided - - - - javax.servlet - servlet-api - 2.5 - provided - - - - javax.portlet - portlet-api - 2.0 - provided - - - - org.slf4j - slf4j-android - 1.6.1-RC1 - - - - org.slf4j - slf4j-api - 1.6.4 - provided - - - - org.slf4j - slf4j-log4j12 - 1.6.4 - provided - - - - com.orbekk - jsonrpc - 0.0-SNAPSHOT - - - - - - - com.jayway.maven.plugins.android.generation2 - android-maven-plugin - 3.0.0 - - ${project.basedir}/AndroidManifest.xml - ${project.basedir}/assets - ${project.basedir}/res - ${project.basedir}/src/main/native - - 8 - - true - true - true - - true - - - - maven-compiler-plugin - 2.3.2 - - 1.6 - 1.6 - - - - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - - com.jayway.maven.plugins.android.generation2 - - - android-maven-plugin - - - [3.0.0,) - - - proguard - - generate-sources - - - - - - - - - - - - - - - diff --git a/master/project.properties b/master/project.properties deleted file mode 100644 index ea89160..0000000 --- a/master/project.properties +++ /dev/null @@ -1,11 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system use, -# "ant.properties", and override values to adapt the script to your -# project structure. - -# Project target. -target=android-8 diff --git a/master/src/main/java/com/orbekk/Broadcast.java b/master/src/main/java/com/orbekk/Broadcast.java deleted file mode 100644 index 84b2d6e..0000000 --- a/master/src/main/java/com/orbekk/Broadcast.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.orbekk; - -import java.io.IOException; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.InetAddress; -import java.net.SocketException; -import java.net.UnknownHostException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import android.content.Context; -import android.net.DhcpInfo; -import android.net.wifi.WifiManager; - -public class Broadcast { - private Context context; - private Logger logger = LoggerFactory.getLogger(getClass()); - - public Broadcast(Context context) { - this.context = context; - } - - public InetAddress getBroadcastAddress() { - WifiManager wifi = (WifiManager)context.getSystemService(Context.WIFI_SERVICE); - DhcpInfo dhcp = wifi.getDhcpInfo(); - - int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask; - byte[] quads = new byte[4]; - for (int k = 0; k < 4; k++) - quads[k] = (byte) ((broadcast >> k * 8) & 0xFF); - try { - return InetAddress.getByAddress(quads); - } catch (UnknownHostException e) { - logger.warn("Failed to find broadcast address."); - return null; - } - } - - public boolean sendBroadcast(byte[] data, int port) { - try { - DatagramSocket socket = new DatagramSocket(port); - socket.setBroadcast(true); - DatagramPacket packet = new DatagramPacket(data, data.length, getBroadcastAddress(), port); - socket.send(packet); - return true; - } catch (SocketException e) { - logger.warn("Failed to send broadcast.", e.fillInStackTrace()); - return false; - } catch (IOException e) { - logger.warn("Error when sending broadcast.", e.fillInStackTrace()); - return false; - } - } -} \ No newline at end of file diff --git a/master/src/main/java/com/orbekk/HelloAndroidActivity.java b/master/src/main/java/com/orbekk/HelloAndroidActivity.java deleted file mode 100644 index 4160bf4..0000000 --- a/master/src/main/java/com/orbekk/HelloAndroidActivity.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.orbekk; - -import com.orbekk.net.Broadcaster; - -import android.app.Activity; -import android.os.Bundle; -import android.util.Log; - -public class HelloAndroidActivity extends Activity { - - private static String TAG = "master"; - private PingServer pingServer; - - /** - * Called when the activity is first created. - * @param savedInstanceState If the activity is being re-initialized after - * previously being shut down then this Bundle contains the data it most - * recently supplied in onSaveInstanceState(Bundle). Note: Otherwise it is null. - */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - System.setProperty("java.net.preferIPv4Stack", "true"); - System.setProperty("java.net.preferIPv6Addresses", "false"); - - Log.i(TAG, "onCreate"); - setContentView(R.layout.main); - - pingServer = PingServer.createPingServer(10080); - try { - pingServer.start(); - } catch (Exception e) { - throw new RuntimeException(e.getMessage()); - } - -// Broadcast broadcast = new Broadcast(this); -// broadcast.sendBroadcast("Broadcast test".getBytes(), 10010); - Broadcaster broadcaster = new Broadcaster(); - Log.i(TAG, "Broadcast success: " + broadcaster.sendBroadcast(10010, "Broadcast test from Android".getBytes())); - } - - @Override - protected void onDestroy() { - pingServer.stop(); - super.onDestroy(); - } -} - diff --git a/master/src/main/java/com/orbekk/PingServer.java b/master/src/main/java/com/orbekk/PingServer.java deleted file mode 100644 index 5df2346..0000000 --- a/master/src/main/java/com/orbekk/PingServer.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.orbekk; - -import org.eclipse.jetty.server.Server; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.googlecode.jsonrpc4j.JsonRpcServer; -import com.orbekk.rpc.PingService; -import com.orbekk.rpc.PingServiceImpl; -import com.orbekk.rpc.RpcHandler; - -public class PingServer { - private Server server; - private Logger logger = LoggerFactory.getLogger(getClass()); - - public PingServer(Server server) { - this.server = server; - } - - public static PingServer createPingServer(int port) { - PingService service = new PingServiceImpl(); - JsonRpcServer jsonServer = new JsonRpcServer(service, PingService.class); - - Server server = new Server(port); - RpcHandler rpcHandler = new RpcHandler(jsonServer); - server.setHandler(rpcHandler); - - return new PingServer(server); - } - - public void start() throws Exception { - logger.info("Starting server."); - server.start(); - } - - public void join() { - try { - server.join(); - } catch (InterruptedException e) { - logger.info("Received InterruptException while waiting for server.", e.fillInStackTrace()); - } - } - - public void stop() { - try { - server.stop(); - } catch (Exception e) { - logger.warn("Exception when stopping server.", e.fillInStackTrace()); - } - } -} diff --git a/same-android/AndroidManifest.xml b/same-android/AndroidManifest.xml new file mode 100644 index 0000000..6a596ed --- /dev/null +++ b/same-android/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + diff --git a/same-android/pom.xml b/same-android/pom.xml new file mode 100644 index 0000000..0af6627 --- /dev/null +++ b/same-android/pom.xml @@ -0,0 +1,139 @@ + + + 4.0.0 + com.orbekk + same-android + 1.0-SNAPSHOT + apk + same-android + + + + com.google.android + android + 2.1.2 + provided + + + + + commons-logging + commons-logging + 1.1.1 + provided + + + + javax.servlet + servlet-api + 2.5 + provided + + + + javax.portlet + portlet-api + 2.0 + provided + + + + org.slf4j + slf4j-android + 1.6.1-RC1 + + + + org.slf4j + slf4j-api + 1.6.4 + provided + + + + org.slf4j + slf4j-log4j12 + 1.6.4 + provided + + + + com.orbekk + jsonrpc + 0.0-SNAPSHOT + + + + + + + com.jayway.maven.plugins.android.generation2 + android-maven-plugin + 3.0.0 + + ${project.basedir}/AndroidManifest.xml + ${project.basedir}/assets + ${project.basedir}/res + ${project.basedir}/src/main/native + + 8 + + true + true + true + + true + + + + maven-compiler-plugin + 2.3.2 + + 1.6 + 1.6 + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + + com.jayway.maven.plugins.android.generation2 + + + android-maven-plugin + + + [3.0.0,) + + + proguard + + generate-sources + + + + + + + + + + + + + + + diff --git a/same-android/project.properties b/same-android/project.properties new file mode 100644 index 0000000..ea89160 --- /dev/null +++ b/same-android/project.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "ant.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-8 diff --git a/same-android/src/main/java/com/orbekk/Broadcast.java b/same-android/src/main/java/com/orbekk/Broadcast.java new file mode 100644 index 0000000..84b2d6e --- /dev/null +++ b/same-android/src/main/java/com/orbekk/Broadcast.java @@ -0,0 +1,56 @@ +package com.orbekk; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.SocketException; +import java.net.UnknownHostException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import android.content.Context; +import android.net.DhcpInfo; +import android.net.wifi.WifiManager; + +public class Broadcast { + private Context context; + private Logger logger = LoggerFactory.getLogger(getClass()); + + public Broadcast(Context context) { + this.context = context; + } + + public InetAddress getBroadcastAddress() { + WifiManager wifi = (WifiManager)context.getSystemService(Context.WIFI_SERVICE); + DhcpInfo dhcp = wifi.getDhcpInfo(); + + int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask; + byte[] quads = new byte[4]; + for (int k = 0; k < 4; k++) + quads[k] = (byte) ((broadcast >> k * 8) & 0xFF); + try { + return InetAddress.getByAddress(quads); + } catch (UnknownHostException e) { + logger.warn("Failed to find broadcast address."); + return null; + } + } + + public boolean sendBroadcast(byte[] data, int port) { + try { + DatagramSocket socket = new DatagramSocket(port); + socket.setBroadcast(true); + DatagramPacket packet = new DatagramPacket(data, data.length, getBroadcastAddress(), port); + socket.send(packet); + return true; + } catch (SocketException e) { + logger.warn("Failed to send broadcast.", e.fillInStackTrace()); + return false; + } catch (IOException e) { + logger.warn("Error when sending broadcast.", e.fillInStackTrace()); + return false; + } + } +} \ No newline at end of file diff --git a/same-android/src/main/java/com/orbekk/HelloAndroidActivity.java b/same-android/src/main/java/com/orbekk/HelloAndroidActivity.java new file mode 100644 index 0000000..4160bf4 --- /dev/null +++ b/same-android/src/main/java/com/orbekk/HelloAndroidActivity.java @@ -0,0 +1,49 @@ +package com.orbekk; + +import com.orbekk.net.Broadcaster; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; + +public class HelloAndroidActivity extends Activity { + + private static String TAG = "master"; + private PingServer pingServer; + + /** + * Called when the activity is first created. + * @param savedInstanceState If the activity is being re-initialized after + * previously being shut down then this Bundle contains the data it most + * recently supplied in onSaveInstanceState(Bundle). Note: Otherwise it is null. + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + System.setProperty("java.net.preferIPv4Stack", "true"); + System.setProperty("java.net.preferIPv6Addresses", "false"); + + Log.i(TAG, "onCreate"); + setContentView(R.layout.main); + + pingServer = PingServer.createPingServer(10080); + try { + pingServer.start(); + } catch (Exception e) { + throw new RuntimeException(e.getMessage()); + } + +// Broadcast broadcast = new Broadcast(this); +// broadcast.sendBroadcast("Broadcast test".getBytes(), 10010); + Broadcaster broadcaster = new Broadcaster(); + Log.i(TAG, "Broadcast success: " + broadcaster.sendBroadcast(10010, "Broadcast test from Android".getBytes())); + } + + @Override + protected void onDestroy() { + pingServer.stop(); + super.onDestroy(); + } +} + diff --git a/same-android/src/main/java/com/orbekk/PingServer.java b/same-android/src/main/java/com/orbekk/PingServer.java new file mode 100644 index 0000000..5df2346 --- /dev/null +++ b/same-android/src/main/java/com/orbekk/PingServer.java @@ -0,0 +1,51 @@ +package com.orbekk; + +import org.eclipse.jetty.server.Server; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.googlecode.jsonrpc4j.JsonRpcServer; +import com.orbekk.rpc.PingService; +import com.orbekk.rpc.PingServiceImpl; +import com.orbekk.rpc.RpcHandler; + +public class PingServer { + private Server server; + private Logger logger = LoggerFactory.getLogger(getClass()); + + public PingServer(Server server) { + this.server = server; + } + + public static PingServer createPingServer(int port) { + PingService service = new PingServiceImpl(); + JsonRpcServer jsonServer = new JsonRpcServer(service, PingService.class); + + Server server = new Server(port); + RpcHandler rpcHandler = new RpcHandler(jsonServer); + server.setHandler(rpcHandler); + + return new PingServer(server); + } + + public void start() throws Exception { + logger.info("Starting server."); + server.start(); + } + + public void join() { + try { + server.join(); + } catch (InterruptedException e) { + logger.info("Received InterruptException while waiting for server.", e.fillInStackTrace()); + } + } + + public void stop() { + try { + server.stop(); + } catch (Exception e) { + logger.warn("Exception when stopping server.", e.fillInStackTrace()); + } + } +} diff --git a/same/pom.xml b/same/pom.xml new file mode 100644 index 0000000..6d3d5f9 --- /dev/null +++ b/same/pom.xml @@ -0,0 +1,95 @@ + + 4.0.0 + + com.orbekk + same + 0.0-SNAPSHOT + jar + + same + http://github.com/orbekk/master + + + UTF-8 + + + + + jsonrpc3j-webdav-maven-repo + jsonrpc4j maven repository + http://jsonrpc4j.googlecode.com/svn/maven/repo/ + default + + + + + + com.googlecode + jsonrpc4j + 0.18 + + + + org.codehaus.jackson + jackson-mapper-asl + 1.7.5 + + + + junit + junit + 4.10 + test + + + + org.slf4j + slf4j-api + 1.6.4 + + + + org.slf4j + slf4j-log4j12 + 1.6.4 + + + + javax.servlet + servlet-api + 2.5 + + + + javax.portlet + portlet-api + 2.0 + + + + org.eclipse.jetty + jetty-servlet + 8.0.0.M3 + + + + org.eclipse.jetty + jetty-server + 8.0.0.M3 + + + + + + + maven-compiler-plugin + 2.3.2 + + 1.6 + 1.6 + + + + + diff --git a/same/src/main/java/com/orbekk/net/BroadcastListener.java b/same/src/main/java/com/orbekk/net/BroadcastListener.java new file mode 100644 index 0000000..c0b66e0 --- /dev/null +++ b/same/src/main/java/com/orbekk/net/BroadcastListener.java @@ -0,0 +1,53 @@ +package com.orbekk.net; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.SocketException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BroadcastListener { + private int port; + private Logger logger = LoggerFactory.getLogger(getClass()); + + public BroadcastListener(int port) { + this.port = port; + } + + public DatagramPacket listen() { + logger.debug("Waiting for broadcast on port " + port); + DatagramSocket socket; + try { + socket = new DatagramSocket(port); + } catch (SocketException e) { + logger.warn("Failed to create socket.", e.fillInStackTrace()); + return null; + } + try { + socket.setBroadcast(true); + } catch (SocketException e) { + logger.warn("Exception: {}", e); + } + byte[] buffer = new byte[2048]; + DatagramPacket packet = new DatagramPacket(buffer, buffer.length); + try { + socket.receive(packet); + } catch (IOException e) { + logger.warn("Exception when listening for broadcast: {}", e); + return null; + } + + String address = packet.getAddress().getHostAddress(); + logger.debug("Received broadcast from " + address + + ": " + new String(packet.getData())); + return packet; + } + + public static void main(String[] args) { + int port = Integer.parseInt(args[0]); + BroadcastListener listener = new BroadcastListener(port); + System.out.println("Received broadcast: " + listener.listen()); + } +} diff --git a/same/src/main/java/com/orbekk/net/Broadcaster.java b/same/src/main/java/com/orbekk/net/Broadcaster.java new file mode 100644 index 0000000..95e279c --- /dev/null +++ b/same/src/main/java/com/orbekk/net/Broadcaster.java @@ -0,0 +1,77 @@ +package com.orbekk.net; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InterfaceAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Enumeration; +import java.util.LinkedList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class Broadcaster { + private Logger logger = LoggerFactory.getLogger(getClass()); + + public List getBroadcastAddresses() { + List broadcastAddresses = new LinkedList(); + + Enumeration interfaces; + try { + interfaces = NetworkInterface.getNetworkInterfaces(); + } catch (SocketException e) { + logger.warn("Network problem?", e.fillInStackTrace()); + return broadcastAddresses; + } + while (interfaces.hasMoreElements()) { + NetworkInterface iface = interfaces.nextElement(); + try { + if (iface.isLoopback()) { + logger.debug("Ignoring looback device " + iface.getName()); + continue; + } + for (InterfaceAddress address : iface.getInterfaceAddresses()) { + InetAddress broadcast = address.getBroadcast(); + if (broadcast != null) { + broadcastAddresses.add(broadcast); + } + } + } catch (SocketException e) { + logger.info("Ignoring interface " + iface.getName(), e.fillInStackTrace()); + } + } + return broadcastAddresses; + } + + public boolean sendBroadcast(int port, byte[] data) { + boolean successful = false; + for (InetAddress broadcastAddress : getBroadcastAddresses()) { + try { + DatagramSocket socket = new DatagramSocket(); + socket.setBroadcast(true); + DatagramPacket packet = new DatagramPacket(data, data.length, broadcastAddress, port); + socket.send(packet); + successful = true; + } catch (SocketException e) { + logger.warn("Failed to send broadcast to " + broadcastAddress + + ". ", e.fillInStackTrace()); + } catch (IOException e) { + logger.warn("Error when sending broadcast to " + + broadcastAddress + ".", e.fillInStackTrace()); + } + } + return successful; + } + + public static void main(String[] args) { + int port = Integer.parseInt(args[0]); + Broadcaster broadcaster = new Broadcaster(); + String message = "Broadcast from Java broadcaster."; + broadcaster.sendBroadcast(port, message.getBytes()); + } +} diff --git a/same/src/main/java/com/orbekk/net/HttpUtil.java b/same/src/main/java/com/orbekk/net/HttpUtil.java new file mode 100644 index 0000000..b4bb887 --- /dev/null +++ b/same/src/main/java/com/orbekk/net/HttpUtil.java @@ -0,0 +1,26 @@ +package com.orbekk.net; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class HttpUtil { + private static final Logger logger = + LoggerFactory.getLogger(HttpUtil.class); + + public static void sendHttpRequest(String url) { + try { + URL pingUrl = new URL(url); + pingUrl.openStream(); + // URLConnection connection = pingUrl.openConnection(); + // connection.connect(); + } catch (MalformedURLException e) { + logger.warn("Unable to send ping to {}: {}.", url, e); + } catch (IOException e) { + logger.warn("Error when sending ping: {}", e); + } + } +} diff --git a/same/src/main/java/com/orbekk/rpc/App.java b/same/src/main/java/com/orbekk/rpc/App.java new file mode 100644 index 0000000..0e9dce3 --- /dev/null +++ b/same/src/main/java/com/orbekk/rpc/App.java @@ -0,0 +1,47 @@ +package com.orbekk.rpc; + +import com.googlecode.jsonrpc4j.JsonRpcServer; +import com.orbekk.same.ConnectionManagerImpl; +import com.orbekk.same.SameState; +import com.orbekk.same.SameService; +import com.orbekk.same.SameServiceImpl; +import org.eclipse.jetty.server.Server; + +public class App { + public static void main(String[] args) { + if (args.length < 3) { + System.err.println("Arguments: port networkName clientId"); + System.exit(1); + } + int port = Integer.parseInt(args[0]); + String networkName = args[1]; + String clientId = args[2]; + + ConnectionManagerImpl connections = new ConnectionManagerImpl(); + + SameState sameState = new SameState(networkName, clientId, + connections); + sameState.start(); + + SameServiceImpl service = new SameServiceImpl(sameState); + JsonRpcServer jsonServer = new JsonRpcServer(service, + SameService.class); + + Server server = new Server(port); + RpcHandler rpcHandler = new RpcHandler(jsonServer, sameState); + server.setHandler(rpcHandler); + + try { + server.start(); + } catch (Exception e) { + System.out.println("Could not start jetty server."); + e.printStackTrace(); + } + + try { + server.join(); + } catch (InterruptedException e) { + System.out.println("Interrupt"); + } + } +} diff --git a/same/src/main/java/com/orbekk/rpc/Client.java b/same/src/main/java/com/orbekk/rpc/Client.java new file mode 100644 index 0000000..225347a --- /dev/null +++ b/same/src/main/java/com/orbekk/rpc/Client.java @@ -0,0 +1,66 @@ +package com.orbekk.rpc; + +import com.googlecode.jsonrpc4j.JsonRpcServer; +import com.orbekk.same.ConnectionManagerImpl; +import com.orbekk.same.SameState; +import com.orbekk.same.SameService; +import com.orbekk.same.SameServiceImpl; +import com.orbekk.net.HttpUtil; +import org.eclipse.jetty.server.Server; + +public class Client { + + public static void main(String[] args) { + if (args.length < 4) { + System.err.println("Arguments: port clientId thisNetworkName " + + "remoteNetworkAddr"); + System.exit(1); + } + int port = Integer.parseInt(args[0]); + String clientId = args[1]; + String networkName = args[2]; + String remoteAddr = args[3]; + + ConnectionManagerImpl connections = new ConnectionManagerImpl(); + + SameState sameState = new SameState(networkName, clientId, + connections); + sameState.start(); + + SameServiceImpl service = new SameServiceImpl(sameState); + JsonRpcServer jsonServer = new JsonRpcServer(service, + SameService.class); + + Server server = new Server(port); + RpcHandler rpcHandler = new RpcHandler(jsonServer, sameState); + server.setHandler(rpcHandler); + + try { + server.start(); + } catch (Exception e) { + System.out.println("Could not start jetty server."); + e.printStackTrace(); + } + + while (sameState.getUrl() == null) { + HttpUtil.sendHttpRequest(remoteAddr + "ping?port=" + port); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + // Ignore interrupt in wait loop. + } + } + + SameService remoteService = connections.getConnection(remoteAddr); + remoteService.notifyNetwork("NoNetwork"); + remoteService.participateNetwork("FirstNetwork", + sameState.getClientId(), sameState.getUrl()); + + try { + server.join(); + } catch (InterruptedException e) { + System.out.println("Interrupt"); + } + + } +} diff --git a/same/src/main/java/com/orbekk/rpc/RpcHandler.java b/same/src/main/java/com/orbekk/rpc/RpcHandler.java new file mode 100644 index 0000000..0bafad4 --- /dev/null +++ b/same/src/main/java/com/orbekk/rpc/RpcHandler.java @@ -0,0 +1,52 @@ +package com.orbekk.rpc; + +import com.googlecode.jsonrpc4j.JsonRpcServer; +import com.orbekk.net.HttpUtil; +import com.orbekk.same.UrlReceiver; +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RpcHandler extends AbstractHandler { + private Logger logger = LoggerFactory.getLogger(getClass()); + private JsonRpcServer rpcServer; + private UrlReceiver urlReceiver; + + public RpcHandler(JsonRpcServer rpcServer, + UrlReceiver urlReceiver) { + this.rpcServer = rpcServer; + this.urlReceiver = urlReceiver; + } + + @Override + public synchronized void handle(String target, Request baseRequest, + HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException { + logger.info("Handling request to target: " + target); + + if (urlReceiver != null) { + String sameServiceUrl = "http://" + request.getLocalAddr() + + ":" + request.getLocalPort() + "/SameService.json"; + urlReceiver.setUrl(sameServiceUrl); + urlReceiver = null; + } + + if (target.equals("/ping")) { + int remotePort = Integer.parseInt(request.getParameter("port")); + String pongUrl = "http://" + request.getRemoteAddr() + ":" + + remotePort + "/pong"; + logger.info("Got ping. Sending pong to {}", pongUrl); + HttpUtil.sendHttpRequest(pongUrl); + } else if (target.equals("/pong")) { + logger.info("Received pong from {}", request.getRemoteAddr()); + } else { + rpcServer.handle(request, response); + } + baseRequest.setHandled(true); + } +} diff --git a/same/src/main/java/com/orbekk/same/ConnectionManager.java b/same/src/main/java/com/orbekk/same/ConnectionManager.java new file mode 100644 index 0000000..985f6f0 --- /dev/null +++ b/same/src/main/java/com/orbekk/same/ConnectionManager.java @@ -0,0 +1,10 @@ +package com.orbekk.same; + +/** + * An interface that returns a connection for a participant. + * + * When testing, this interface can be mocked to use local participants only. + */ +public interface ConnectionManager { + SameService getConnection(String url); +} diff --git a/same/src/main/java/com/orbekk/same/ConnectionManagerImpl.java b/same/src/main/java/com/orbekk/same/ConnectionManagerImpl.java new file mode 100644 index 0000000..841d5fa --- /dev/null +++ b/same/src/main/java/com/orbekk/same/ConnectionManagerImpl.java @@ -0,0 +1,31 @@ +package com.orbekk.same; + +import com.googlecode.jsonrpc4j.JsonRpcHttpClient; +import com.googlecode.jsonrpc4j.ProxyUtil; +import java.net.MalformedURLException; +import java.net.URL; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ConnectionManagerImpl implements ConnectionManager { + + private Logger logger = LoggerFactory.getLogger(getClass()); + + public ConnectionManagerImpl() { + } + + @Override + public SameService getConnection(String url) { + SameService service = null; + try { + JsonRpcHttpClient client = new JsonRpcHttpClient(new URL(url)); + service = ProxyUtil.createProxy( + this.getClass().getClassLoader(), + SameService.class, + client); + } catch (MalformedURLException e) { + logger.warn("Unable to create client for {}, {}", url, e); + } + return service; + } +} diff --git a/same/src/main/java/com/orbekk/same/SameService.java b/same/src/main/java/com/orbekk/same/SameService.java new file mode 100644 index 0000000..8f239da --- /dev/null +++ b/same/src/main/java/com/orbekk/same/SameService.java @@ -0,0 +1,40 @@ +package com.orbekk.same; + +import java.util.Map; + +public interface SameService { + /** + * A notification that 'networkName' exists. + * + * This is called by any participant of a network after a broadcast + * has been performed. + */ + void notifyNetwork(String networkName); + + /** + * A request from the callee to participate in 'networkName'. + */ + void participateNetwork(String networkName, String clientId, String url); + + /** + * Notification of participation in network. + */ + void notifyParticipation(String networkName, String masterId); + + /** + * New state. + * + * When sent to a non-master from the master, use 'newState' as the + * current state. + * + * When sent to a master, broadcast the new state to all clients. + */ + void setState(String newState); + + /** + * Notify all nodes of network participants. + * + * Only sent from master to non-master. + */ + void setParticipants(Map participants); +} diff --git a/same/src/main/java/com/orbekk/same/SameServiceImpl.java b/same/src/main/java/com/orbekk/same/SameServiceImpl.java new file mode 100644 index 0000000..27579b5 --- /dev/null +++ b/same/src/main/java/com/orbekk/same/SameServiceImpl.java @@ -0,0 +1,60 @@ +package com.orbekk.same; + +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SameServiceImpl implements SameService { + private Logger logger = LoggerFactory.getLogger(getClass()); + private SameState sameState; + + public SameServiceImpl(SameState sameState) { + this.sameState = sameState; + } + + @Override + public void notifyNetwork(String networkName) { + logger.info("Notification from network " + networkName); + } + + @Override + public void participateNetwork(String networkName, String clientId, + String url) { + if (!networkName.equals(sameState.getNetworkName())) { + logger.warn("Client tried to join {}, but network name is {}.", + networkName, sameState.getNetworkName()); + return; + } + if (clientId.equals("") || url.equals("")) { + logger.warn("Missing client info: ClientId({}), URL({}).", + clientId, url); + return; + } + sameState.addParticipant(clientId, url); + } + + @Override + public void notifyParticipation(String networkName, String masterId) { + logger.info("Joining network {}. Master is {}", networkName, masterId); + // int i = 1; + // for (Map.Entry e : participants.entrySet()) { + // String clientId = e.getKey(); + // String url = e.getValue(); + // logger.info(" {} participant {}: {}, {}", + // new Object[]{networkName, i, clientId, url}); + // i++; + // } + sameState.joinNetwork(networkName, masterId); + } + + @Override + public void setParticipants(Map participants) { + sameState.setParticipants(participants); + } + + @Override + public void setState(String newState) { + sameState.setState(newState); + } +} diff --git a/same/src/main/java/com/orbekk/same/SameState.java b/same/src/main/java/com/orbekk/same/SameState.java new file mode 100644 index 0000000..c9cd216 --- /dev/null +++ b/same/src/main/java/com/orbekk/same/SameState.java @@ -0,0 +1,233 @@ +package com.orbekk.same; + +import java.util.List; +import java.util.LinkedList; +import java.util.Map; +import java.util.HashMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The implementation of a 'Same' state. + * + * This class manages the current state of the Same protocol. + */ +public class SameState extends Thread implements UrlReceiver { + private Logger logger = LoggerFactory.getLogger(getClass()); + private ConnectionManager connections; + + // TODO: Change the name of State. + private com.orbekk.same.State state = + new com.orbekk.same.State(); + + /** + * The client id of this participant. + */ + private String clientId; + + /** + * Stopping condition for this thread. + */ + private boolean stopped = false; + + private String _setState = null; + private Map _setParticipants = null; + + private Map pendingParticipants = + new HashMap(); + + public SameState(String networkName, String clientId, + ConnectionManager connections) { + state.setNetworkName(networkName); + this.clientId = clientId; + this.connections = connections; + state.setMasterId(clientId); + state.getParticipants().put(clientId, null); + } + + public String getMasterId() { + return state.getMasterId(); + } + + public synchronized Map getParticipants() { + return state.getParticipants(); + } + + /** + * Reset this SameService to an initial state. + * + * TODO: Implement fully. + */ + private synchronized void resetState() { + state = new com.orbekk.same.State(); + pendingParticipants.clear(); + } + + public synchronized void joinNetwork(String networkName, String masterId) { + resetState(); + state.setNetworkName(networkName); + state.setMasterId(masterId); + logger.info("Joined network {}.", networkName); + } + + public String getClientId() { + return clientId; + } + + public String getNetworkName() { + return state.getNetworkName(); + } + + public String getCurrentState() { + return state.getData(); + } + + /** + * TODO: Move to a separate library. + */ + public void librarySetNewState(String newState) { + connections.getConnection( + state.getParticipants().get(state.getMasterId())) + .setState(newState); + } + + public String getUrl() { + return state.getParticipants().get(clientId); + } + + @Override + public void setUrl(String url) { + logger.info("My URL is {}", url); + state.getParticipants().put(clientId, url); + } + + public synchronized void addParticipant(String clientId, String url) { + logger.info("PendingParticipant.add: {} ({})", clientId, url); + pendingParticipants.put(clientId, url); + notifyAll(); + } + + public synchronized void setParticipants(Map participants) { + logger.info("Pending operation: _setParticipants"); + _setParticipants = participants; + notifyAll(); + } + + public synchronized void setState(String newState) { + logger.info("Pending operation: _setState"); + _setState = newState; + notifyAll(); + } + + private synchronized void handleSetParticipants() { + if (_setParticipants != null) { + if (isMaster()) { + logger.error("{}: Master received setParticipants.", clientId); + } else { + logger.info("{}: New participants committed.", clientId); + state.getParticipants().clear(); + state.getParticipants().putAll(_setParticipants); + } + } + _setParticipants = null; + } + + public synchronized void handleSetState() { + if (_setState != null) { + if (isMaster()) { + broadcast(new ServiceOperation() { + @Override void run(SameService service) { + service.setState(_setState); + } + }); + } + state.setData(_setState); + _setState = null; + } + } + + private boolean isMaster() { + return state.getMasterId().equals(clientId); + } + + private synchronized void handleNewParticipants() { + if (!isMaster()) { + for (Map.Entry e : pendingParticipants.entrySet()) { + SameService master = connections.getConnection( + state.getParticipants().get(state.getMasterId())); + logger.info("Redirecting participant request to {}", + state.getMasterId()); + String clientId = e.getKey(); + String url = e.getValue(); + master.participateNetwork(state.getNetworkName(), clientId, + url); + } + } else { + state.getParticipants().putAll(pendingParticipants); + for (Map.Entry e : + pendingParticipants.entrySet()) { + String clientId = e.getKey(); + String url = e.getValue(); + logger.info("New participant: {} URL({})", clientId, url); + SameService remoteService = connections.getConnection(url); + remoteService.notifyParticipation(state.getNetworkName(), + state.getMasterId()); + broadcast(new ServiceOperation(){ + @Override void run(SameService service) { + service.setParticipants(state.getParticipants()); + } + }); + } + } + pendingParticipants.clear(); + } + + /** + * This method runs the pending commands to SameState. + * + * It should be called by the worker thread, but can be called directly + * for testing purposes to avoid threading in unit tests. + */ + synchronized void internalRun() { + handleNewParticipants(); + handleSetState(); + handleSetParticipants(); + } + + public synchronized void run() { + while (!stopped) { + internalRun(); + try { + wait(1000); + } catch (InterruptedException e) { + // Ignore interrupt in wait loop. + } + } + } + + public synchronized void stopSame() { + try { + stopped = true; + notifyAll(); + this.join(); + } catch (InterruptedException e) { + logger.warn("Got InterruptedException while waiting for SameState " + + "to finish. Ignoring."); + } + } + + public abstract static class ServiceOperation { + abstract void run(SameService service); + } + + public synchronized void broadcast(ServiceOperation operation) { + for (Map.Entry e : + state.getParticipants().entrySet()) { + String clientId = e.getKey(); + String url = e.getValue(); + if (!clientId.equals(this.clientId)) { + operation.run(connections.getConnection(url)); + } + } + } +} diff --git a/same/src/main/java/com/orbekk/same/State.java b/same/src/main/java/com/orbekk/same/State.java new file mode 100644 index 0000000..70ffa83 --- /dev/null +++ b/same/src/main/java/com/orbekk/same/State.java @@ -0,0 +1,78 @@ +package com.orbekk.same; + +import java.util.Map; +import java.util.HashMap; + +public class State { + private long stateIteration = 0; + private Map participants = new HashMap(); + private String networkName = ""; + private String masterId = ""; + private String data = ""; + + public long getStateIteration() { + return stateIteration; + } + + public void setStateIteration(long stateIteration) { + this.stateIteration = stateIteration; + } + + public Map getParticipants() { + return participants; + } + + public String getNetworkName() { + return networkName; + } + + public void setNetworkName(String networkName) { + this.networkName = networkName; + } + + public String getMasterId() { + return masterId; + } + + public void setMasterId(String masterId) { + this.masterId = masterId; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + @Override + public String toString() { + StringBuilder participantsString = new StringBuilder(); + participantsString.append("["); + boolean first = true; + for (Map.Entry e : participants.entrySet()) { + if (!first) { + participantsString.append(", "); + } + first = false; + participantsString.append(e.getKey()) + .append("(") + .append(e.getValue()) + .append(")"); + String clientId = e.getKey(); + String url = e.getValue(); + } + participantsString.append("]"); + + return String.format( + "State( \n" + + " stateIteration = %d,\n" + + " networkName = %s,\n" + + " masterId = %s,\n" + + " data = %s,\n" + + " participants = %s\n" + + ")", stateIteration, networkName, masterId, data, + participantsString); + } +} diff --git a/same/src/main/java/com/orbekk/same/UrlReceiver.java b/same/src/main/java/com/orbekk/same/UrlReceiver.java new file mode 100644 index 0000000..31a0276 --- /dev/null +++ b/same/src/main/java/com/orbekk/same/UrlReceiver.java @@ -0,0 +1,10 @@ +package com.orbekk.same; + +/** + * An interface to get notified of the URL to this computer. + * + * This interface is used to reliably obtain the URL of this host. + */ +public interface UrlReceiver { + void setUrl(String url); +} diff --git a/same/src/main/resources/log4j.properties b/same/src/main/resources/log4j.properties new file mode 100644 index 0000000..70f8a5f --- /dev/null +++ b/same/src/main/resources/log4j.properties @@ -0,0 +1,7 @@ +log4j.rootLogger=INFO, A1 +log4j.com.orbekk=DEBUG, A1 + +log4j.appender.A1=org.apache.log4j.ConsoleAppender +log4j.appender.A1.layout=org.apache.log4j.PatternLayout +log4j.appender.A1.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] - %m%n + diff --git a/same/src/test/java/com/orbekk/same/SameStateTest.java b/same/src/test/java/com/orbekk/same/SameStateTest.java new file mode 100644 index 0000000..b16f5f9 --- /dev/null +++ b/same/src/test/java/com/orbekk/same/SameStateTest.java @@ -0,0 +1,105 @@ +package com.orbekk.same; + +import static org.junit.Assert.*; + +import java.util.Map; +import java.util.HashMap; +import org.junit.Test; +import org.junit.Before; + +public class SameStateTest { + private MockConnectionManager connections; + private SameState state1, state2, state3; + private SameService service1, service2, service3; + + public static class MockConnectionManager implements ConnectionManager { + public Map connections = + new HashMap(); + + @Override + public SameService getConnection(String url) { + return connections.get(url); + } + } + + public SameStateTest() { + } + + @Before public void setUp() { + connections = new MockConnectionManager(); + + state1 = new SameState("Network1", "Client1", connections); + state1.setUrl("test://client1"); + service1 = new SameServiceImpl(state1); + state2 = new SameState("Network2", "Client2", connections); + state2.setUrl("test://client2"); + service2 = new SameServiceImpl(state2); + state3 = new SameState("Network3", "Client3", connections); + state3.setUrl("test://client3"); + service3 = new SameServiceImpl(state3); + + connections.connections.put(state1.getUrl(), service1); + connections.connections.put(state2.getUrl(), service2); + connections.connections.put(state3.getUrl(), service3); + } + + public void joinNetwork() { + connections.getConnection(state1.getUrl()). + participateNetwork("Network1", state2.getClientId(), + state2.getUrl()); + connections.getConnection(state1.getUrl()). + participateNetwork("Network1", state3.getClientId(), + state3.getUrl()); + state1.internalRun(); + state2.internalRun(); + state3.internalRun(); + + assertTrue(state1.getParticipants().size() == 3); + assertTrue(state2.getParticipants().size() == 3); + assertTrue(state3.getParticipants().size() == 3); + } + + @Test public void testJoinNetwork() { + connections.getConnection(state1.getUrl()). + participateNetwork("Network1", state2.getClientId(), + state2.getUrl()); + assertTrue(state1.getParticipants().size() == 1); + assertTrue(state2.getParticipants().size() == 1); + + state1.internalRun(); + state2.internalRun(); + + assertTrue(state1.getParticipants().size() == 2); + assertTrue(state2.getParticipants().size() == 2); + assertEquals(state1.getNetworkName(), state2.getNetworkName()); + + connections.getConnection(state2.getUrl()). + participateNetwork("Network1", state3.getClientId(), + state3.getUrl()); + state2.internalRun(); + state1.internalRun(); + state3.internalRun(); + state2.internalRun(); + + assertTrue(state1.getParticipants().size() == 3); + assertTrue(state2.getParticipants().size() == 3); + assertTrue(state3.getParticipants().size() == 3); + assertEquals(state1.getNetworkName(), state2.getNetworkName()); + assertEquals(state2.getNetworkName(), state3.getNetworkName()); + + assertEquals("Client1", state1.getMasterId()); + assertEquals("Client1", state2.getMasterId()); + assertEquals("Client1", state3.getMasterId()); + } + + @Test public void setState() { + joinNetwork(); + state1.librarySetNewState("New state1"); + state1.internalRun(); + state2.internalRun(); + state3.internalRun(); + assertEquals("New state1", state1.getCurrentState()); + assertEquals("New state1", state2.getCurrentState()); + assertEquals("New state1", state2.getCurrentState()); + } +} -- cgit v1.2.3