diff --git a/java/org/servalproject/servaldna/AbstractMdpProtocol.java b/java/org/servalproject/servaldna/AbstractMdpProtocol.java index dc1fc1d3..c9de7d51 100644 --- a/java/org/servalproject/servaldna/AbstractMdpProtocol.java +++ b/java/org/servalproject/servaldna/AbstractMdpProtocol.java @@ -12,8 +12,8 @@ public abstract class AbstractMdpProtocol extends ChannelSelector.Handler { protected final MdpSocket socket; protected final AsyncResult results; - public AbstractMdpProtocol(ChannelSelector selector, AsyncResult results) throws IOException { - socket = new MdpSocket(); + public AbstractMdpProtocol(ChannelSelector selector, int loopbackMdpPort, AsyncResult results) throws IOException { + this.socket = new MdpSocket(loopbackMdpPort); socket.bind(); this.selector = selector; this.results = results; diff --git a/java/org/servalproject/servaldna/MdpDnaLookup.java b/java/org/servalproject/servaldna/MdpDnaLookup.java index a82bf52b..65179bf5 100644 --- a/java/org/servalproject/servaldna/MdpDnaLookup.java +++ b/java/org/servalproject/servaldna/MdpDnaLookup.java @@ -7,8 +7,8 @@ import java.io.IOException; */ public class MdpDnaLookup extends AbstractMdpProtocol { - public MdpDnaLookup(ChannelSelector selector, AsyncResult results) throws IOException { - super(selector, results); + public MdpDnaLookup(ChannelSelector selector, int loopbackMdpPort, AsyncResult results) throws IOException { + super(selector, loopbackMdpPort, results); } public void sendRequest(SubscriberId destination, String did) throws IOException { diff --git a/java/org/servalproject/servaldna/MdpServiceLookup.java b/java/org/servalproject/servaldna/MdpServiceLookup.java index e4c3c6b7..e68794f5 100644 --- a/java/org/servalproject/servaldna/MdpServiceLookup.java +++ b/java/org/servalproject/servaldna/MdpServiceLookup.java @@ -21,8 +21,8 @@ public class MdpServiceLookup extends AbstractMdpProtocol results) throws IOException { - super(selector, results); + public MdpServiceLookup(ChannelSelector selector, int loopbackMdpPort, AsyncResult results) throws IOException { + super(selector, loopbackMdpPort, results); } public void sendRequest(SubscriberId destination, String pattern) throws IOException { diff --git a/java/org/servalproject/servaldna/MdpSocket.java b/java/org/servalproject/servaldna/MdpSocket.java index 07cac2b4..84261559 100644 --- a/java/org/servalproject/servaldna/MdpSocket.java +++ b/java/org/servalproject/servaldna/MdpSocket.java @@ -17,10 +17,11 @@ public class MdpSocket{ private int port; private static final InetAddress loopback; - public static int loopbackMdpPort =0; + private final int loopbackMdpPort; static { InetAddress local=null; try { + // can't trust Inet4Address.getLocalHost() as some implementations can fail to resolve the name "loopback" local = Inet4Address.getByAddress(new byte[]{127, 0, 0, 1}); } catch (UnknownHostException e) { e.printStackTrace(); @@ -29,12 +30,15 @@ public class MdpSocket{ } /* Create an unbound socket, may be used for other information requests before binding */ - public MdpSocket() throws IOException { + public MdpSocket(int loopbackMdpPort) throws IOException { + this.loopbackMdpPort = loopbackMdpPort; } - public MdpSocket(int port) throws IOException { + public MdpSocket(int loopbackMdpPort, int port) throws IOException { + this(loopbackMdpPort); bind(SubscriberId.ANY, port); } - public MdpSocket(SubscriberId sid, int port) throws IOException { + public MdpSocket(int loopbackMdpPort, SubscriberId sid, int port) throws IOException { + this(loopbackMdpPort); bind(sid, port); } diff --git a/java/org/servalproject/servaldna/ServalDClient.java b/java/org/servalproject/servaldna/ServalDClient.java index b6c22baa..325a5845 100644 --- a/java/org/servalproject/servaldna/ServalDClient.java +++ b/java/org/servalproject/servaldna/ServalDClient.java @@ -20,72 +20,33 @@ package org.servalproject.servaldna; +import org.servalproject.codec.Base64; +import org.servalproject.servaldna.meshms.MeshMSConversationList; +import org.servalproject.servaldna.meshms.MeshMSException; +import org.servalproject.servaldna.meshms.MeshMSMessageList; + import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; -import java.net.HttpURLConnection; -import org.servalproject.codec.Base64; -import org.servalproject.servaldna.SubscriberId; -import org.servalproject.servaldna.ServalDCommand; -import org.servalproject.servaldna.ServalDInterfaceException; -import org.servalproject.servaldna.meshms.MeshMSConversationList; -import org.servalproject.servaldna.meshms.MeshMSMessageList; -import org.servalproject.servaldna.meshms.MeshMSException; public class ServalDClient implements ServalDHttpConnectionFactory { + private final int httpPort; + private final String restfulUsername; + private final String restfulPassword; - private static final String restfulUsername = "ServalDClient"; - private static final String restfulPasswordDefault = "u6ng^ues%@@SabLEEEE8"; - private static String restfulPassword; - protected boolean connected; - int httpPort; - - public static ServalDClient newServalDClient() - { - return new ServalDClient(); - } - - protected ServalDClient() - { - restfulPassword = null; - connected = false; - httpPort = 0; - } - - private void connect() throws ServalDInterfaceException - { - ensureServerRunning(); - if (!connected) { - if (!fetchRestfulAuthorization()) - createRestfulAuthorization(); - connected = true; - } - } - - private void ensureServerRunning() throws ServalDInterfaceException - { - ServalDCommand.Status s = ServalDCommand.serverStatus(); - if (!s.status.equals("running")) - throw new ServalDInterfaceException("server is not running"); - if (s.httpPort < 1 || s.httpPort > 65535) - throw new ServalDInterfaceException("invalid HTTP port number: " + s.httpPort); - httpPort = s.httpPort; - } - - private boolean fetchRestfulAuthorization() throws ServalDInterfaceException - { - restfulPassword = ServalDCommand.getConfigItem("rhizome.api.restful.users." + restfulUsername + ".password"); - return restfulPassword != null; - } - - private void createRestfulAuthorization() throws ServalDInterfaceException - { - ServalDCommand.setConfigItem("rhizome.api.restful.users." + restfulUsername + ".password", restfulPasswordDefault); - ServalDCommand.configSync(); - if (!fetchRestfulAuthorization()) - throw new ServalDInterfaceException("restful password not set"); + public ServalDClient(int httpPort, String restfulUsername, String restfulPassword) throws ServalDInterfaceException { + if (httpPort < 1 || httpPort > 65535) + throw new ServalDInterfaceException("invalid HTTP port number: " + httpPort); + if (restfulUsername == null) + throw new ServalDInterfaceException("invalid HTTP username"); + if (restfulPassword == null) + throw new ServalDInterfaceException("invalid HTTP password"); + this.httpPort = httpPort; + this.restfulUsername = restfulUsername; + this.restfulPassword = restfulPassword; } public MeshMSConversationList meshmsListConversations(SubscriberId sid) throws ServalDInterfaceException, IOException, MeshMSException @@ -105,9 +66,6 @@ public class ServalDClient implements ServalDHttpConnectionFactory // interface ServalDHttpConnectionFactory public HttpURLConnection newServalDHttpConnection(String path) throws ServalDInterfaceException, IOException { - connect(); - assert restfulPassword != null; - assert httpPort != 0; URL url = new URL("http", "localhost", httpPort, path); URLConnection uconn = url.openConnection(); HttpURLConnection conn; @@ -117,7 +75,6 @@ public class ServalDClient implements ServalDHttpConnectionFactory catch (ClassCastException e) { throw new ServalDInterfaceException("URL.openConnection() returned a " + uconn.getClass().getName() + ", expecting a HttpURLConnection", e); } - int status = 0; conn.setAllowUserInteraction(false); try { conn.addRequestProperty("Authorization", "Basic " + Base64.encode((restfulUsername + ":" + restfulPassword).getBytes("US-ASCII"))); diff --git a/java/org/servalproject/servaldna/ServerControl.java b/java/org/servalproject/servaldna/ServerControl.java new file mode 100644 index 00000000..ebc0f87c --- /dev/null +++ b/java/org/servalproject/servaldna/ServerControl.java @@ -0,0 +1,109 @@ +package org.servalproject.servaldna; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.SecureRandom; + +/** + * Created by jeremy on 20/06/14. + */ +public class ServerControl { + private String instancePath; + private final String execPath; + private int loopbackMdpPort; + private int httpPort=0; + private int pid; + private static final String restfulUsername="ServalDClient"; + private ServalDClient client; + + public ServerControl(){ + this(null); + } + public ServerControl(String execPath){ + this.execPath = execPath; + } + + public String getInstancePath(){ + return instancePath; + } + + private void setStatus(ServalDCommand.Status result){ + loopbackMdpPort = result.mdpInetPort; + pid = result.pid; + httpPort = result.httpPort; + instancePath = result.instancePath; + } + + private void clearStatus(){ + loopbackMdpPort = 0; + pid = 0; + httpPort = 0; + client = null; + } + + public void start() throws ServalDFailureException { + if (execPath==null) + setStatus(ServalDCommand.serverStart()); + else + setStatus(ServalDCommand.serverStart(execPath)); + } + + public void stop() throws ServalDFailureException { + try{ + ServalDCommand.serverStop(); + }finally{ + clearStatus(); + } + } + + public void restart() throws ServalDFailureException { + try { + stop(); + } catch (ServalDFailureException e) { + // ignore failures, at least we tried... + e.printStackTrace(); + } + start(); + } + + public boolean isRunning() throws ServalDFailureException { + ServalDCommand.Status s = ServalDCommand.serverStatus(); + + if (s.status.equals("running")) { + setStatus(s); + }else{ + clearStatus(); + } + return pid!=0; + } + + public MdpServiceLookup getMdpService(ChannelSelector selector, AsyncResult results) throws ServalDInterfaceException, IOException { + if (!isRunning()) + throw new ServalDInterfaceException("server is not running"); + return new MdpServiceLookup(selector, this.loopbackMdpPort, results); + } + + public MdpDnaLookup getMdpDnaLookup(ChannelSelector selector, AsyncResult results) throws ServalDInterfaceException, IOException { + if (!isRunning()) + throw new ServalDInterfaceException("server is not running"); + return new MdpDnaLookup(selector, this.loopbackMdpPort, results); + } + + public ServalDClient getRestfulClient() throws ServalDInterfaceException { + if (!isRunning()) + throw new ServalDInterfaceException("server is not running"); + if (client==null) { + String restfulPassword = ServalDCommand.getConfigItem("rhizome.api.restful.users." + restfulUsername + ".password"); + if (restfulPassword == null) { + String pwd = new BigInteger(130, new SecureRandom()).toString(32); + ServalDCommand.setConfigItem("rhizome.api.restful.users." + restfulUsername + ".password", pwd); + ServalDCommand.configSync(); + restfulPassword = ServalDCommand.getConfigItem("rhizome.api.restful.users." + restfulUsername + ".password"); + if (restfulPassword == null) + throw new ServalDInterfaceException("Failed to set restful password"); + } + client = new ServalDClient(this.httpPort, restfulUsername, restfulPassword); + } + return client; + } +} diff --git a/java/org/servalproject/test/CommandLine.java b/java/org/servalproject/test/CommandLine.java index f5fd1b63..fd2de9bf 100644 --- a/java/org/servalproject/test/CommandLine.java +++ b/java/org/servalproject/test/CommandLine.java @@ -4,10 +4,11 @@ import org.servalproject.servaldna.AsyncResult; import org.servalproject.servaldna.ChannelSelector; import org.servalproject.servaldna.MdpDnaLookup; import org.servalproject.servaldna.MdpServiceLookup; -import org.servalproject.servaldna.MdpSocket; import org.servalproject.servaldna.ResultList; import org.servalproject.servaldna.ServalDCommand; import org.servalproject.servaldna.ServalDFailureException; +import org.servalproject.servaldna.ServalDInterfaceException; +import org.servalproject.servaldna.ServerControl; import org.servalproject.servaldna.SubscriberId; import java.io.IOException; @@ -34,14 +35,8 @@ public class CommandLine { } } - static void lookup(String did) throws IOException, InterruptedException, ServalDFailureException { - ServalDCommand.Status s = ServalDCommand.serverStatus(); - System.out.println(s); - if (s.getResult()!=0) - throw new ServalDFailureException("Serval daemon isn't running"); - MdpSocket.loopbackMdpPort = s.mdpInetPort; - ChannelSelector selector = new ChannelSelector(); - MdpDnaLookup lookup = new MdpDnaLookup(selector, new AsyncResult() { + static void lookup(String did) throws IOException, InterruptedException, ServalDInterfaceException { + MdpDnaLookup lookup = new ServerControl().getMdpDnaLookup(new ChannelSelector(), new AsyncResult() { @Override public void result(ServalDCommand.LookupResult nextResult) { System.out.println(nextResult.toString()); @@ -52,14 +47,8 @@ public class CommandLine { lookup.close(); } - static void service(String pattern) throws IOException, InterruptedException, ServalDFailureException { - ServalDCommand.Status s = ServalDCommand.serverStatus(); - System.out.println(s); - if (s.getResult()!=0) - throw new ServalDFailureException("Serval daemon isn't running"); - MdpSocket.loopbackMdpPort = s.mdpInetPort; - ChannelSelector selector = new ChannelSelector(); - MdpServiceLookup lookup = new MdpServiceLookup(selector, new AsyncResult() { + static void service(String pattern) throws IOException, InterruptedException, ServalDInterfaceException { + MdpServiceLookup lookup = new ServerControl().getMdpService(new ChannelSelector(), new AsyncResult() { @Override public void result(MdpServiceLookup.ServiceResult nextResult) { System.out.println(nextResult.toString()); diff --git a/java/org/servalproject/test/Meshms.java b/java/org/servalproject/test/Meshms.java index 6731b4e7..073001d8 100644 --- a/java/org/servalproject/test/Meshms.java +++ b/java/org/servalproject/test/Meshms.java @@ -20,23 +20,23 @@ package org.servalproject.test; -import java.lang.System; -import java.io.IOException; -import org.servalproject.servaldna.SubscriberId; - import org.servalproject.servaldna.ServalDClient; import org.servalproject.servaldna.ServalDInterfaceException; -import org.servalproject.servaldna.meshms.MeshMSConversationList; +import org.servalproject.servaldna.ServerControl; +import org.servalproject.servaldna.SubscriberId; import org.servalproject.servaldna.meshms.MeshMSConversation; -import org.servalproject.servaldna.meshms.MeshMSMessageList; -import org.servalproject.servaldna.meshms.MeshMSMessage; +import org.servalproject.servaldna.meshms.MeshMSConversationList; import org.servalproject.servaldna.meshms.MeshMSException; +import org.servalproject.servaldna.meshms.MeshMSMessage; +import org.servalproject.servaldna.meshms.MeshMSMessageList; + +import java.io.IOException; public class Meshms { static void meshms_list_conversations(SubscriberId sid) throws ServalDInterfaceException, IOException, InterruptedException { - ServalDClient client = ServalDClient.newServalDClient(); + ServalDClient client = new ServerControl().getRestfulClient(); MeshMSConversationList list = null; try { list = client.meshmsListConversations(sid); @@ -64,7 +64,7 @@ public class Meshms { static void meshms_list_messages(SubscriberId sid1, SubscriberId sid2) throws ServalDInterfaceException, IOException, InterruptedException { - ServalDClient client = ServalDClient.newServalDClient(); + ServalDClient client = new ServerControl().getRestfulClient(); MeshMSMessageList list = null; try { list = client.meshmsListMessages(sid1, sid2); diff --git a/tests/meshmsjava b/tests/meshmsjava index 248591b0..05cc7e17 100755 --- a/tests/meshmsjava +++ b/tests/meshmsjava @@ -32,11 +32,6 @@ setup() { set log.console.level debug \ set debug.httpd on create_identities 4 - configure_servald_server() { - add_servald_interface - executeOk_servald config \ - set rhizome.api.restful.users.joe.password bloggs - } start_servald_server }