From 945e2116dae6dc7529dc46002dd08b45daa4856f Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Wed, 29 Aug 2012 23:27:31 +0000 Subject: many bug fixes, server/service working, signal pipe working --- ISSUES | 300 +++++------------ bin/gnunet-dht | 2 +- src/org/gnunet/construct/ByteFill.java | 39 --- src/org/gnunet/construct/Construct.java | 61 +++- src/org/gnunet/construct/Constructable.java | 28 -- src/org/gnunet/construct/Double.java | 3 + src/org/gnunet/construct/FixedSizeArray.java | 1 - src/org/gnunet/construct/FixedSizeByteArray.java | 39 --- .../gnunet/construct/FixedSizeIntegerArray.java | 21 ++ src/org/gnunet/construct/Int16.java | 2 +- src/org/gnunet/construct/Int32.java | 2 +- src/org/gnunet/construct/Int64.java | 4 +- src/org/gnunet/construct/Int8.java | 2 +- src/org/gnunet/construct/IntegerFill.java | 4 + .../construct/MessageIdAnnotationProcessor.java | 2 +- src/org/gnunet/construct/MessageLoader.java | 31 +- src/org/gnunet/construct/MessageUnion.java | 2 +- src/org/gnunet/construct/MsgMap.txt | 9 +- src/org/gnunet/construct/ProtocolViolation.java | 35 -- .../construct/ProtocolViolationException.java | 35 ++ src/org/gnunet/construct/ReflectUtil.java | 10 +- src/org/gnunet/construct/UInt16.java | 2 +- src/org/gnunet/construct/UInt32.java | 2 +- src/org/gnunet/construct/UInt64.java | 2 +- src/org/gnunet/construct/UInt8.java | 2 +- src/org/gnunet/construct/Union.java | 3 + src/org/gnunet/construct/UnionCase.java | 6 + .../gnunet/construct/parsers/ByteFillParser.java | 100 ------ src/org/gnunet/construct/parsers/FillParser.java | 10 +- .../construct/parsers/FixedSizeArrayParser.java | 3 + .../parsers/FixedSizeByteArrayParser.java | 71 ---- .../parsers/FixedSizeIntegerArrayParser.java | 99 ++++++ .../construct/parsers/IntegerFillParser.java | 20 +- .../gnunet/construct/parsers/IntegerParser.java | 7 +- src/org/gnunet/construct/parsers/NestedParser.java | 8 +- .../gnunet/construct/parsers/SequenceParser.java | 7 +- src/org/gnunet/construct/parsers/StringParser.java | 24 +- src/org/gnunet/construct/parsers/UnionParser.java | 4 +- .../construct/parsers/VariableSizeArrayParser.java | 2 +- .../parsers/VariableSizeIntegerArrayParser.java | 1 - src/org/gnunet/construct/parsers/package-info.java | 25 ++ src/org/gnunet/core/ConnectNotifyMessage.java | 7 +- .../gnunet/core/NotifyInboundTrafficMessage.java | 2 +- .../gnunet/core/NotifyOutboundTrafficMessage.java | 2 +- src/org/gnunet/dht/ClientGetMessage.java | 2 +- src/org/gnunet/dht/ClientPutMessage.java | 2 +- src/org/gnunet/dht/ClientResultMessage.java | 2 +- src/org/gnunet/dht/DistributedHashTable.java | 18 +- src/org/gnunet/dht/MonitorGetRespMessage.java | 2 +- src/org/gnunet/dht/MonitorPutMessage.java | 2 +- src/org/gnunet/hello/HelloMessage.java | 50 +++ src/org/gnunet/hello/package-info.java | 25 ++ src/org/gnunet/mesh/ClientConnectMessage.java | 13 +- src/org/gnunet/mesh/ConnectHandler.java | 9 + src/org/gnunet/mesh/DisconnectHandler.java | 9 + src/org/gnunet/mesh/Mesh.java | 158 ++++++++- src/org/gnunet/mesh/TunnelCreateMessage.java | 14 +- src/org/gnunet/mesh/package-info.java | 25 ++ src/org/gnunet/nse/NetworkSizeEstimation.java | 10 +- src/org/gnunet/peerinfo/InfoMessage.java | 2 +- src/org/gnunet/peerinfo/PeerInfo.java | 2 +- src/org/gnunet/peerinfo/PeerProcessor.java | 2 +- .../gnunet/peerinfo/RsaPublicKeyBinaryEncoded.java | 4 +- src/org/gnunet/peerinfo/package-info.java | 25 ++ src/org/gnunet/requests/Request.java | 4 +- src/org/gnunet/requests/package-info.java | 25 ++ src/org/gnunet/statistics/Statistics.java | 24 +- src/org/gnunet/testing/TestingServer.java | 2 +- src/org/gnunet/testing/TestingSetup.java | 37 +-- src/org/gnunet/testing/TestingSubsystem.java | 6 +- src/org/gnunet/testing/package-info.java | 27 ++ src/org/gnunet/transport/Transport.java | 2 +- src/org/gnunet/transport/package-info.java | 25 ++ src/org/gnunet/util/AbsoluteTime.java | 37 ++- src/org/gnunet/util/Client.java | 111 +++++-- src/org/gnunet/util/Configuration.java | 6 +- src/org/gnunet/util/Connection.java | 78 ++++- src/org/gnunet/util/Continuation.java | 2 +- src/org/gnunet/util/HashCode.java | 7 +- src/org/gnunet/util/HelloMessage.java | 50 --- src/org/gnunet/util/PeerIdentity.java | 4 +- src/org/gnunet/util/Program.java | 48 ++- src/org/gnunet/util/Resolver.java | 20 +- src/org/gnunet/util/RunaboutUtil.java | 1 - src/org/gnunet/util/Scheduler.java | 21 +- src/org/gnunet/util/Server.java | 365 +++++++++++++++++---- src/org/gnunet/util/Service.java | 97 ++++-- src/org/gnunet/util/Strings.java | 75 +++-- src/org/gnunet/util/TESTMessage.java | 31 -- src/org/gnunet/util/TestMessage.java | 31 ++ src/org/gnunet/util/UnknownMessageBody.java | 2 +- src/org/gnunet/util/getopt/Argument.java | 47 +++ src/org/gnunet/util/getopt/ArgumentAction.java | 29 ++ src/org/gnunet/util/getopt/Option.java | 44 --- src/org/gnunet/util/getopt/OptionAction.java | 26 -- src/org/gnunet/util/getopt/Parser.java | 66 ++-- src/org/grothoff/Runabout.java | 5 +- src/org/grothoff/package-info.java | 2 +- test/org/gnunet/construct/ByteFillMessage.java | 2 +- test/org/gnunet/construct/ConstructTest.java | 2 +- test/org/gnunet/construct/FillParserTest.java | 37 +++ test/org/gnunet/construct/FixedSizeTest.java | 52 +++ test/org/gnunet/construct/FrameSizeTest.java | 50 +++ test/org/gnunet/construct/QueryMessage.java | 2 +- test/org/gnunet/construct/SendMessageTest.java | 34 ++ test/org/gnunet/construct/SimpleTestMessage.java | 36 -- test/org/gnunet/construct/SimpleTestMessage2.java | 29 -- test/org/gnunet/construct/SizeTestMessage.java | 34 -- test/org/gnunet/construct/StringTest.java | 50 +++ test/org/gnunet/construct/StringTuple.java | 29 ++ test/org/gnunet/construct/VarTestMessage.java | 29 -- .../gnunet/construct/VariableSizeArrayTest.java | 33 ++ test/org/gnunet/core/CoreTest.java | 11 +- test/org/gnunet/dht/DHTTest.java | 4 +- test/org/gnunet/nse/NSETest.java | 56 ++++ test/org/gnunet/statistics/StatisticsTest.java | 10 +- test/org/gnunet/testing/TestingSetupTest.java | 12 +- test/org/gnunet/util/ClientServerTest.java | 171 +++++++++- test/org/gnunet/util/FilePipeExample.java | 3 +- test/org/gnunet/util/MeshTest.java | 36 ++ test/org/gnunet/util/ResolverTest.java | 4 +- test/org/gnunet/util/ServerExample.java | 5 +- test/org/gnunet/util/StringsTest.java | 20 ++ test/org/gnunet/util/getopt/GetoptTest.java | 113 +++++-- test/org/nse/NSETest.java | 58 ---- tools/build | 3 + tools/coverage | 3 - 127 files changed, 2350 insertions(+), 1352 deletions(-) delete mode 100644 src/org/gnunet/construct/ByteFill.java delete mode 100644 src/org/gnunet/construct/Constructable.java delete mode 100644 src/org/gnunet/construct/FixedSizeByteArray.java create mode 100644 src/org/gnunet/construct/FixedSizeIntegerArray.java delete mode 100644 src/org/gnunet/construct/ProtocolViolation.java create mode 100644 src/org/gnunet/construct/ProtocolViolationException.java delete mode 100644 src/org/gnunet/construct/parsers/ByteFillParser.java delete mode 100644 src/org/gnunet/construct/parsers/FixedSizeByteArrayParser.java create mode 100644 src/org/gnunet/construct/parsers/FixedSizeIntegerArrayParser.java create mode 100644 src/org/gnunet/construct/parsers/package-info.java create mode 100644 src/org/gnunet/hello/HelloMessage.java create mode 100644 src/org/gnunet/hello/package-info.java create mode 100644 src/org/gnunet/mesh/ConnectHandler.java create mode 100644 src/org/gnunet/mesh/DisconnectHandler.java create mode 100644 src/org/gnunet/mesh/package-info.java create mode 100644 src/org/gnunet/peerinfo/package-info.java create mode 100644 src/org/gnunet/requests/package-info.java create mode 100644 src/org/gnunet/testing/package-info.java create mode 100644 src/org/gnunet/transport/package-info.java delete mode 100644 src/org/gnunet/util/HelloMessage.java delete mode 100644 src/org/gnunet/util/TESTMessage.java create mode 100644 src/org/gnunet/util/TestMessage.java create mode 100644 src/org/gnunet/util/getopt/Argument.java create mode 100644 src/org/gnunet/util/getopt/ArgumentAction.java delete mode 100644 src/org/gnunet/util/getopt/Option.java delete mode 100644 src/org/gnunet/util/getopt/OptionAction.java create mode 100644 test/org/gnunet/construct/FillParserTest.java create mode 100644 test/org/gnunet/construct/FixedSizeTest.java create mode 100644 test/org/gnunet/construct/FrameSizeTest.java create mode 100644 test/org/gnunet/construct/SendMessageTest.java delete mode 100644 test/org/gnunet/construct/SimpleTestMessage.java delete mode 100644 test/org/gnunet/construct/SimpleTestMessage2.java delete mode 100644 test/org/gnunet/construct/SizeTestMessage.java create mode 100644 test/org/gnunet/construct/StringTest.java create mode 100644 test/org/gnunet/construct/StringTuple.java delete mode 100644 test/org/gnunet/construct/VarTestMessage.java create mode 100644 test/org/gnunet/construct/VariableSizeArrayTest.java create mode 100644 test/org/gnunet/nse/NSETest.java create mode 100644 test/org/gnunet/util/MeshTest.java create mode 100644 test/org/gnunet/util/StringsTest.java delete mode 100644 test/org/nse/NSETest.java diff --git a/ISSUES b/ISSUES index 2528f68..4b5544d 100644 --- a/ISSUES +++ b/ISSUES @@ -1,248 +1,106 @@ -* I'm currently confused about the statistics API bug (from C), and how shutdown/disconnect is/should be handled. -* gnunet vs GNUnet, gnunet-java project name -* I'm currently trying to increase the robustness of the service APIs, - discuss behavior in some cases -* packaging requirements +in gnunet-java-ext there is a working example of a service and a corresponding client program, that actually work ;) + * simple greeting server, client gives name and server returns greeting + * illustrates using program/service, using the configuration, creating messages + * works with os control pipe / arm +arm-4477 WARNING Configuration file `(null)' for service `greeting' not valid: option missing -==================================================================== +even when not using the signal pipe, does arm really kill processes? + * arm does never seem to send a sigkill if process does not respond + * sometimes arm command hangs! -* IzPack is ~15MB, do we really want to have it in the svn repo? -No, env var. + $ gnunet-arm -c config/greeting.conf -k greeting -LDEBUG + Aug 29 19:25:22-808046 arm-api-5971 INFO Stopping service `greeting' within 60000 ms + Service `greeting' was already not running. -* the Runabout can now be an anonymous inner class - * implementation overhead: for public subclasses only constant overhead in the Runabout constructor - * for private/anonymous inner classes: 10-20% overhead, measured over 100M calls - * only issue left: visit methods have to be public, but this is a non issue. -* review the RequestQueue mechanism +Aug 29 18:21:19-505909 arm-4751 ERROR Failed to start service `greeting' +Service `greeting' has been started. -* Statistics: - * currently watches can't be canceled on the service level, only on the api level, is this intentional? -=> fine for now +logging with arm: what gets piped to where + * seems like service stdout->/dev/null, service stderr->arm stderr -* DHT: - * getStart timeout does not really make sense (why timeout for transmission to the service, - but not for retrieval of answers?) - Is this just a documentation error? -=> fine to NOT have the timeout argument in the Java API +Construct: has gotten very complex, i'm currently trying to trace a particular bug + * UPDATE: bug is gone! + * unit tests for construct have gotten better, still not good enough + * FrameSize + * recursive messages + * indirect recursion (over unions) (see core.SendMessage) works + * direct recursion has problems (see test/org.gnunet.construct.FrameSizeTest) -* core: - * what happens if during a transmission the service disconnects? should we retry? -=> "no" +Question: Is all this too complicated? Should I invest the time to fix things as they are now intended, or should +we simplify? -* with the changes in ARM there is no way to restart gnunet with arm if some client is misbehaving -=> SVN UP +* found some problems with timeouts in client/connection. + * should i write unit-tests for this timing-stuff? + * UPDATE: should be fixed now! -* I'm often getting - May 09 09:21:49-194786 util-11121 WARNING `socket' failed at connection.c:892 with error: Too many open files -=> SVN UP +* program/service in general: + * how to handle the return value of main? + * java has no return value for main + * we must use System.exit(n) instead + * how about a (Program/Service).exit(n) that does cleanup and then calls System.exit(n)? +* configuration: what is $DEFAULTCONFIG? and CONFIG=? -================== +mesh: + * we can't use multipe instances of org.gnunet.mesh.Mesh to test the API + * the local peer can't be the peer on the other end! + * the C-api has practically no coverage on mesh_api.c + * see below for a suggestion! + * LOCAL_TUNNEL_CREATE was used for two things, creating tunnels and being notified about incoming tunnels -#!bin/sh -java -jar $GNUNET_JAVA_PREFIX/lib/gnunet.jar +* extending org.gnunet.testing so that multiple peers can be started and communicate with each other! + * the C-testing-api allows to create multiple peers, but they don't seem to be able to communicate with each + other! + * => the peers should somehow exchange their hellos / use a shared directory for the hellos + testbed vs testing: is this correct? + * testbed for large-scale testing across many "real" nodes + * testing for testcases on one host -================== +* test coverage approaching a better state, any feedback? -#!/bin/sh -if test -z $GNUNET_JAVA_PREFIX -then - GNUNET_JAVA_PREFIX=%INSTALL_PREFIX% -fi -java -jar $GNUNET_JAVA_PREFIX/lib/gnunet.jar +* I think there should be some documentation in addition to the source code and the tutorial + * what would be the preferred format for such documentation? latex? + * (considering that they perhaps should end up on a website / should be browsable) + * examples: + * how do unions work in construct? + * other stuff in construct + * how does annotation processing work? + * project layout - what goes where + * as there is no standard java project layout +rationale for putting org.gnunet.testing in the src/-tree, not in test/: + * when developing an extension for gnunet-java, the developers may want to access the testing functionality from + the jar, so it should be included there! + * the code itself is tested, we want coverage etc. -=========================================================== +* is there an effort to document what hostkeys files are etc.? -* @UNIXONLY@ PORT = 2089 in src/util/resolver.conf.on - * what's the purpose? we also need this line to be enabled on JAVAPORT +continuous integration: using Jenkins (=Hudson, forked away from Oracle) + * easier to configure than buildbot + * support for JUnit out-of-the-box + * support for cobertura via plugin -* something I didn't think through from the beginning: - how should unknown message types be handled by message handlers? - * sometimes we want to see the message (e.g. in server), sometimes it is an error - * alternative 1: signal an error to the message handler, somehow pass the original message - * alternative 2: pass a special UnknownMessage to the message handler, filter it for higher-level - APIs and signal an error. +build system: still maintaining bash scripts, trying out gradle + * faster builds, build scripts far easier to read/write + * no integration with cobertura :( + * gradle has excellent Ant integration => Coverage (bash wrapper not very usable) + * can generate project files for eclipse/intellij +what's next? + * some actual stuff built with gnunet-java? -* the recvDone is kind of clunky (see test/org.gnunet.util.ServerExample) +* general question: when should an api use per-connection receive, and per-service-receive? -* finally core/statistics/dht/resolver/... have unit tests! - (and coverage works again, but i can't access the cobertura via ssh yet) - * currently most test rely on a running gnunet, and use the default configuration - * alternative approach: - * configuration is copied from resource (may be in a jar!) to a temp file, then passed on the command - line to gunet-service-* - * useful to test behavior on disconnects - * problem: java sucks at managing processes, processes stay alive if we abort a test - * dht get: can we assume that "our" peer immediately stores the value? +does this make sense / do we need it: + * support in the scheduler for communicating asynchronously with other processes via stdin/stdout/stderr + * currently only used in testing, uses blocking i/o -* naming: when do we use destroy, when do we use disconnect, or is this arbitrary? - start-stop, create-destroy, connect-disconnect; constructor: fine, destructor: see C API +* stream is implemented as a library, not as a service + * why? + * should GNUnet-java also implement it? -* implications of using exceptions in callbacks - * esp. when the exception is non-fatal, i.e. the exception is caught, handled, and the program continues - * java exceptions have no restarts, may leave gnunet-java in inconsistent state; FINALLY! - -* how to test callbacks? we do not only need to test for the right values, but also check that callback has actually - been called. - * first approach: thow a TestSuccess exception, discarded (see above) - * current approach: build a list of assertions, check assertions after scheduler is done. - * each assertion stores whether it already succeeded and a message - - -* finalziers: used to destroy object. lead to heisenbug/double disconnect. now policy: check if object - has been disposed of properly, otherwise log a warning. - * cannot guarantee cleanup anyway - (java behavior: run finalizers iff unreachabla during gc, may never happen, finalizers on jvm shutdown deprecated) - - -* regarding peerinfo - * i don't fully understand how HELLOs work. - -* what are the next important services to implement? (probably mesh, peerinfo, transport) - - -* i'm currently considering to use the google guava library - * Apache License 2.0 - * con: large (1.8MB jar) - * but: unused class files could be stripped from the jar, e.g. using ProGuard - * would replace the apache commons io library - * contains collections used throughout gnunet - * bloomfilter - * multimap, alleviates the boilerplate code when dealing with hashmaps of lists - * methods to deal with "signed" primitives - * dealing with files (e.g. copying) - * redirecting i/o streams from/to files (when issuing external commands) - * hashing utilities - * tables (mapping from key pair to value), would replace nested Maps in e.g. Configurations - * ... - - - - -* long-term todos: - * refactor the Construct implementation, implement the "nicer" syntax - * refactor the getopt implementation - - - - -============================================================ - -* tests now run on the cobertura account :) - * see https://gnunet.org/cobertura/ - * gnunet needs to compiled with --disable-nls to work on the server - * cronjob added - * could/should we report the success of JUnit tests? - * (as somehow my bugs only seem to show when running on another system ;) - -* gnunet-java now can now start/restart services for testing with the testing wrapper executable - * i duplicated the code for GNUNET_TESTING_service_run_restartable - * now passes the Peer to main, which can start/stop it - * probably also should pass the config file name (not only the handle) - * how about a GNUNET_TESTING_peer_get_config_path? - * what about windows? - -* server/service: - * needed for testing the server: getting an unused port with java - * how do we test the signal pipeline? - * probably with runin arm in a testing peer, but then we would also have to talk to arm - - -the following notes are old: -* we are still not able to test "temporary destruction" - * because every time a new port may be generated - * we need a way to kill a service and run it with the same config - * now implemented! -* review if everything in Makefile.am is correct - * what is *_DEPENDENCIES vs *_LDADD? -* how do we get a handle to stdin (in a non-hacky way) that can be selected on by scheduler? -* c-getopt question: GNUNET_GETOPT_run returns index of first non-option. - so options and non-options may not be mixed? -* i have to manually write the config file, why isn't there a way to get the - file name, not just the ConfigurationHandle? -* could we get a confirmation that a service *really* is dead? - * or is this guaranteed if TESTING_service_run returns? - * otherwise there is no way to reliably test behavior on interrupted connection -* TESTING_service_run does not indicate that the service could not be run! - * just logs an error - * currently we check availability of data on stderr. - - -PEERINFO_GET vs PEERINFO_GET_ALL - * if we have PEERINFO_GET_ALL, why is the peer-field in ListPeerMessage(with type=PEERINFO_GET) empty? - - -* are peerinfo requests queued? - * i remember discussing peerinfo as a compilicated example for a general message queueing implementation. why? - * if we have to queue: how about a "choke/release" for the outgoing message queue? - * otherwise we don't know which requests belongs to which response -* can peerinfo return more than one record per peer? - - --------------------------------------------------------------- - -* see mantis for problem with the signal pipe - -* what should the gnunet-testing-run-server tool be called, now that - there already is plain gnunet-testing? - -* where should TESTMessage and HELLOMessage, PeerIdentity, HashCode go? - * and do we want to call them TESTMessage or TestMessage? - -* had a bug in the IPv6 address parsing code - * tried to fix it / rewrite it, eventually got frustrating - * found out guava has an implementation of this :) - * also implements shortening (like ::1) - * by reading the code: implementing all this correctly would not have been a fun time - -* TestingServer now allows the client/connection/server to be tested easily - * found quite some bugs during this - -* thoughts about exponential backoff / the client-connection stuff in GNUnet and gnunet-java - * why do we wait the entire backoff period, if the connection could be available earlier? - -* discuss what mesh does, what transport does - * i found the documentation for transport on gnunet.org - * the is not much information about mesh, except for the source code - ---------------------------------------------------------------- - -* reference count / receive_done behavior is a bit strange / confusing - * clients are disconnected only when refcnt==0 *and* shutdown is requested? - * behavior on receive done: when success=1 but refcnt=0, why don't we disconnect the client? - - /** - * Was processing if incoming messages suspended while - * we were still processing data already received? - * This is a counter saying how often processing was - * suspended (once per handler invoked). - */ - -I don't understand that comment! - - - -* im currently confused about the different layers of GNUnet / I don't get the big picture - * e.g. transport's distance vector plugin vs mesh - * peerinfo / mesh - * assuming a large network, doesn't a client have to store a large amout of information? - -* how to test MESH? - * maybe talk to Harsha about testbed? :) - -* interesting things happen with JUnit - * failure of one test causes timeout in another - -* review org.gnunet.testing - -cp a x ; cp b x -is not the same as -cp b x ; cp a x -if x does not exist prior to copying diff --git a/bin/gnunet-dht b/bin/gnunet-dht index 840db34..951355a 100755 --- a/bin/gnunet-dht +++ b/bin/gnunet-dht @@ -1,11 +1,11 @@ #!/bin/sh -DIR=`dirname $0` if [ "%GNJ_INSTALLED" = "true" ]; then export CLASSPATH="%INSTALL_PATH/share/java/*" else + DIR=`dirname $0` export CLASSPATH="$DIR/../build/:$DIR/../lib/*" fi diff --git a/src/org/gnunet/construct/ByteFill.java b/src/org/gnunet/construct/ByteFill.java deleted file mode 100644 index febc64c..0000000 --- a/src/org/gnunet/construct/ByteFill.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2011, 2012 Christian Grothoff (and other contributing authors) - - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, or (at your - option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. - */ - -package org.gnunet.construct; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Fills a byte array with the remaining data in the current frame. - * Annotation target must be of type "byte[]" - * - * Deprecated, you should use @IntegerFill(false, 8), or @FillWith @UInt8 once it is available. - * - * @author Florian Dold - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.FIELD) -public @interface ByteFill { -} diff --git a/src/org/gnunet/construct/Construct.java b/src/org/gnunet/construct/Construct.java index 603f5a5..965b97b 100644 --- a/src/org/gnunet/construct/Construct.java +++ b/src/org/gnunet/construct/Construct.java @@ -50,7 +50,7 @@ Wanted syntax (not fully implemented yet) /** - * Parse and write the binary representation of java classes, as defined by org.gnunet.construct.*-Annotations + * Parse and write the binary representation of java classes, as defined by org.gnunet.construct.*-annotations * on their members. * * @author Christian Grothoff @@ -62,9 +62,18 @@ public class Construct { .getLogger(Construct.class); - private static HashMap, Parser> parserCache = new HashMap, + private static Map, Parser> parserCache = new HashMap, Parser>(100); + + /** + * The class Construct is not intended to be instantiated, this its constructor is private. + */ + private Construct() { + + } + + /** * Given a byte buffer with a message, parse it into an object of type c. The * fields of the class are expected to be annotated with annotations from @@ -82,6 +91,15 @@ public class Construct { return m; } + /** + * Given a byte array with a message, parse it into an object of type c. The + * fields of the class are expected to be annotated with annotations from + * the construct package. + * + * @param srcBuf buffer with the serialized binary data + * @param c desired object type to return + * @return instance of the desired object type + */ public static T parseAs(byte[] srcBuf, Class c) { return parseAs(ByteBuffer.wrap(srcBuf), c); } @@ -248,8 +266,7 @@ public class Construct { Field nestedField = field; if (n.newFrame()) { - ParserGenerator pg = new ParserGenerator(); - Parser p = getParser((Class) nestedField.getType(), pg); + Parser p = getParser((Class) nestedField.getType()); parser = new NestedParser(p, n.optional(), nestedField, true); @@ -269,11 +286,6 @@ public class Construct { } } - public void visit(ByteFill bf) { - parser = new ByteFillParser(field); - } - - public void visit(FixedSizeArray fsa) { Field f = field; int elemNumber = fsa.length(); @@ -284,14 +296,13 @@ public class Construct { parser = new FixedSizeArrayParser(elemNumber, parser, f); } - public void visit(FixedSizeByteArray fsba) { - Field f = field; - int elemNumber = fsba.length(); - parser = new FixedSizeByteArrayParser(elemNumber, f); + public void visit(FixedSizeIntegerArray fsa) { + Field f = field; + int elemNumber = fsa.length(); + parser = new FixedSizeIntegerArrayParser(elemNumber, fsa.signed(), fsa.bitSize() / 8, f); } - public void visit(Double d) { if (!field.getType().equals(java.lang.Double.TYPE)) { throw new AssertionError("@Double target must be a primitive 'double' field"); @@ -322,6 +333,10 @@ public class Construct { Parser p = getParser((Class) field.getType() .getComponentType()); + if (!Message.class.isAssignableFrom(field.getType().getComponentType())) { + throw new AssertionError("VariableSizeArray only valid on arrays of messages."); + } + try { parser = new VariableSizeArrayParser(p, c.getField(vsa .lengthField()), field); @@ -412,10 +427,10 @@ public class Construct { } /** - * Compute the size of a serialized message. + * Compute the exact size of a serialized message. * * @param m object to serialize - * @return number of bytes required, -1 on error + * @return number of bytes required to store the message in binary form */ public static int getSize(Message m) { if (m == null) { @@ -425,6 +440,13 @@ public class Construct { return p.getSize(m); } + + /** + * Return the binary representation of the message m + * + * @param m the message to serialize + * @return a byte array containing the serialized message + */ public static byte[] toBinary(Message m) { byte[] a = new byte[getSize(m)]; ByteBuffer buf = ByteBuffer.wrap(a); @@ -432,6 +454,13 @@ public class Construct { return a; } + /** + * Fill in all fields of a message that are inferable from existing information. + * + * Examples: The size field for variable size arrays, the type of unions, ... + * + * @param m the message that should be patched + */ public static void patch(Message m) { Parser p = getParser(m.getClass()); p.patch(m, p.getSize(m), null, m); diff --git a/src/org/gnunet/construct/Constructable.java b/src/org/gnunet/construct/Constructable.java deleted file mode 100644 index 1981492..0000000 --- a/src/org/gnunet/construct/Constructable.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2011, 2012 Christian Grothoff (and other contributing authors) - - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, or (at your - option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. - */ - -package org.gnunet.construct; - -/** - * Implemented by classes that know how to serialize/deserialize themselves. - */ -public interface Constructable { - // todo -} diff --git a/src/org/gnunet/construct/Double.java b/src/org/gnunet/construct/Double.java index 21f128e..f20e137 100644 --- a/src/org/gnunet/construct/Double.java +++ b/src/org/gnunet/construct/Double.java @@ -25,6 +25,9 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * A number stored in the IEEE 754 double-precision binary floating-point format. + */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Double { diff --git a/src/org/gnunet/construct/FixedSizeArray.java b/src/org/gnunet/construct/FixedSizeArray.java index ce6ff6b..699ae68 100644 --- a/src/org/gnunet/construct/FixedSizeArray.java +++ b/src/org/gnunet/construct/FixedSizeArray.java @@ -29,7 +29,6 @@ import java.lang.annotation.Target; * An array of messages with static size. * * @author Florian Dold - * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) diff --git a/src/org/gnunet/construct/FixedSizeByteArray.java b/src/org/gnunet/construct/FixedSizeByteArray.java deleted file mode 100644 index 6232718..0000000 --- a/src/org/gnunet/construct/FixedSizeByteArray.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2011, 2012 Christian Grothoff (and other contributing authors) - - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, or (at your - option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. - */ - -package org.gnunet.construct; - - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * An array of bytes with static size. - * - * @author Florian Dold - * - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.FIELD) -public @interface FixedSizeByteArray { - int length(); -} \ No newline at end of file diff --git a/src/org/gnunet/construct/FixedSizeIntegerArray.java b/src/org/gnunet/construct/FixedSizeIntegerArray.java new file mode 100644 index 0000000..b2418b8 --- /dev/null +++ b/src/org/gnunet/construct/FixedSizeIntegerArray.java @@ -0,0 +1,21 @@ +package org.gnunet.construct; + + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +/** + * An array of integers with static size. + * + * @author Florian Dold + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface FixedSizeIntegerArray { + int length(); + int bitSize(); + boolean signed(); +} diff --git a/src/org/gnunet/construct/Int16.java b/src/org/gnunet/construct/Int16.java index 8017636..677324b 100644 --- a/src/org/gnunet/construct/Int16.java +++ b/src/org/gnunet/construct/Int16.java @@ -26,7 +26,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Synonym for Integer(byteSize=2, signed=true) + * Signed 16-bit integer value. * * @author Florian Dold * diff --git a/src/org/gnunet/construct/Int32.java b/src/org/gnunet/construct/Int32.java index 97d904a..da993ca 100644 --- a/src/org/gnunet/construct/Int32.java +++ b/src/org/gnunet/construct/Int32.java @@ -26,7 +26,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Synonym for Integer(byteSize=4, signed=true) + * Signed 32-bit integer value. * * @author Florian Dold * diff --git a/src/org/gnunet/construct/Int64.java b/src/org/gnunet/construct/Int64.java index 7318c0d..be5d940 100644 --- a/src/org/gnunet/construct/Int64.java +++ b/src/org/gnunet/construct/Int64.java @@ -26,9 +26,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Synonym for Integer(byteSize=8, signed=true) - * - * @author Florian Dold + * Signed 64-bit integer value. * */ diff --git a/src/org/gnunet/construct/Int8.java b/src/org/gnunet/construct/Int8.java index fbf83f4..a0aaa14 100644 --- a/src/org/gnunet/construct/Int8.java +++ b/src/org/gnunet/construct/Int8.java @@ -27,7 +27,7 @@ import java.lang.annotation.Target; /** - * Synonym for Integer(byteSize=1, signed=true) + * Signed 8-bit integer value. * * @author Florian Dold * diff --git a/src/org/gnunet/construct/IntegerFill.java b/src/org/gnunet/construct/IntegerFill.java index 846ee14..4564c83 100644 --- a/src/org/gnunet/construct/IntegerFill.java +++ b/src/org/gnunet/construct/IntegerFill.java @@ -25,6 +25,10 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * Fills the rest of the message with integers of the specified kind. The annotation may only be present on the + * last serialized field of message. + */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface IntegerFill { diff --git a/src/org/gnunet/construct/MessageIdAnnotationProcessor.java b/src/org/gnunet/construct/MessageIdAnnotationProcessor.java index d27c992..8a17ba9 100644 --- a/src/org/gnunet/construct/MessageIdAnnotationProcessor.java +++ b/src/org/gnunet/construct/MessageIdAnnotationProcessor.java @@ -42,7 +42,7 @@ import java.util.*; /** - * Creates a resource file 'MsgMap.txt' in the package 'org.gnunet.construct'. + * Creates a resource file 'MsgMap.txt' in the package 'org.gnunet.construct' of the source tree. */ @SupportedAnnotationTypes("org.gnunet.construct.UnionCase") @SupportedSourceVersion(SourceVersion.RELEASE_6) diff --git a/src/org/gnunet/construct/MessageLoader.java b/src/org/gnunet/construct/MessageLoader.java index d40dfda..7ba2e60 100644 --- a/src/org/gnunet/construct/MessageLoader.java +++ b/src/org/gnunet/construct/MessageLoader.java @@ -37,21 +37,26 @@ import java.util.Map; /** - * Load message maps + * Load message maps, which contain the information the parse/write unions. */ public class MessageLoader { private static final Logger logger = LoggerFactory .getLogger(MessageLoader.class); - - + /** + * Thrown when a trying to serialize an object that is not registered as a union type. + */ public static class UnknownUnionException extends RuntimeException { public UnknownUnionException(String msg) { super(msg); } } + + /** + * Thrown when parsing a union whose ID is not known. + */ public static class UnknownUnionIdException extends RuntimeException { } @@ -86,19 +91,22 @@ public class MessageLoader { } public static void loadMessageMap(URL loc) { - if (loc == null) + if (loc == null) { throw new RuntimeException("could not load message map"); + } BufferedReader in = null; try { in = new BufferedReader(new InputStreamReader(loc.openStream())); String line; while ((line = in.readLine()) != null) { // skip empty lines and comments - if (line.isEmpty() || line.charAt(0) == '#') + if (line.isEmpty() || line.charAt(0) == '#') { continue; + } String[] m = line.split("="); - if (m.length != 2) + if (m.length != 2) { throw new RuntimeException("invalid message map format (separation by '=')"); + } String[] left = m[0].split("[|]"); if (left.length != 2) { logger.debug(m[0]); @@ -134,8 +142,9 @@ public class MessageLoader { private static void maybeClose(Closeable in) { try { - if (in != null) + if (in != null) { in.close(); + } } catch (IOException e) { throw new RuntimeException("error closing stream: " + e.getMessage()); } @@ -164,7 +173,7 @@ public class MessageLoader { Class cls = map.get(tag); if (cls == null) { - throw new ProtocolViolation("don't know how to translate message of type " + tag); + throw new ProtocolViolationException("don't know how to translate message of type " + tag); } return cls; @@ -173,10 +182,12 @@ public class MessageLoader { public static int getUnionTag(Class unionInterface, Class unionCase) { Map, Integer> map = tagmap.get(unionInterface); - if (map == null) + if (map == null) { throw new AssertionError(String.format("%s is not a known union type", unionInterface)); - if (!map.containsKey(unionCase)) + } + if (!map.containsKey(unionCase)) { throw new AssertionError(String.format("%s is not a known instance of %s", unionCase, unionInterface)); + } return map.get(unionCase); } } diff --git a/src/org/gnunet/construct/MessageUnion.java b/src/org/gnunet/construct/MessageUnion.java index 260b9f6..0481df2 100644 --- a/src/org/gnunet/construct/MessageUnion.java +++ b/src/org/gnunet/construct/MessageUnion.java @@ -21,7 +21,7 @@ package org.gnunet.construct; /** - * Marker interface for Message Unions + * Marker interface for message unions */ public interface MessageUnion extends Message { } diff --git a/src/org/gnunet/construct/MsgMap.txt b/src/org/gnunet/construct/MsgMap.txt index 9136d6e..8f09b68 100644 --- a/src/org/gnunet/construct/MsgMap.txt +++ b/src/org/gnunet/construct/MsgMap.txt @@ -1,9 +1,12 @@ org.gnunet.util.Resolver$Address|0=org.gnunet.util.Resolver$TextualAddress org.gnunet.util.Resolver$Address|1=org.gnunet.util.Resolver$NumericAddress org.gnunet.util.GnunetMessage$Body|68=org.gnunet.core.DisconnectNotifyMessage -org.gnunet.util.GnunetMessage$Body|1=org.gnunet.util.TESTMessage +org.gnunet.util.GnunetMessage$Body|1=org.gnunet.util.TestMessage +org.gnunet.util.GnunetMessage$Body|274=org.gnunet.mesh.TunnelDestroyMessage +org.gnunet.util.GnunetMessage$Body|273=org.gnunet.mesh.TunnelCreateMessage org.gnunet.util.GnunetMessage$Body|70=org.gnunet.core.NotifyInboundTrafficMessage org.gnunet.util.GnunetMessage$Body|71=org.gnunet.core.NotifyOutboundTrafficMessage +org.gnunet.util.GnunetMessage$Body|272=org.gnunet.mesh.ClientConnectMessage org.gnunet.util.GnunetMessage$Body|4=org.gnunet.util.Resolver$GetMessage org.gnunet.util.GnunetMessage$Body|64=org.gnunet.core.InitMessage org.gnunet.util.GnunetMessage$Body|5=org.gnunet.util.Resolver$ResolverResponse @@ -22,8 +25,8 @@ org.gnunet.util.GnunetMessage$Body|144=org.gnunet.dht.ClientGetStopMessage org.gnunet.util.GnunetMessage$Body|145=org.gnunet.dht.ClientResultMessage org.gnunet.util.GnunetMessage$Body|332=org.gnunet.peerinfo.InfoMessage org.gnunet.util.GnunetMessage$Body|333=org.gnunet.peerinfo.InfoEnd -org.gnunet.util.GnunetMessage$Body|331=org.gnunet.peerinfo.ListAllPeersMessage org.gnunet.util.GnunetMessage$Body|149=org.gnunet.dht.MonitorGetMessage +org.gnunet.util.GnunetMessage$Body|331=org.gnunet.peerinfo.ListAllPeersMessage org.gnunet.util.GnunetMessage$Body|150=org.gnunet.dht.MonitorGetRespMessage org.gnunet.util.GnunetMessage$Body|151=org.gnunet.dht.MonitorPutMessage org.gnunet.util.GnunetMessage$Body|171=org.gnunet.statistics.GetResponseEndMessage @@ -32,4 +35,4 @@ org.gnunet.util.GnunetMessage$Body|169=org.gnunet.statistics.GetMessage org.gnunet.util.GnunetMessage$Body|168=org.gnunet.statistics.SetMessage org.gnunet.util.GnunetMessage$Body|173=org.gnunet.statistics.WatchResponseMessage org.gnunet.util.GnunetMessage$Body|172=org.gnunet.statistics.WatchMessage -# generated 2012/06/27 15:23:37 +# generated 2012/08/22 15:06:49 diff --git a/src/org/gnunet/construct/ProtocolViolation.java b/src/org/gnunet/construct/ProtocolViolation.java deleted file mode 100644 index 028c62c..0000000 --- a/src/org/gnunet/construct/ProtocolViolation.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2011, 2012 Christian Grothoff (and other contributing authors) - - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, or (at your - option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. - */ - -package org.gnunet.construct; - - -/** - * Thrown when a received message is invalid - * - * @author Florian Dold - * - */ -public class ProtocolViolation extends RuntimeException { - - public ProtocolViolation(String s) { - super(s); - } -} diff --git a/src/org/gnunet/construct/ProtocolViolationException.java b/src/org/gnunet/construct/ProtocolViolationException.java new file mode 100644 index 0000000..a21cf17 --- /dev/null +++ b/src/org/gnunet/construct/ProtocolViolationException.java @@ -0,0 +1,35 @@ +/* + This file is part of GNUnet. + (C) 2011, 2012 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +package org.gnunet.construct; + + +/** + * Thrown when a received message is invalid. + * + * @author Florian Dold + * + */ +public class ProtocolViolationException extends RuntimeException { + + public ProtocolViolationException(String s) { + super(s); + } +} diff --git a/src/org/gnunet/construct/ReflectUtil.java b/src/org/gnunet/construct/ReflectUtil.java index 0bc50bc..532f4fa 100644 --- a/src/org/gnunet/construct/ReflectUtil.java +++ b/src/org/gnunet/construct/ReflectUtil.java @@ -58,11 +58,11 @@ public class ReflectUtil { } else if (t.equals(Integer.TYPE)) { Array.setInt(arr, i, (int) v); } else if (t.equals(Short.TYPE)) { - Array.setInt(arr, i, (short) v); + Array.setShort(arr, i, (short) v); } else if (t.equals(Byte.TYPE)) { - Array.setInt(arr, i, (byte) v); + Array.setByte(arr, i, (byte) v); } else if (t.equals(Character.TYPE)) { - Array.setInt(arr, i, (char) v); + Array.setChar(arr, i, (char) v); } } @@ -71,8 +71,7 @@ public class ReflectUtil { } /** - * assign an enum value to each numeric type we want to serialize in - * order do switch statements on field types + * An enumeration of all built-in type that can store integers. */ public enum NumFieldType { BIGNUM, BYTE_PRIM, SHORT_PRIM, INT_PRIM, LONG_PRIM, CHAR_PRIM @@ -252,6 +251,7 @@ public class ReflectUtil { for (int i = 0; i < path.size() - 1; ++i) { try { obj = path.get(i).get(obj); + } catch (IllegalArgumentException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { diff --git a/src/org/gnunet/construct/UInt16.java b/src/org/gnunet/construct/UInt16.java index 64e4341..18c24b2 100644 --- a/src/org/gnunet/construct/UInt16.java +++ b/src/org/gnunet/construct/UInt16.java @@ -27,7 +27,7 @@ import java.lang.annotation.Target; /** - * Synonym for Integer(byteSize=2, signed=false) + * Unsigned 16-bit integer value * * @author Florian Dold * diff --git a/src/org/gnunet/construct/UInt32.java b/src/org/gnunet/construct/UInt32.java index 4bd4e6b..ab4a278 100644 --- a/src/org/gnunet/construct/UInt32.java +++ b/src/org/gnunet/construct/UInt32.java @@ -26,7 +26,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Synonym for Integer(byteSize=4, signed=false) + * Unsigned 32-bit integer value * * @author Florian Dold * diff --git a/src/org/gnunet/construct/UInt64.java b/src/org/gnunet/construct/UInt64.java index 3d3b22a..d45cf69 100644 --- a/src/org/gnunet/construct/UInt64.java +++ b/src/org/gnunet/construct/UInt64.java @@ -26,7 +26,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Synonym for Integer(byteSize=8, signed=false) + * Unsigned 64-bit integer value * * @author Florian Dold * diff --git a/src/org/gnunet/construct/UInt8.java b/src/org/gnunet/construct/UInt8.java index 64e881e..58b8335 100644 --- a/src/org/gnunet/construct/UInt8.java +++ b/src/org/gnunet/construct/UInt8.java @@ -28,7 +28,7 @@ import java.lang.annotation.Target; /** - * Synonym for Integer(byteSize=1, signed=false) + * Unsigned 8-bit integer value. * * @author Florian Dold * diff --git a/src/org/gnunet/construct/Union.java b/src/org/gnunet/construct/Union.java index 9e319aa..d35512e 100644 --- a/src/org/gnunet/construct/Union.java +++ b/src/org/gnunet/construct/Union.java @@ -26,6 +26,9 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * A field that stores a union, whose cases are discriminated by the field specified with {@literal tag}. + */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Union { diff --git a/src/org/gnunet/construct/UnionCase.java b/src/org/gnunet/construct/UnionCase.java index 9171ba9..7622dd4 100644 --- a/src/org/gnunet/construct/UnionCase.java +++ b/src/org/gnunet/construct/UnionCase.java @@ -26,6 +26,12 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * Annotation for messages that are a case of a union, with a distinct value to discriminate the case. + * + * Classes annotated by {@literal UnionCase} should implement + * the marker interface for their respective union type. + */ @Retention(RetentionPolicy.SOURCE) @Target(ElementType.TYPE) public @interface UnionCase { diff --git a/src/org/gnunet/construct/parsers/ByteFillParser.java b/src/org/gnunet/construct/parsers/ByteFillParser.java deleted file mode 100644 index a581229..0000000 --- a/src/org/gnunet/construct/parsers/ByteFillParser.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2011, 2012 Christian Grothoff (and other contributing authors) - - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, or (at your - option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. - */ - -package org.gnunet.construct.parsers; - -import org.gnunet.construct.Message; -import org.gnunet.construct.ReflectUtil; -import org.gnunet.construct.ProtocolViolation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.lang.reflect.Array; -import java.lang.reflect.Field; -import java.nio.ByteBuffer; -import java.util.List; - -/** - * Parse a byte array that takes up all the remaining space in its frame. - * A 0-element array takes up no space. - * 'null' is an invalid value for a field of this type. - * - * @author Florian Dold - * - */ -public class ByteFillParser implements Parser { - private static final Logger logger = LoggerFactory - .getLogger(ByteFillParser.class); - - private final Field targetField; - - - public ByteFillParser(Field field) { - targetField = field; - } - - @Override - public int getSize(Message src) { - return Array.getLength(ReflectUtil.justGet(src, targetField)); - } - - - @Override - public int parse(ByteBuffer srcBuf, int frameOffset, Message frameObj, Message dst, List frameSizePath) { - int frameSize = ReflectUtil.justGetInt(frameObj, frameSizePath); - int remaining = frameOffset + frameSize - srcBuf.position(); - - if (remaining < 0) { - throw new ProtocolViolation("negative size remaining for variable size message"); - } - - if (remaining == 0) { - ReflectUtil.justSet(dst, targetField, new byte[0]); - return 0; - } - - byte[] a = new byte[remaining]; - - srcBuf.get(a); - - ReflectUtil.justSet(dst, targetField, a); - - return frameOffset; - } - - @Override - public int write(ByteBuffer dstBuf, Message src) { - byte[] a = (byte[]) ReflectUtil.justGet(src, targetField); - dstBuf.put(a); - return a.length; - } - - @Override - public void patch(Message message, int frameSize, List frameSizePath, Message frameObject) { - // the size field is contained in the frameObject - ReflectUtil.justSetInt(frameObject, frameSizePath, frameSize); - } - - @Override - public int getStaticSize() { - // not known! - return 0; - } -} diff --git a/src/org/gnunet/construct/parsers/FillParser.java b/src/org/gnunet/construct/parsers/FillParser.java index b8e4b43..b266424 100644 --- a/src/org/gnunet/construct/parsers/FillParser.java +++ b/src/org/gnunet/construct/parsers/FillParser.java @@ -62,10 +62,15 @@ public class FillParser implements Parser { public int parse(ByteBuffer srcBuf, int frameOffset, Message frameObj, final Message dstObj, List frameSizePath) { + + final int frameSize = ReflectUtil.justGetInt(dstObj, frameSizePath); int remaining = frameOffset + frameSize - srcBuf.position(); int size = 0; + Class elemType = targetField.getType().getComponentType(); + + ArrayList list = new ArrayList(10); while (remaining > 0) { @@ -74,10 +79,13 @@ public class FillParser implements Parser { int s = elemParser.parse(srcBuf, frameOffset, frameObj, next, null); size += s; remaining -= s; + list.add(next); } + Object arr = Array.newInstance(elemType, list.size()); + try { - targetField.set(dstObj, list.toArray()); + targetField.set(dstObj, list.toArray((Object[]) arr)); } catch (IllegalAccessException e) { throw new AssertionError("cannot access field"); } diff --git a/src/org/gnunet/construct/parsers/FixedSizeArrayParser.java b/src/org/gnunet/construct/parsers/FixedSizeArrayParser.java index 855a685..3dbc720 100644 --- a/src/org/gnunet/construct/parsers/FixedSizeArrayParser.java +++ b/src/org/gnunet/construct/parsers/FixedSizeArrayParser.java @@ -82,6 +82,9 @@ public class FixedSizeArrayParser implements Parser { final Message srcObj) { int size = 0; final Object arr = ReflectUtil.justGet(srcObj, targetField); + if (Array.getLength(arr) != elemNumber) { + throw new AssertionError("wrong number of elements"); + } for (int i = 0; i < Array.getLength(arr); ++i) { size += elemParser.write(dstBuf, (Message) Array.get(arr, i)); } diff --git a/src/org/gnunet/construct/parsers/FixedSizeByteArrayParser.java b/src/org/gnunet/construct/parsers/FixedSizeByteArrayParser.java deleted file mode 100644 index 5349f05..0000000 --- a/src/org/gnunet/construct/parsers/FixedSizeByteArrayParser.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2011, 2012 Christian Grothoff (and other contributing authors) - - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, or (at your - option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. - */ - -package org.gnunet.construct.parsers; - -import org.gnunet.construct.Message; -import org.gnunet.construct.ReflectUtil; - -import java.lang.reflect.Field; -import java.nio.ByteBuffer; -import java.util.List; - -public class FixedSizeByteArrayParser implements Parser { - private final int elemNumber; - private final Field targetField; - - public FixedSizeByteArrayParser(int elemNumber, Field f) { - this.elemNumber = elemNumber; - this.targetField = f; - } - - @Override - public int getSize(Message srcObj) { - return elemNumber; - } - - @Override - public int parse(ByteBuffer srcBuf, int frameStart, Message frameObj, Message dstObj, List frameSizePath) { - byte[] data = new byte[elemNumber]; - srcBuf.get(data); - ReflectUtil.justSet(dstObj, targetField, data); - return elemNumber; - } - - @Override - public int write(ByteBuffer dstBuf, Message srcObj) { - byte[] data = (byte[]) ReflectUtil.justGet(srcObj, targetField); - if (data.length != elemNumber) { - throw new AssertionError("fixed size array has wrong size"); - } - dstBuf.put(data); - return elemNumber; - } - - @Override - public void patch(Message m, int frameSize, List frameSizePath, Message frameObj) { - // nothing to do - } - - @Override - public int getStaticSize() { - return elemNumber; - } -} diff --git a/src/org/gnunet/construct/parsers/FixedSizeIntegerArrayParser.java b/src/org/gnunet/construct/parsers/FixedSizeIntegerArrayParser.java new file mode 100644 index 0000000..3605e97 --- /dev/null +++ b/src/org/gnunet/construct/parsers/FixedSizeIntegerArrayParser.java @@ -0,0 +1,99 @@ +/* + This file is part of GNUnet. + (C) 2011, 2012 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +package org.gnunet.construct.parsers; + +import org.gnunet.construct.Message; +import org.gnunet.construct.ReflectUtil; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.nio.ByteBuffer; +import java.util.List; + +public class FixedSizeIntegerArrayParser implements Parser { + + private final int byteSize; + private final boolean signed; + + private final Field targetField; + + private final int elemNumber; + + public FixedSizeIntegerArrayParser(final int elemNumber, boolean signed, int byteSize, final Field f) { + targetField = f; + this.elemNumber = elemNumber; + this.signed = signed; + this.byteSize = byteSize; + } + + @Override + public int getSize(final Message srcObj) { + return byteSize * elemNumber; + } + + @Override + public int parse(ByteBuffer srcBuf, int frameOffset, + Message frameObj, final Message dstObj, List frameSizePath) { + int size = 0; + + @SuppressWarnings("unchecked") + final Class arrayElementType = (Class) targetField.getType().getComponentType(); + + if (!arrayElementType.isPrimitive()) { + throw new AssertionError("IntegerFillParser is expected to be of primitive type, not " + arrayElementType); + } + + final Object arr = Array.newInstance(targetField.getType().getComponentType(), elemNumber); + ReflectUtil.justSet(dstObj, targetField, arr); + + for (int i = 0; i < elemNumber; ++i) { + long v = IntegerUtil.readLong(srcBuf, signed, byteSize); + ReflectUtil.justSetArray(arr, i, v); + } + + return size; + } + + @Override + public int write(final ByteBuffer dstBuf, + final Message srcObj) { + int size = 0; + final Object arr = ReflectUtil.justGet(srcObj, targetField); + if (Array.getLength(arr) != elemNumber) { + throw new AssertionError("wrong number of elements"); + } + for (int i = 0; i < Array.getLength(arr); ++i) { + IntegerUtil.writeLong(Array.getLong(arr, i), dstBuf, signed, byteSize); + size += byteSize; + } + return size; + } + + @Override + public void patch(Message m, int frameSize, List frameSizePath, Message frameObj) { + // nothing to patch! + } + + @Override + public int getStaticSize() { + return elemNumber * byteSize; + } +} diff --git a/src/org/gnunet/construct/parsers/IntegerFillParser.java b/src/org/gnunet/construct/parsers/IntegerFillParser.java index 1d485da..8c08627 100644 --- a/src/org/gnunet/construct/parsers/IntegerFillParser.java +++ b/src/org/gnunet/construct/parsers/IntegerFillParser.java @@ -58,9 +58,25 @@ public class IntegerFillParser implements Parser { final int frameSize = ReflectUtil.justGetInt(frameObj, frameSizePath); int remaining = frameStart + frameSize - srcBuf.position(); - throw new UnsupportedOperationException("not yet implemented"); + int elemNumber = remaining / byteSize; - // return getSize(dstObj); + @SuppressWarnings("unchecked") + final Class arrayElementType = (Class) targetField.getType().getComponentType(); + + if (!arrayElementType.isPrimitive()) { + throw new AssertionError("IntegerFillParser is expected to be of primitive type, not " + arrayElementType); + } + + final Object arr = Array.newInstance(arrayElementType, elemNumber); + ReflectUtil.justSet(dstObj, targetField, arr); + + + for (int i = 0; i < elemNumber; ++i) { + long v = IntegerUtil.readLong(srcBuf, signed, byteSize); + ReflectUtil.justSetArray(arr, i, v); + } + + return remaining; } @Override diff --git a/src/org/gnunet/construct/parsers/IntegerParser.java b/src/org/gnunet/construct/parsers/IntegerParser.java index 42646a7..4f71aa5 100644 --- a/src/org/gnunet/construct/parsers/IntegerParser.java +++ b/src/org/gnunet/construct/parsers/IntegerParser.java @@ -80,7 +80,12 @@ public class IntegerParser implements Parser { @Override public void patch(Message m, int frameSize, List frameSizePath, Message frameObj) { - // nothing to do + // todo: optimize this! + /* + if (frameSizePath != null) { + ReflectUtil.justSetInt(frameObj, frameSizePath, frameSize); + } + */ } @Override diff --git a/src/org/gnunet/construct/parsers/NestedParser.java b/src/org/gnunet/construct/parsers/NestedParser.java index d42a43c..d36fad6 100644 --- a/src/org/gnunet/construct/parsers/NestedParser.java +++ b/src/org/gnunet/construct/parsers/NestedParser.java @@ -21,8 +21,8 @@ package org.gnunet.construct.parsers; import org.gnunet.construct.Message; +import org.gnunet.construct.ProtocolViolationException; import org.gnunet.construct.ReflectUtil; -import org.gnunet.construct.ProtocolViolation; import java.lang.reflect.Field; import java.nio.ByteBuffer; @@ -62,11 +62,11 @@ public class NestedParser implements Parser { if (optional) { int remaining = frameOffset + ReflectUtil.justGetInt(frameObj, frameSizePath) - srcBuf.position(); if (remaining < 0) { - throw new ProtocolViolation("remaining size negative"); + throw new ProtocolViolationException("remaining size negative"); } if (remaining == 0) { if (!optional) { - throw new ProtocolViolation("not optional"); + throw new ProtocolViolationException("not optional"); } ReflectUtil.justSet(dstObj, targetField, null); return 0; @@ -89,7 +89,7 @@ public class NestedParser implements Parser { Message nestedMessage = (Message) ReflectUtil.justGet(m, targetField); if (newFrame) { - nestedParser.patch(nestedMessage, nestedParser.getSize(nestedMessage), null, m); + nestedParser.patch(nestedMessage, nestedParser.getSize(nestedMessage), null, nestedMessage); } else { nestedParser.patch(nestedMessage, frameSize, frameSizePath, frameObj); } diff --git a/src/org/gnunet/construct/parsers/SequenceParser.java b/src/org/gnunet/construct/parsers/SequenceParser.java index 7d35cc4..f472533 100644 --- a/src/org/gnunet/construct/parsers/SequenceParser.java +++ b/src/org/gnunet/construct/parsers/SequenceParser.java @@ -21,6 +21,7 @@ package org.gnunet.construct.parsers; import org.gnunet.construct.Message; +import org.gnunet.construct.ReflectUtil; import java.lang.reflect.Field; import java.nio.ByteBuffer; @@ -58,7 +59,6 @@ public class SequenceParser implements Parser { Message frameObj, final Message dst, List frameSizePath) { int size = 0; for (final Parser p : childParsers) { - List path; size += p.parse(srcBuf, frameOffset, frameObj, dst, frameSizePath == null ? myFrameSizePath : frameSizePath); } @@ -76,6 +76,11 @@ public class SequenceParser implements Parser { @Override public void patch(Message m, int frameSize, List frameSizePath, Message frameObj) { + // todo: this should be optimized / only be done by the topmost sequence parse => introduce a boolean parameter + if (myFrameSizePath != null) { + ReflectUtil.justSetInt(frameObj, myFrameSizePath, frameSize); + } + for (final Parser p : childParsers) { p.patch(m, frameSize, frameSizePath == null ? myFrameSizePath : frameSizePath, frameObj); } diff --git a/src/org/gnunet/construct/parsers/StringParser.java b/src/org/gnunet/construct/parsers/StringParser.java index a96d396..8df171e 100644 --- a/src/org/gnunet/construct/parsers/StringParser.java +++ b/src/org/gnunet/construct/parsers/StringParser.java @@ -21,7 +21,7 @@ package org.gnunet.construct.parsers; import org.gnunet.construct.Message; -import org.gnunet.construct.ProtocolViolation; +import org.gnunet.construct.ProtocolViolationException; import org.gnunet.construct.ReflectUtil; import java.io.UnsupportedEncodingException; @@ -63,12 +63,20 @@ public class StringParser implements Parser { public int parse(final ByteBuffer srcBuf, int frameOffset, Message frameObj, final Message dstObj, List frameSizePath) { - if (srcBuf.get(srcBuf.position()) == 0) { - if (!optional) { - throw new ProtocolViolation("no data received for non-optional string"); + if (optional) { + if (frameSizePath == null) { + throw new AssertionError("optional string with no length field in the message!"); + } + final int frameSize = ReflectUtil.justGetInt(dstObj, frameSizePath); + int remaining = frameOffset + frameSize - srcBuf.position(); + + if (remaining == 0) { + if (!optional) { + throw new ProtocolViolationException("no data received for non-optional string"); + } + ReflectUtil.justSet(dstObj, targetField, null); + return 0; } - ReflectUtil.justSet(dstObj, targetField, null); - return 1; } int length = 0; @@ -125,7 +133,9 @@ public class StringParser implements Parser { @Override public void patch(Message m, int frameSize, List frameSizePath, Message frameObj) { - // nothing to patch + if (frameSizePath != null) { + ReflectUtil.justSetInt(frameObj, frameSizePath, frameSize); + } } @Override diff --git a/src/org/gnunet/construct/parsers/UnionParser.java b/src/org/gnunet/construct/parsers/UnionParser.java index ba167cd..23d265a 100644 --- a/src/org/gnunet/construct/parsers/UnionParser.java +++ b/src/org/gnunet/construct/parsers/UnionParser.java @@ -21,7 +21,7 @@ package org.gnunet.construct.parsers; import org.gnunet.construct.*; -import org.gnunet.construct.ProtocolViolation; +import org.gnunet.construct.ProtocolViolationException; import java.lang.reflect.Field; import java.nio.ByteBuffer; @@ -66,7 +66,7 @@ public class UnionParser implements Parser { int remaining = frameOffset + ReflectUtil.justGetInt(frameObj, frameSizePath) - srcBuf.position(); if (remaining <= 0) { if (!optional) { - throw new ProtocolViolation("not optional"); + throw new ProtocolViolationException("not optional"); } ReflectUtil.justSet(dstObj, targetField, null); return 0; diff --git a/src/org/gnunet/construct/parsers/VariableSizeArrayParser.java b/src/org/gnunet/construct/parsers/VariableSizeArrayParser.java index a85aa0a..2e64f63 100644 --- a/src/org/gnunet/construct/parsers/VariableSizeArrayParser.java +++ b/src/org/gnunet/construct/parsers/VariableSizeArrayParser.java @@ -70,7 +70,7 @@ public class VariableSizeArrayParser implements Parser { for (int i = 0; i < elemNumber; ++i) { Message elemObj; - elemObj = (Message) ReflectUtil.justInstantiate(arrayElementType); + elemObj = ReflectUtil.justInstantiate(arrayElementType); Array.set(arr, i, elemObj); diff --git a/src/org/gnunet/construct/parsers/VariableSizeIntegerArrayParser.java b/src/org/gnunet/construct/parsers/VariableSizeIntegerArrayParser.java index e5d49bd..b3f134d 100644 --- a/src/org/gnunet/construct/parsers/VariableSizeIntegerArrayParser.java +++ b/src/org/gnunet/construct/parsers/VariableSizeIntegerArrayParser.java @@ -45,7 +45,6 @@ public class VariableSizeIntegerArrayParser implements Parser { @Override public int getSize(final Message src) { - int size = 0; final Object arr = ReflectUtil.justGet(src, targetField); if (arr == null) { diff --git a/src/org/gnunet/construct/parsers/package-info.java b/src/org/gnunet/construct/parsers/package-info.java new file mode 100644 index 0000000..5da3a94 --- /dev/null +++ b/src/org/gnunet/construct/parsers/package-info.java @@ -0,0 +1,25 @@ +/* + This file is part of GNUnet. + (C) 2011, 2012 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + + +/** + * The actual parsers for reading and writing annotated messages. + */ +package org.gnunet.construct.parsers; diff --git a/src/org/gnunet/core/ConnectNotifyMessage.java b/src/org/gnunet/core/ConnectNotifyMessage.java index 7d88472..4eafe02 100644 --- a/src/org/gnunet/core/ConnectNotifyMessage.java +++ b/src/org/gnunet/core/ConnectNotifyMessage.java @@ -20,10 +20,7 @@ package org.gnunet.core; -import org.gnunet.construct.ByteFill; -import org.gnunet.construct.NestedMessage; -import org.gnunet.construct.UInt32; -import org.gnunet.construct.UnionCase; +import org.gnunet.construct.*; import org.gnunet.util.GnunetMessage; import org.gnunet.util.PeerIdentity; @@ -47,7 +44,7 @@ public class ConnectNotifyMessage implements GnunetMessage.Body { public PeerIdentity peer; - @ByteFill + @FillWith @UInt8 public byte[] atsInfo; diff --git a/src/org/gnunet/core/NotifyInboundTrafficMessage.java b/src/org/gnunet/core/NotifyInboundTrafficMessage.java index 24df262..45647de 100644 --- a/src/org/gnunet/core/NotifyInboundTrafficMessage.java +++ b/src/org/gnunet/core/NotifyInboundTrafficMessage.java @@ -53,6 +53,6 @@ public class NotifyInboundTrafficMessage implements GnunetMessage.Body { * Not typed as GnunetMessage.Body because the message type may not be known by this * peer. */ - @ByteFill + @FillWith @UInt8 public byte[] payloadBody; } diff --git a/src/org/gnunet/core/NotifyOutboundTrafficMessage.java b/src/org/gnunet/core/NotifyOutboundTrafficMessage.java index f2080e6..900f8be 100644 --- a/src/org/gnunet/core/NotifyOutboundTrafficMessage.java +++ b/src/org/gnunet/core/NotifyOutboundTrafficMessage.java @@ -52,7 +52,7 @@ public class NotifyOutboundTrafficMessage implements GnunetMessage.Body { * Not typed as GnunetMessage.Body because the message type may not be known by this * peer. */ - @ByteFill + @FillWith @UInt8 public byte[] payloadBody; } diff --git a/src/org/gnunet/dht/ClientGetMessage.java b/src/org/gnunet/dht/ClientGetMessage.java index 40223eb..cd317fb 100644 --- a/src/org/gnunet/dht/ClientGetMessage.java +++ b/src/org/gnunet/dht/ClientGetMessage.java @@ -46,6 +46,6 @@ public class ClientGetMessage implements GnunetMessage.Body { public HashCode key; @UInt64 public long uniqueId; - @ByteFill + @FillWith @UInt8 public byte[] xquery; } diff --git a/src/org/gnunet/dht/ClientPutMessage.java b/src/org/gnunet/dht/ClientPutMessage.java index 398ebf6..4b63e92 100644 --- a/src/org/gnunet/dht/ClientPutMessage.java +++ b/src/org/gnunet/dht/ClientPutMessage.java @@ -49,6 +49,6 @@ public class ClientPutMessage implements GnunetMessage.Body { public AbsoluteTimeMessage expiration; @NestedMessage public HashCode hash; - @ByteFill + @FillWith @UInt8 public byte[] data; } diff --git a/src/org/gnunet/dht/ClientResultMessage.java b/src/org/gnunet/dht/ClientResultMessage.java index 7d3f50f..fab614f 100644 --- a/src/org/gnunet/dht/ClientResultMessage.java +++ b/src/org/gnunet/dht/ClientResultMessage.java @@ -51,6 +51,6 @@ public class ClientResultMessage implements GnunetMessage.Body { public PeerIdentity[] putPath; @VariableSizeArray(lengthField = "getPathLength") public PeerIdentity[] getPath; - @ByteFill + @FillWith @UInt8 public byte[] data; } diff --git a/src/org/gnunet/dht/DistributedHashTable.java b/src/org/gnunet/dht/DistributedHashTable.java index c868a08..e3d406b 100644 --- a/src/org/gnunet/dht/DistributedHashTable.java +++ b/src/org/gnunet/dht/DistributedHashTable.java @@ -23,8 +23,8 @@ package org.gnunet.dht; import org.gnunet.requests.Request; import org.gnunet.requests.RequestQueue; import org.gnunet.util.*; -import org.gnunet.util.getopt.Option; -import org.gnunet.util.getopt.OptionAction; +import org.gnunet.util.getopt.Argument; +import org.gnunet.util.getopt.ArgumentAction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -345,45 +345,45 @@ public class DistributedHashTable { public static void main(String[] args) { new Program(args) { - @Option(action = OptionAction.SET, + @Argument(action = ArgumentAction.SET, shortname = "p", longname = "put", description = "set a value in the DHT; default is get") boolean modePut = false; - @Option(action = OptionAction.SET, + @Argument(action = ArgumentAction.SET, shortname = "m", longname = "monitor", description = "monitor requests going to the local DHT") boolean monitor = false; - @Option(action = OptionAction.STORE_STRING, + @Argument(action = ArgumentAction.STORE_STRING, shortname = "d", longname = "data", description = "data (only used with --put)") String data = null; - @Option(action = OptionAction.STORE_STRING, + @Argument(action = ArgumentAction.STORE_STRING, shortname = "k", longname = "key", description = "key used for the operation") String key = null; - @Option(action = OptionAction.STORE_STRING, + @Argument(action = ArgumentAction.STORE_STRING, shortname = "t", longname = "type", description = "type of data used in this operation") String type = null; - @Option(action = OptionAction.STORE_STRING, + @Argument(action = ArgumentAction.STORE_STRING, shortname = "e", longname = "expire", description = "expiration (ony use with --put)") String expiration = null; - @Option(action = OptionAction.STORE_NUMBER, + @Argument(action = ArgumentAction.STORE_NUMBER, shortname = "r", longname = "replication", description = "desired replication (only used with --put)") diff --git a/src/org/gnunet/dht/MonitorGetRespMessage.java b/src/org/gnunet/dht/MonitorGetRespMessage.java index b388b6c..3bf145b 100644 --- a/src/org/gnunet/dht/MonitorGetRespMessage.java +++ b/src/org/gnunet/dht/MonitorGetRespMessage.java @@ -67,6 +67,6 @@ public class MonitorGetRespMessage implements GnunetMessage.Body { @VariableSizeArray(lengthField = "get_path_length") public PeerIdentity[] getPath; - @ByteFill + @FillWith @UInt8 public byte[] data; } diff --git a/src/org/gnunet/dht/MonitorPutMessage.java b/src/org/gnunet/dht/MonitorPutMessage.java index 7d7261f..103c05b 100644 --- a/src/org/gnunet/dht/MonitorPutMessage.java +++ b/src/org/gnunet/dht/MonitorPutMessage.java @@ -77,6 +77,6 @@ public class MonitorPutMessage implements GnunetMessage.Body { @VariableSizeArray(lengthField = "put_path_length") public PeerIdentity[] putPath; - @ByteFill + @FillWith @UInt8 public byte[] data; } diff --git a/src/org/gnunet/hello/HelloMessage.java b/src/org/gnunet/hello/HelloMessage.java new file mode 100644 index 0000000..0d90912 --- /dev/null +++ b/src/org/gnunet/hello/HelloMessage.java @@ -0,0 +1,50 @@ +/* + This file is part of GNUnet. + (C) 2011, 2012 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +package org.gnunet.hello; + +import org.gnunet.construct.*; +import org.gnunet.peerinfo.RsaPublicKeyBinaryEncoded; + +/** + * A HELLO message is used to exchange information about + * transports with other peers. This struct is always + * followed by the actual network addresses which have + * the format: + * + * 1) transport-name (0-terminated) + * 2) address-length (uint16_t, network byte order; possibly + * unaligned!) + * 3) address expiration (GNUNET_TIME_AbsoluteNBO); possibly + * unaligned!) + * 4) address (address-length bytes; possibly unaligned!) + * + * @author Florian Dold + */ +public class HelloMessage implements Message { + @UInt32 + public int reserved; + + @NestedMessage + public RsaPublicKeyBinaryEncoded publicKey; + + @FillWith @UInt8 + public byte[] addresses; +} diff --git a/src/org/gnunet/hello/package-info.java b/src/org/gnunet/hello/package-info.java new file mode 100644 index 0000000..78a5193 --- /dev/null +++ b/src/org/gnunet/hello/package-info.java @@ -0,0 +1,25 @@ +/* + This file is part of GNUnet. + (C) 2011, 2012 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + + +/** + * Management of hello-messages from peers. + */ +package org.gnunet.hello; diff --git a/src/org/gnunet/mesh/ClientConnectMessage.java b/src/org/gnunet/mesh/ClientConnectMessage.java index 3a09373..89332f9 100644 --- a/src/org/gnunet/mesh/ClientConnectMessage.java +++ b/src/org/gnunet/mesh/ClientConnectMessage.java @@ -1,9 +1,6 @@ package org.gnunet.mesh; -import org.gnunet.construct.FixedSizeArray; -import org.gnunet.construct.UInt16; -import org.gnunet.construct.UnionCase; -import org.gnunet.construct.VariableSizeArray; +import org.gnunet.construct.*; import org.gnunet.util.GnunetMessage; /** @@ -14,11 +11,11 @@ import org.gnunet.util.GnunetMessage; @UnionCase(272) public class ClientConnectMessage implements GnunetMessage.Body { @UInt16 - public int applications; + public int applications_length; @UInt16 - public int types; - @VariableSizeArray(lengthField = "applications") + public int types_length; + @VariableSizeIntegerArray(lengthField = "applications_length", signed = false, bitSize = 16) public int[] apps_list; - @VariableSizeArray(lengthField = "types") + @VariableSizeIntegerArray(lengthField = "types_length", signed = false, bitSize = 16) public int[] types_list; } diff --git a/src/org/gnunet/mesh/ConnectHandler.java b/src/org/gnunet/mesh/ConnectHandler.java new file mode 100644 index 0000000..9f9b5f8 --- /dev/null +++ b/src/org/gnunet/mesh/ConnectHandler.java @@ -0,0 +1,9 @@ +package org.gnunet.mesh; + +/** + * ... + * + * @author Florian Dold + */ +public class ConnectHandler { +} diff --git a/src/org/gnunet/mesh/DisconnectHandler.java b/src/org/gnunet/mesh/DisconnectHandler.java new file mode 100644 index 0000000..6cbf02a --- /dev/null +++ b/src/org/gnunet/mesh/DisconnectHandler.java @@ -0,0 +1,9 @@ +package org.gnunet.mesh; + +/** + * ... + * + * @author Florian Dold + */ +public interface DisconnectHandler { +} diff --git a/src/org/gnunet/mesh/Mesh.java b/src/org/gnunet/mesh/Mesh.java index d739b61..4b43c97 100644 --- a/src/org/gnunet/mesh/Mesh.java +++ b/src/org/gnunet/mesh/Mesh.java @@ -25,36 +25,122 @@ import org.gnunet.requests.RequestQueue; import org.gnunet.util.*; import org.grothoff.Runabout; -import java.util.List; - /** + * + * * @author Florian Dold */ public class Mesh { private RequestQueue requestQueue; private TunnelEndHandler tunnelEndHandler; private Runabout messageReceiver; - private List applications; + private int[] applications; private InboundTunnelHandler inboundTunnelHandler; - public static class RootTunnel { - int tunnel_id; + private int nextTunnelId = 0x80000000; + + + public class RootTunnel extends Tunnel { public void addPeer(PeerIdentity peerIdentity) { } + /** + * Request that the given peer isn't added to this tunnel in calls to + * connect_by_* calls, (due to misbehaviour, bad performance, ...). + * + * @param peerIdentity peer identity of the peer which should be blacklisted + * for the tunnel. + */ public void blacklist(PeerIdentity peerIdentity) { } - public void destroy() { - // ... + /** + * Request that the given peer isn't blacklisted anymore from this tunnel, + * and therefore can be added in future calls to connect*. + * The peer must have been previously blacklisted for this tunnel. + * + * @param peerIdentity peer identity of the peer which shouldn't be blacklisted + * for the tunnel anymore. + */ + public void unblacklist(PeerIdentity peerIdentity) { + + } + /** + * Request that the mesh should try to connect to a peer supporting the given + * message type. + * + * @param appType application type that must be supported by the peer + * (MESH should discover peer in proximity handling this type) + */ + public void requestConnectByType (int appType) { + + } + + /** + * Request that the mesh should try to connect to a peer matching the + * description given in the service string. + * + * @param description string describing the destination node requirements + */ + public void requestConnectByType (String description) { + + } + + /** + * Request that a peer should be added to the tunnel. The connect handler + * will be called when the peer connects + * + * @param peer peer to add + */ + public void requestConnectAdd (PeerIdentity peer) { + + } + + /** + * Request that a peer should be removed from the tunnel. The existing + * disconnect handler will be called ONCE if we were connected. + * + * @param peer peer to remove + */ + public void requestConnectDel (PeerIdentity peer) { + + } + + private void registerWithService() { + requestQueue.add(new TunnelCreateRequest(this)); } } - public static class Tunnel extends RootTunnel { - public Cancelable notifyTransmitReady(PeerIdentity target) { + public class Tunnel { + int tunnel_id; + + /** + * Ask the mesh to call "notify" once it is ready to transmit the + * given number of bytes to the specified tunnel or target. + * Only one call can be active at any time, to issue another request, + * wait for the callback or cancel the current request. + * + * @param maxdelay how long can the message wait? + * @param target destination for the message + * NULL for multicast to all tunnel targets + * @param notify_size how many bytes of buffer space does notify want? + * @param transmitter handler to call when buffer space is available; + * will be called with NULL on timeout or if the overall queue + * for this peer is larger than queue_size and this is currently + * the message with the lowest priority + * @return non-NULL if the notify callback was queued, + * NULL if we can not even queue the request (insufficient + * memory); if NULL is returned, "notify" will NOT be called. + */ + public Cancelable notifyTransmitReady(RelativeTime maxdelay, PeerIdentity target, int notify_size, MessageTransmitter transmitter) { return null; } + + public void destroy() { + // ... + } + } @@ -62,7 +148,28 @@ public class Mesh { @Override public void transmit(Connection.MessageSink sink) { - //To change body of implemented methods use File | Settings | File Templates. + ClientConnectMessage ccm = new ClientConnectMessage(); + ccm.applications_length = applications.length; + ccm.apps_list = applications; + int[] types = RunaboutUtil.getRunaboutMessageTypes(messageReceiver); + ccm.types_list = types; + ccm.types_length = types.length; + sink.send(ccm); + } + } + + public class TunnelCreateRequest extends Request { + public RootTunnel tunnel; + + public TunnelCreateRequest(RootTunnel rootTunnel) { + tunnel = rootTunnel; + } + + @Override + public void transmit(Connection.MessageSink sink) { + TunnelCreateMessage tcm = new TunnelCreateMessage(); + tcm.tunnel_id = tunnel.tunnel_id; + sink.send(tcm); } } @@ -76,7 +183,7 @@ public class Mesh { public Mesh(Configuration cfg, InboundTunnelHandler inboundTunnelHandler, - TunnelEndHandler tunnelEndHandler, Runabout messageReceiver, List applications) { + TunnelEndHandler tunnelEndHandler, Runabout messageReceiver, int... applications) { this.tunnelEndHandler = tunnelEndHandler; this.messageReceiver = messageReceiver; this.applications = applications; @@ -86,17 +193,38 @@ public class Mesh { requestQueue = new RequestQueue(client, new MeshMessageReceiver()); requestQueue.add(new ClientConnectRequest()); - } /** * Create a new tunnel (we're initiator and will be allowed to add/remove peers * and to broadcast). + * + * @param connectHandler function to call when peers are actually connected + * @param disconnectHandler function to call when peers are disconnected */ - public RootTunnel createTunnel(/* ... */) { - return null; + public RootTunnel createTunnel(ConnectHandler connectHandler, DisconnectHandler disconnectHandler) { + RootTunnel tunnel = new RootTunnel(); + tunnel.tunnel_id = nextTunnelId++; + tunnel.registerWithService(); + return tunnel; } + + /** + * Announce to ther peer the availability of services described by the regex, + * in order to be reachable to other peers via connect_by_string. + * + * Note that the first 8 characters are considered to be part of a prefix, + * (for instance 'gnunet://'). If you put a variable part in there (*, +. ()), + * all matching strings will be stored in the DHT. + * + * @param regex string with the regular expression describing local services. + */ + public void announceRegex(String regex) { + + } + + /** * Disconnect from the mesh service. All tunnels will be destroyed. All tunnel * disconnect callbacks will be called on any still connected peers, notifying @@ -104,6 +232,6 @@ public class Mesh { * called should any inbound tunnels still exist. */ public void disconnect() { - + requestQueue.destroy(); } } diff --git a/src/org/gnunet/mesh/TunnelCreateMessage.java b/src/org/gnunet/mesh/TunnelCreateMessage.java index 96d821c..7b63bc6 100644 --- a/src/org/gnunet/mesh/TunnelCreateMessage.java +++ b/src/org/gnunet/mesh/TunnelCreateMessage.java @@ -1,11 +1,17 @@ package org.gnunet.mesh; +import org.gnunet.construct.NestedMessage; import org.gnunet.construct.UInt32; import org.gnunet.construct.UnionCase; import org.gnunet.util.GnunetMessage; +import org.gnunet.util.PeerIdentity; /** - * ... + * Message used to + * a) request the service to create a new tunnel with the given tunnel id + * b) notify the client of a newly created tunnel + * + * todo: this is bad design, split into two messages in the C code! * * @author Florian Dold */ @@ -13,4 +19,10 @@ import org.gnunet.util.GnunetMessage; public class TunnelCreateMessage implements GnunetMessage.Body { @UInt32 public int tunnel_id; + + /** + * Only present if sent from server to client (purpose b) + */ + @NestedMessage(optional = true) + public PeerIdentity otherEnd; } diff --git a/src/org/gnunet/mesh/package-info.java b/src/org/gnunet/mesh/package-info.java new file mode 100644 index 0000000..fb5a8a4 --- /dev/null +++ b/src/org/gnunet/mesh/package-info.java @@ -0,0 +1,25 @@ +/* + This file is part of GNUnet. + (C) 2011, 2012 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + + +/** + * Create tunnels for packet-based communication to distant peers. + */ +package org.gnunet.mesh; diff --git a/src/org/gnunet/nse/NetworkSizeEstimation.java b/src/org/gnunet/nse/NetworkSizeEstimation.java index db4c8f0..284d144 100644 --- a/src/org/gnunet/nse/NetworkSizeEstimation.java +++ b/src/org/gnunet/nse/NetworkSizeEstimation.java @@ -23,10 +23,10 @@ package org.gnunet.nse; import org.gnunet.construct.Double; import org.gnunet.construct.*; -import org.gnunet.construct.ProtocolViolation; +import org.gnunet.construct.ProtocolViolationException; import org.gnunet.util.*; -import org.gnunet.util.getopt.Option; -import org.gnunet.util.getopt.OptionAction; +import org.gnunet.util.getopt.Argument; +import org.gnunet.util.getopt.ArgumentAction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -72,7 +72,7 @@ public class NetworkSizeEstimation { @Override public void process(GnunetMessage.Body msg) { if (!(msg instanceof UpdateMessage)) { - throw new ProtocolViolation("got unexcpected message"); + throw new ProtocolViolationException("got unexcpected message"); } UpdateMessage uMsg = (UpdateMessage) msg; @@ -169,7 +169,7 @@ public class NetworkSizeEstimation { public static void main(String[] args) { new Program(args) { - @Option(action = OptionAction.SET, + @Argument(action = ArgumentAction.SET, shortname = "w", longname = "continuous", description = "don't exit after the first estimation response") diff --git a/src/org/gnunet/peerinfo/InfoMessage.java b/src/org/gnunet/peerinfo/InfoMessage.java index 72cafac..d8d7daa 100644 --- a/src/org/gnunet/peerinfo/InfoMessage.java +++ b/src/org/gnunet/peerinfo/InfoMessage.java @@ -23,7 +23,7 @@ package org.gnunet.peerinfo; import org.gnunet.construct.NestedMessage; import org.gnunet.construct.UInt32; import org.gnunet.construct.UnionCase; -import org.gnunet.util.HelloMessage; +import org.gnunet.hello.HelloMessage; import org.gnunet.util.GnunetMessage; import org.gnunet.util.PeerIdentity; diff --git a/src/org/gnunet/peerinfo/PeerInfo.java b/src/org/gnunet/peerinfo/PeerInfo.java index 29510e4..2cc04d6 100644 --- a/src/org/gnunet/peerinfo/PeerInfo.java +++ b/src/org/gnunet/peerinfo/PeerInfo.java @@ -21,7 +21,7 @@ package org.gnunet.peerinfo; -import org.gnunet.util.HelloMessage; +import org.gnunet.hello.HelloMessage; import org.gnunet.requests.Request; import org.gnunet.requests.RequestQueue; import org.gnunet.util.*; diff --git a/src/org/gnunet/peerinfo/PeerProcessor.java b/src/org/gnunet/peerinfo/PeerProcessor.java index c981afd..b096c37 100644 --- a/src/org/gnunet/peerinfo/PeerProcessor.java +++ b/src/org/gnunet/peerinfo/PeerProcessor.java @@ -1,6 +1,6 @@ package org.gnunet.peerinfo; -import org.gnunet.util.HelloMessage; +import org.gnunet.hello.HelloMessage; import org.gnunet.util.PeerIdentity; /** diff --git a/src/org/gnunet/peerinfo/RsaPublicKeyBinaryEncoded.java b/src/org/gnunet/peerinfo/RsaPublicKeyBinaryEncoded.java index 0fc86f7..86985f3 100644 --- a/src/org/gnunet/peerinfo/RsaPublicKeyBinaryEncoded.java +++ b/src/org/gnunet/peerinfo/RsaPublicKeyBinaryEncoded.java @@ -20,7 +20,7 @@ package org.gnunet.peerinfo; -import org.gnunet.construct.FixedSizeByteArray; +import org.gnunet.construct.FixedSizeIntegerArray; import org.gnunet.construct.Message; import org.gnunet.construct.UInt16; import org.gnunet.construct.UInt8; @@ -45,7 +45,7 @@ public class RsaPublicKeyBinaryEncoded implements Message { /** * The key itself, contains n followed by e. */ - @FixedSizeByteArray(length = RsaPublicKeyBinaryEncoded.GNUNET_CRYPTO_RSA_KEY_LENGTH) + @FixedSizeIntegerArray(length = RsaPublicKeyBinaryEncoded.GNUNET_CRYPTO_RSA_KEY_LENGTH, signed = false, bitSize = 8) public byte[] key; /** diff --git a/src/org/gnunet/peerinfo/package-info.java b/src/org/gnunet/peerinfo/package-info.java new file mode 100644 index 0000000..19cebdb --- /dev/null +++ b/src/org/gnunet/peerinfo/package-info.java @@ -0,0 +1,25 @@ +/* + This file is part of GNUnet. + (C) 2011, 2012 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + + +/** + * Access to information about known hosts. + */ +package org.gnunet.peerinfo; diff --git a/src/org/gnunet/requests/Request.java b/src/org/gnunet/requests/Request.java index a7b9ea9..ef36471 100644 --- a/src/org/gnunet/requests/Request.java +++ b/src/org/gnunet/requests/Request.java @@ -15,7 +15,7 @@ public abstract class Request { /** * Called whenever the request could not be transmitted due to timeout. * - * @return + * @return true if the request should be queued again */ public boolean onTransmitTimeout() { // per default, just drop the message on timeout! @@ -55,7 +55,7 @@ public abstract class Request { * Called to determine after how long the request should time out. * Per default, the deadline is FOREVER. * - * @return + * @return the deadline for this request */ public AbsoluteTime getDeadline() { return deadline; diff --git a/src/org/gnunet/requests/package-info.java b/src/org/gnunet/requests/package-info.java new file mode 100644 index 0000000..892a606 --- /dev/null +++ b/src/org/gnunet/requests/package-info.java @@ -0,0 +1,25 @@ +/* + This file is part of GNUnet. + (C) 2011, 2012 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + + +/** + * General mechanism for queueing requests to a service. + */ +package org.gnunet.requests; diff --git a/src/org/gnunet/statistics/Statistics.java b/src/org/gnunet/statistics/Statistics.java index 89b91b5..2ef9fa0 100644 --- a/src/org/gnunet/statistics/Statistics.java +++ b/src/org/gnunet/statistics/Statistics.java @@ -29,8 +29,8 @@ package org.gnunet.statistics; import org.gnunet.requests.Request; import org.gnunet.requests.RequestQueue; import org.gnunet.util.*; -import org.gnunet.util.getopt.Option; -import org.gnunet.util.getopt.OptionAction; +import org.gnunet.util.getopt.Argument; +import org.gnunet.util.getopt.ArgumentAction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -190,7 +190,7 @@ public class Statistics { } public void transmit(Connection.MessageSink sink) { - sink.send(new TESTMessage()); + sink.send(new TestMessage()); // todo: disconnect when not receiving the TEST message back after timeout } } @@ -208,7 +208,7 @@ public class Statistics { } } - public void visit(TESTMessage m) { + public void visit(TestMessage m) { client.disconnect(); } @@ -331,28 +331,28 @@ public class Statistics { */ public static void main(String[] args) { new Program(args) { - @Option( + @Argument( shortname = "x", longname = "set", - action = OptionAction.SET, + action = ArgumentAction.SET, description = "set a value") boolean test; - @Option( + @Argument( shortname = "w", longname = "watch", - action = OptionAction.SET, + action = ArgumentAction.SET, description = "set a value") boolean watch; - @Option( + @Argument( shortname = "n", longname = "name", - action = OptionAction.STORE_STRING, + action = ArgumentAction.STORE_STRING, description = "statistics name") String statisticsName = ""; - @Option( + @Argument( shortname = "s", longname = "subsystem", - action = OptionAction.STORE_STRING, + action = ArgumentAction.STORE_STRING, description = "subsystem name") String subsystemName = ""; diff --git a/src/org/gnunet/testing/TestingServer.java b/src/org/gnunet/testing/TestingServer.java index 84ccd93..fe57884 100644 --- a/src/org/gnunet/testing/TestingServer.java +++ b/src/org/gnunet/testing/TestingServer.java @@ -20,7 +20,7 @@ public class TestingServer { public final Server server; private final ServerSocketChannel srvChan; - TestingServer() { + public TestingServer() { this(RelativeTime.FOREVER, true); } diff --git a/src/org/gnunet/testing/TestingSetup.java b/src/org/gnunet/testing/TestingSetup.java index ebb3dbc..6f8d578 100644 --- a/src/org/gnunet/testing/TestingSetup.java +++ b/src/org/gnunet/testing/TestingSetup.java @@ -29,44 +29,23 @@ import org.gnunet.util.RelativeTime; * * @author Florian Dold */ -public class TestingSetup { +public final class TestingSetup { + + private TestingSetup() { + + } public static class SetupException extends RuntimeException { - public SetupException(String msg, Throwable cause) { - super(msg, cause); + public SetupException(Exception e) { + super(e); } - public SetupException(String msg) { super(msg); } - public SetupException(Throwable cause) { - super(cause); - } - } - - /** - * Start a subsystem for testing. Note that subsystems started with this function - * are independent of each other. - * - * After the JVM exits, the subsystem will be shut down. - * - * @param name name of the Subsystem - * @return - */ - public TestingSubsystem startSubsystem(String name) { - return new TestingSubsystem(name); - } - - public TestingServer createServer() { - return new TestingServer(); - } - - public TestingServer createServer(RelativeTime idleTimeout, boolean requireFound) { - return new TestingServer(idleTimeout, requireFound); } - public TestingSetup() { + public static void setup() { String log = System.getenv("GNJ_LOGLEVEL"); if (log != null) { Program.configureLogging(log, null); diff --git a/src/org/gnunet/testing/TestingSubsystem.java b/src/org/gnunet/testing/TestingSubsystem.java index f38ac43..b96277d 100644 --- a/src/org/gnunet/testing/TestingSubsystem.java +++ b/src/org/gnunet/testing/TestingSubsystem.java @@ -30,7 +30,9 @@ import java.io.InputStreamReader; import java.io.OutputStreamWriter; /** -* @author Florian Dold + * Handle to a GNUnet subsystem that has been started for testing purposes. + * + * @author Florian Dold */ public class TestingSubsystem { private static final Logger logger = LoggerFactory @@ -46,7 +48,7 @@ public class TestingSubsystem { return cfg; } - TestingSubsystem(String service) { + public TestingSubsystem(String service) { try { p = Runtime.getRuntime().exec(new String[]{"gnunet-testing-run-service", "-s", service}); } catch (IOException e) { diff --git a/src/org/gnunet/testing/package-info.java b/src/org/gnunet/testing/package-info.java new file mode 100644 index 0000000..1a91586 --- /dev/null +++ b/src/org/gnunet/testing/package-info.java @@ -0,0 +1,27 @@ +/* + This file is part of GNUnet. + (C) 2011, 2012 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + + +/** + * Various utilities for testing. + * + * This is in the main source code location, as the testing package itself has unit tests. + */ +package org.gnunet.testing; diff --git a/src/org/gnunet/transport/Transport.java b/src/org/gnunet/transport/Transport.java index 50b5c4f..fdb54fb 100644 --- a/src/org/gnunet/transport/Transport.java +++ b/src/org/gnunet/transport/Transport.java @@ -26,7 +26,7 @@ import org.gnunet.util.PeerIdentity; import org.gnunet.util.RelativeTime; /** - * low-level peer-to-peer I/O API + * A connection to the transport service. * * @author Florian Dold */ diff --git a/src/org/gnunet/transport/package-info.java b/src/org/gnunet/transport/package-info.java new file mode 100644 index 0000000..6b52f7a --- /dev/null +++ b/src/org/gnunet/transport/package-info.java @@ -0,0 +1,25 @@ +/* + This file is part of GNUnet. + (C) 2011, 2012 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + + +/** + * Low-level peer-to-peer I/O API. + */ +package org.gnunet.transport; diff --git a/src/org/gnunet/util/AbsoluteTime.java b/src/org/gnunet/util/AbsoluteTime.java index 2856fb2..d9df13a 100644 --- a/src/org/gnunet/util/AbsoluteTime.java +++ b/src/org/gnunet/util/AbsoluteTime.java @@ -32,6 +32,9 @@ public class AbsoluteTime implements Comparable { private static final Logger logger = LoggerFactory .getLogger(AbsoluteTime.class); + /** + * Constant for 'the beginning of time' in our frame. + */ public final static AbsoluteTime ZERO = new AbsoluteTime(0); public final static AbsoluteTime FOREVER = new AbsoluteTime(Long.MAX_VALUE); @@ -95,16 +98,25 @@ public class AbsoluteTime implements Comparable { } + /** + * {@inheritDoc} + */ @Override public boolean equals(Object other) { return other instanceof AbsoluteTime && compareTo((AbsoluteTime) other) == 0; } + /** + * {@inheritDoc} + */ @Override public int hashCode() { return (int) this.abs_value; } + /** + * {@inheritDoc} + */ @Override public int compareTo(AbsoluteTime other) { if (this.abs_value < other.abs_value) { @@ -115,7 +127,10 @@ public class AbsoluteTime implements Comparable { } return 0; } - + + /** + * {@inheritDoc} + */ @Override public String toString() { if (this.isForever()) { @@ -132,8 +147,12 @@ public class AbsoluteTime implements Comparable { public boolean isDue() { return this.abs_value < now().abs_value; } - - + + /** + * Does this AbsoluteTime value represent forever? + * + * @return this==FOREVER + */ public boolean isForever() { return this.abs_value == Long.MAX_VALUE; } @@ -220,10 +239,22 @@ public class AbsoluteTime implements Comparable { return new AbsoluteTime(abs_value - duration.getMilliseconds()); } + /** + * Get a serializable message corresponding to this AbsoluteTime. + * + * @return a serializable message corresponding to this AbsoluteTime + */ public AbsoluteTimeMessage asMessage() { return new AbsoluteTimeMessage(this); } + /** + * Get the AbsoluteTime from a AbsoluteTimeMessage. + * + * @param m serializable representation of an AbsoluteTime + * + * @return the real AbsoluteTime associated with m + */ public static AbsoluteTime fromNetwork(AbsoluteTimeMessage m) { return m.value__ < 0 ? AbsoluteTime.FOREVER : new AbsoluteTime(m.value__); } diff --git a/src/org/gnunet/util/Client.java b/src/org/gnunet/util/Client.java index d2e1308..fbd94a6 100644 --- a/src/org/gnunet/util/Client.java +++ b/src/org/gnunet/util/Client.java @@ -22,6 +22,7 @@ package org.gnunet.util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + /** * A connection to a gnunet service. * @@ -68,6 +69,12 @@ public class Client { */ private boolean notifyTransmitReadyDelayed; + /** + * When notifyTransmitReadyDelayed is true, this can be used to cancel the task + * waiting for the connection to be established. + */ + private Cancelable delayedNotifyTransmitHandle; + /** * Create a connection to a service. * @@ -111,16 +118,6 @@ public class Client { return connection.receive(timeout, receiver); } - - private static class DelayedTransmitHandle implements Cancelable { - Cancelable realTransmitHandle; - Cancelable timeoutHandle; - @Override - public void cancel() { - throw new UnsupportedOperationException(); - } - } - /** * Ask the client to call us once it is able to send a message. * @@ -132,39 +129,103 @@ public class Client { * if the caller does not care about temporary connection errors, * for example because the protocol is stateless * @param size size of the message we want to transmit, can be an upper bound - *@param transmitter the MessageTransmitter object to call once the client is ready to transmit or + * @param transmitter the MessageTransmitter object to call once the client is ready to transmit or * when the timeout is over. Guaranteed to be called *after* notifyTransmitReady has returned. @return a handle that can be used to cancel the transmit request + * + * @return a handle to cancel the notification */ public Cancelable notifyTransmitReady(final RelativeTime timeout, final boolean autoRetry, int size, final MessageTransmitter transmitter) { + if (notifyTransmitReadyDelayed) { + throw new AssertionError("notifyTransmitReady called twice!"); + } if (connection.isConnected()) { return connection.notifyTransmitReady(0, timeout, transmitter); } else { notifyTransmitReadyDelayed = true; - final DelayedTransmitHandle delayedTransmitHandle = new DelayedTransmitHandle(); - delayedTransmitHandle.timeoutHandle = Scheduler.addDelayed(connectBackoff, new Scheduler.Task() { + final AbsoluteTime deadline = timeout.toAbsolute(); + delayedNotifyTransmitHandle = connection.notifyConnected(connectBackoff, new Continuation() { @Override - public void run(Scheduler.RunContext ctx) { - if (connection == null) { - return; - } - if (connection.isConnected()) { + public void cont(boolean success) { + delayedNotifyTransmitHandle = null; + if (success) { notifyTransmitReadyDelayed = false; - connection.notifyTransmitReady(0, timeout, transmitter); + delayedNotifyTransmitHandle = connection.notifyTransmitReady(0, timeout, new MessageTransmitter() { + @Override + public void transmit(Connection.MessageSink sink) { + delayedNotifyTransmitHandle = null; + transmitter.transmit(sink); + } + + @Override + public void handleError() { + delayedNotifyTransmitHandle = null; + transmitter.handleError(); + } + }); } else { - logger.debug("still not connected, retrying in {}ms", connectBackoff.getMilliseconds()); - reconnect(); - connectBackoff = RelativeTime.min(connectBackoff.multiply(2), MAX_BACKOFF); - Scheduler.addDelayed(connectBackoff, this); + if (deadline.isDue()) { + transmitter.handleError(); + } else { + RelativeTime timeout = deadline.getRemaining(); + connectBackoff = RelativeTime.min(timeout, RelativeTime.min(connectBackoff.multiply(2), MAX_BACKOFF)); + reconnect(); + delayedNotifyTransmitHandle = connection.notifyConnected(connectBackoff, this); + } } } }); - return delayedTransmitHandle; + return new Cancelable() { + @Override + public void cancel() { + if (delayedNotifyTransmitHandle != null) { + delayedNotifyTransmitHandle.cancel(); + } + } + }; } } + /** + * Convenience method for sending messages. + * + * @param timeout when should we give up sending the message, and call cont.cont(false) + * @param message the message to send + * @param cont called when the message has been sent successfully or on error + * @return a handle to cancel sending the message + */ + public Cancelable transmitWhenReady(final RelativeTime timeout, final GnunetMessage.Body message, final Continuation cont) { + return notifyTransmitReady(timeout, false, 0, new MessageTransmitter() { + @Override + public void transmit(Connection.MessageSink sink) { + sink.send(message); + if (cont != null) { + cont.cont(true); + } + } + + @Override + public void handleError() { + if (cont != null) { + cont.cont(false); + } + } + }); + } + + /** + * Convenience method for sending messages. Timeout defaults to FOREVER. + * + * @param message the message to send + * @param cont called when the message has been sent successfully or on error + * @return a handle to cancel sending the message + */ + public Cancelable transmitWhenReady(final GnunetMessage.Body message, final Continuation cont) { + return transmitWhenReady(RelativeTime.FOREVER, message, cont); + } + - public void reconnect() { + public final void reconnect() { if (connection != null) { connection.disconnect(); } diff --git a/src/org/gnunet/util/Configuration.java b/src/org/gnunet/util/Configuration.java index bd2c545..001477a 100644 --- a/src/org/gnunet/util/Configuration.java +++ b/src/org/gnunet/util/Configuration.java @@ -348,7 +348,11 @@ public class Configuration { } for (File dir : dirs) { if (dir.exists() && dir.isDirectory()) { - for (File f : dir.listFiles()) { + File[] files = dir.listFiles(); + if (files == null) { + continue; + } + for (File f : files) { parse(f.getAbsolutePath()); } } diff --git a/src/org/gnunet/util/Connection.java b/src/org/gnunet/util/Connection.java index 85d3c9b..fe7393d 100644 --- a/src/org/gnunet/util/Connection.java +++ b/src/org/gnunet/util/Connection.java @@ -22,7 +22,7 @@ package org.gnunet.util; import org.gnunet.construct.Construct; import org.gnunet.construct.MessageLoader; -import org.gnunet.construct.ProtocolViolation; +import org.gnunet.construct.ProtocolViolationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,7 +33,6 @@ import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.nio.channels.spi.SelectorProvider; -import java.util.Deque; import java.util.LinkedList; /** @@ -95,6 +94,16 @@ public class Connection { private ByteBuffer transmitBuffer = ByteBuffer.allocate(GnunetMessage.Header.SIZE); private boolean disconnected = false; + /** + * Timeout task for the connect notify. + */ + private Scheduler.TaskConfiguration notifyConnectedTimeout; + + /** + * Continuation to call when connected + */ + private Continuation notifyConnectedContinuation; + private class AddressProbe { Cancelable connectTask; @@ -150,7 +159,7 @@ public class Connection { try { MessageLoader.getUnionClass(GnunetMessage.Body.class, msgh.messageType); - } catch (ProtocolViolation e) { + } catch (ProtocolViolationException e) { found = false; } @@ -187,7 +196,7 @@ public class Connection { return; } } - logger.debug(String.format("read %s bytes", n)); + logger.debug(String.format("read %s bytes from %s", n, connectionChannel.socket().toString())); } catch (IOException e) { logger.error("read failed:", e); try { @@ -263,10 +272,6 @@ public class Connection { notifyTimeoutTask = tc.schedule(); } - public boolean notifyDone() { - return notifyTimeoutTask == null; - } - public void cancel() { if (transmitTask != null) { transmitTask.cancel(); @@ -283,7 +288,7 @@ public class Connection { this.transmitTask = null; try { int n = connectionChannel.write(transmitBuffer); - logger.debug("connectionChannel has written " + n + " bytes"); + logger.debug("connectionChannel has written " + n + " bytes to " + connectionChannel.socket().toString()); } catch (IOException e) { throw new IOError(e); } @@ -372,7 +377,6 @@ public class Connection { class ConnectionResolveHandler implements Resolver.AddressCallback { private final int port; - private Deque addressList; public ConnectionResolveHandler(int port) { this.port = port; @@ -425,7 +429,7 @@ public class Connection { } private void finishConnect(SocketChannel channel) { - boolean connected = false; + boolean connected; try { connected = channel.finishConnect(); connectionChannel = channel; @@ -439,6 +443,7 @@ public class Connection { logger.debug("finishConnect() was not successful: {}", (Object) e); return; } + if (connected) { if (currentTransmitHelper != null) { currentTransmitHelper.start(); @@ -446,6 +451,16 @@ public class Connection { if (currentReceiveHelper != null && !currentReceiveHelper.working) { currentReceiveHelper.schedule(); } + Continuation c = notifyConnectedContinuation; + notifyConnectedContinuation = null; + if (notifyConnectedTimeout != null) { + notifyConnectedTimeout.cancel(); + notifyConnectedTimeout = null; + } + if (c != null) { + c.cont(true); + } + } else { logger.error("socket reported OP_CONNECT but is not connected"); } @@ -477,10 +492,10 @@ public class Connection { /** * Receive one message from the network. * - * @param timeout deadline after which MessageReceiver.onError will be called + * @param timeout deadline after which receiver.onError() will be called * @param receiver MessageReceiver that is responsible for the received message */ - public ReceiveHandle receive(RelativeTime timeout, MessageReceiver receiver) { + public ReceiveHandle receive(RelativeTime timeout, final MessageReceiver receiver) { if (currentReceiveHelper != null) { throw new AssertionError("receive must not be called while receiving"); } @@ -494,6 +509,8 @@ public class Connection { final ReceiveHelper rh = new ReceiveHelper(receiver, timeout); currentReceiveHelper = rh; + // we can only schedule the receive helper if we are sure the connection is made, otherwise + // select will misbehave! if (connectionChannel.isConnected()) { currentReceiveHelper.schedule(); } @@ -507,7 +524,7 @@ public class Connection { } /** - * Call notify once the we are ready to transmit data. + * Call the transmitter once the we are ready to transmit data. * * @param size number of bytes to send * @param timeout after how long should we give up (and call transmitter.transmit(null)) @@ -550,6 +567,33 @@ public class Connection { }; } + + /** + * Call cont after establishing the connection or when the timeout has occured. + * + * @param timeout timeout + * @param cont continuation to call + * @return + */ + /* package-protected */ Cancelable notifyConnected(RelativeTime timeout, final Continuation cont) { + if (notifyConnectedTimeout != null) { + throw new AssertionError(); + } + this.notifyConnectedContinuation = cont; + this.notifyConnectedTimeout = Scheduler.addDelayed(timeout, new Scheduler.Task() { + @Override + public void run(Scheduler.RunContext ctx) { + Continuation c = notifyConnectedContinuation; + notifyConnectedContinuation = null; + Connection.this.notifyConnectedTimeout = null; + if (c != null) { + c.cont(false); + } + } + }); + return this.notifyConnectedTimeout; + } + /** * Disconnect. There must not be any pending transmit/receive requests. * Any buffered data scheduled for writing is discarded. @@ -567,13 +611,15 @@ public class Connection { } if (nextTransmitHelper != null) { - logger.error("disconnect called, but there is a notifyTransmitReady pending"); + // todo: do we want the error/warning logging or not? + //logger.error("disconnect called, but there is a notifyTransmitReady pending"); nextTransmitHelper.cancel(); nextTransmitHelper = null; } if (currentReceiveHelper != null) { - logger.error("disconnect called, but there is a receive pending"); + // todo: same here, want or not? + // logger.error("disconnect called, but there is a receive pending"); currentReceiveHelper.cancel(); currentReceiveHelper = null; } diff --git a/src/org/gnunet/util/Continuation.java b/src/org/gnunet/util/Continuation.java index d626c9e..e1027c0 100644 --- a/src/org/gnunet/util/Continuation.java +++ b/src/org/gnunet/util/Continuation.java @@ -21,5 +21,5 @@ package org.gnunet.util; public interface Continuation { - public void cont(boolean success); + void cont(boolean success); } diff --git a/src/org/gnunet/util/HashCode.java b/src/org/gnunet/util/HashCode.java index 02db150..3c3996e 100644 --- a/src/org/gnunet/util/HashCode.java +++ b/src/org/gnunet/util/HashCode.java @@ -21,11 +21,12 @@ package org.gnunet.util; -import org.gnunet.construct.FixedSizeByteArray; +import org.gnunet.construct.FixedSizeIntegerArray; import org.gnunet.construct.Message; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.Arrays; /** @@ -33,7 +34,7 @@ import java.security.NoSuchAlgorithmException; */ public class HashCode implements Message { - @FixedSizeByteArray(length = 64) + @FixedSizeIntegerArray(length = 64, signed = false, bitSize = 8) public byte[] data; // should be immutable, final, can't be due to construct @@ -45,7 +46,7 @@ public class HashCode implements Message { if (hash.length != 64) { throw new AssertionError("HashCode has to have length 64"); } - data = hash; + data = Arrays.copyOf(hash, hash.length); } /** diff --git a/src/org/gnunet/util/HelloMessage.java b/src/org/gnunet/util/HelloMessage.java deleted file mode 100644 index aec6637..0000000 --- a/src/org/gnunet/util/HelloMessage.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2011, 2012 Christian Grothoff (and other contributing authors) - - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, or (at your - option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. - */ - -package org.gnunet.util; - -import org.gnunet.construct.*; -import org.gnunet.peerinfo.RsaPublicKeyBinaryEncoded; - -/** - * A HELLO message is used to exchange information about - * transports with other peers. This struct is always - * followed by the actual network addresses which have - * the format: - * - * 1) transport-name (0-terminated) - * 2) address-length (uint16_t, network byte order; possibly - * unaligned!) - * 3) address expiration (GNUNET_TIME_AbsoluteNBO); possibly - * unaligned!) - * 4) address (address-length bytes; possibly unaligned!) - * - * @author Florian Dold - */ -public class HelloMessage implements Message { - @UInt32 - public int reserved; - - @NestedMessage - public RsaPublicKeyBinaryEncoded publicKey; - - @ByteFill - public byte[] addresses; -} diff --git a/src/org/gnunet/util/PeerIdentity.java b/src/org/gnunet/util/PeerIdentity.java index ceb6447..0d21528 100644 --- a/src/org/gnunet/util/PeerIdentity.java +++ b/src/org/gnunet/util/PeerIdentity.java @@ -21,7 +21,7 @@ package org.gnunet.util; -import org.gnunet.construct.FixedSizeByteArray; +import org.gnunet.construct.FixedSizeIntegerArray; import org.gnunet.construct.Message; import java.util.Arrays; @@ -32,7 +32,7 @@ import java.util.Arrays; */ public class PeerIdentity implements Message { - @FixedSizeByteArray(length = 64) + @FixedSizeIntegerArray(length = 64, signed = false, bitSize = 8) public byte[] data; static final String HEXES = "0123456789ABCDEF"; diff --git a/src/org/gnunet/util/Program.java b/src/org/gnunet/util/Program.java index a5cd147..1c7f1b9 100644 --- a/src/org/gnunet/util/Program.java +++ b/src/org/gnunet/util/Program.java @@ -21,8 +21,8 @@ package org.gnunet.util; import org.apache.log4j.*; -import org.gnunet.util.getopt.Option; -import org.gnunet.util.getopt.OptionAction; +import org.gnunet.util.getopt.Argument; +import org.gnunet.util.getopt.ArgumentAction; import org.gnunet.util.getopt.Parser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,6 +33,8 @@ import java.io.IOException; /** * Program is the entry point class for everything that uses gnunet services or APIs. * + * Also specifies the default command line arguments using the org.gnunet.util.getopt annotations. + * * @see Service */ public abstract class Program { @@ -42,33 +44,33 @@ public abstract class Program { protected final Configuration cfg = new Configuration(); - @Option(shortname = "c", longname = "config", + @Argument(shortname = "c", longname = "config", description = "Path of the configuration file", argumentName = "FILENAME", - action = OptionAction.STORE_STRING) + action = ArgumentAction.STORE_STRING) public String cfgFileName; - @Option(shortname = "h", longname = "help", + @Argument(shortname = "h", longname = "help", description = "print this help message", - action = OptionAction.SET) + action = ArgumentAction.SET) public boolean printHelp; - @Option(shortname = "v", longname = "version", + @Argument(shortname = "v", longname = "version", description = "print version", - action = OptionAction.SET) + action = ArgumentAction.SET) public boolean showVersion; - @Option(shortname = "L", longname = "log", + @Argument(shortname = "L", longname = "log", description = "configure logging to use LOGLEVEL", argumentName = "LOGLEVEL", - action = OptionAction.STORE_STRING) + action = ArgumentAction.STORE_STRING) public String logLevel; - @Option(shortname = "l", longname = "logfile", + @Argument(shortname = "l", longname = "logfile", description = "configure logging to write logs to LOGFILE", argumentName = "LOGFILE", - action = OptionAction.STORE_STRING) + action = ArgumentAction.STORE_STRING) public String logFile; @@ -79,7 +81,7 @@ public abstract class Program { /** * A program with the desired environment for a gnunet utility. - * While executing the scheduler is guaranteed to run, command arguments are parsed, + * While executing, the scheduler is guaranteed to run, command arguments are parsed, * the default configuration is loaded and the DNS Resolver is initialized. * * @param args array of command line arguments to parse. used to automatically load additional settings @@ -94,6 +96,12 @@ public abstract class Program { */ } + /** + * Configure logging with the given log level and log file. + * + * @param logLevel one of DEBUG,INFO,WARN,ERROR,OFF + * @param logFile logfile, absolute or relative to the current working directory + */ public static void configureLogging(String logLevel, String logFile) { org.apache.log4j.Logger rootLogger = LogManager.getRootLogger(); @@ -155,7 +163,7 @@ public abstract class Program { /** * Start the Program as the initial task of the Scheduler. */ - public void start() { + public final void start() { Parser optParser = new Parser(this); unprocessedArgs = optParser.parse(args); @@ -177,12 +185,22 @@ public abstract class Program { } else { Scheduler.run(new Scheduler.Task() { public void run(Scheduler.RunContext c) { - Program.this.run(); + Program.this.runHook(); } }); } } + /** + * Overridden by specializations of Program, like Service. + * + * Allows for start() to be final. + */ + /* package-private */ + void runHook() { + run(); + } + /** * Override to implement the behavior of the Program. */ diff --git a/src/org/gnunet/util/Resolver.java b/src/org/gnunet/util/Resolver.java index ba47611..831da1e 100644 --- a/src/org/gnunet/util/Resolver.java +++ b/src/org/gnunet/util/Resolver.java @@ -22,14 +22,12 @@ package org.gnunet.util; import com.google.common.net.InetAddresses; import org.gnunet.construct.*; -import org.gnunet.construct.ProtocolViolation; -import org.gnunet.util.getopt.Option; -import org.gnunet.util.getopt.OptionAction; +import org.gnunet.construct.ProtocolViolationException; +import org.gnunet.util.getopt.Argument; +import org.gnunet.util.getopt.ArgumentAction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.net.Inet4Address; -import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.LinkedList; @@ -85,7 +83,7 @@ public class Resolver { @UnionCase(GetMessage.DIRECTION_GET_NAME) public static class NumericAddress implements Address { - @ByteFill + @FillWith @UInt8 public byte[] addr; } @@ -98,7 +96,7 @@ public class Resolver { public static class ResponseBody implements Message { - @ByteFill + @FillWith @UInt8 public byte[] addr; } @@ -292,13 +290,13 @@ public class Resolver { if (len == 4 || len == 16) { in_addr = InetAddress.getByAddress(gmsg.responseBody.addr); } else { - throw new ProtocolViolation("malformed address message"); + throw new ProtocolViolationException("malformed address message"); } rh.cb.onAddress(in_addr); rh.receiveTask = client.receive(deadline.getRemaining(), this); } catch (UnknownHostException e) { - throw new ProtocolViolation("malformed address"); + throw new ProtocolViolationException("malformed address"); } } else { resolveActive = false; @@ -360,9 +358,9 @@ public class Resolver { public static void main(final String[] argv) { new Program(argv) { - @Option(shortname = "r", longname = "reverse", + @Argument(shortname = "r", longname = "reverse", description = "do reverse dns lookup", - action = OptionAction.SET) + action = ArgumentAction.SET) boolean isReverse; @Override diff --git a/src/org/gnunet/util/RunaboutUtil.java b/src/org/gnunet/util/RunaboutUtil.java index 54c16e6..a82dc0a 100644 --- a/src/org/gnunet/util/RunaboutUtil.java +++ b/src/org/gnunet/util/RunaboutUtil.java @@ -44,7 +44,6 @@ public class RunaboutUtil { } @SuppressWarnings("unchecked") - // todo: where to put this? public static int[] getRunaboutMessageTypes(Runabout r) { ArrayList visitees = getRunaboutVisitees(r); int[] msgtypes = new int[visitees.size()]; diff --git a/src/org/gnunet/util/Scheduler.java b/src/org/gnunet/util/Scheduler.java index 611467e..0ea9e15 100644 --- a/src/org/gnunet/util/Scheduler.java +++ b/src/org/gnunet/util/Scheduler.java @@ -391,8 +391,9 @@ public class Scheduler { private static void addSubscriberTask(Collection executableTasks, TaskConfiguration[] subscribers, int eventType) { - if (subscribers[eventType] == null) + if (subscribers[eventType] == null) { return; + } executableTasks.add(subscribers[eventType]); subscribers[eventType].ctx.reasons.add(eventToReason[eventType]); } @@ -570,8 +571,10 @@ public class Scheduler { ByteBuffer buffer = ByteBuffer.allocate(256); + boolean quit = false; + try { - while (true) { + while (!quit) { buffer.clear(); fileChannel.read(buffer); @@ -581,17 +584,25 @@ public class Scheduler { pipe.sink().write(buffer); } } catch (IOException e) { - throw new IOError(e); + quit = true; } } } - - public static FilePipe createFilePipe(File file) { + public static FilePipe openFilePipe(File file) { FilePipeThread fpt = new FilePipeThread(file); fpt.setDaemon(true); fpt.start(); return new FilePipe(fpt); } + + public static class AsyncProcess { + // getIn, getOut, getErr + + } + + public static AsyncProcess openAsyncProcess(/*...*/) { + throw new UnsupportedOperationException("not implemented yet"); + } } diff --git a/src/org/gnunet/util/Server.java b/src/org/gnunet/util/Server.java index f2db539..3d65754 100644 --- a/src/org/gnunet/util/Server.java +++ b/src/org/gnunet/util/Server.java @@ -20,6 +20,7 @@ package org.gnunet.util; +import org.gnunet.construct.Construct; import org.grothoff.Runabout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,47 +30,138 @@ import java.net.SocketAddress; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedList; import java.util.List; +/** + * A server allows to wait for incoming connections from clients and respectively communicate with those clients. + */ public class Server { private static final Logger logger = LoggerFactory .getLogger(Server.class); + /** + * Default idle timeout for new clients. + */ private final RelativeTime idleTimeout; + + /** + * If true, disconnect a client when it sends a message we do not expect to receive. Otherwise, the unexpected + * message will just be discarded. + */ private final boolean requireFound; - private List listenSockets; - private List clients = new LinkedList(); - private MessageRunabout receivedMessagehandler; + /** + * The sockets this server accepts new connections on. + */ + private List listenSockets = new ArrayList(); + + /** + * The list of all clients connected to this server. + */ + private List clientHandles = new LinkedList(); + + /** + * The runabout that receives received messages, as well as information about the sender of the last + * received message. + */ + private MessageRunabout receivedMessageHandler; + + /** + * Whenever a client is disconnected all disconnect handlers are informed. + */ private List disconnectHandlers = new LinkedList(); - private ArrayList expectedMessages; - private boolean shutdownRequested; + /** + * Classes of the messages we expect to receive. If a received message is not in this list, the client + * will be disconnected, otherwise the message is just ignored. + */ + private List expectedMessages = Collections.emptyList(); + + /** + * If true, shut down as soon as all non-monitor clients have finished, and do not allow new connections + * to be made to this server. + */ + private boolean inSoftShutdown; + + /** + * Task that is executed as soon as a connection is ready to be accepted. + */ private Cancelable acceptTask; + /** + * True if we are destroyed, or in the process of being destroyed with no way back. + */ + private boolean destroyed; + + + /** + * Interface implemented by disconnect handlers, whose onDisconnect method is called whenever a client + * is disconnected from the server. + */ public interface DisconnectHandler { + /** + * Called whenever a client is disconnected from the server. + * + * @param clientHandle the handle for the client that was disconnected + */ void onDisconnect(ClientHandle clientHandle); } - + /** + * A handle to a (remote) client connected to this server. + *

+ * Every client handle keeps a reference count.. + * Whenever a part of the programs saves a client handle for further interaction with it, keep() should be called. + * This prevents the server from disconnecting the client when it is idle. + * Once this interaction is over, drop() will decrement the reference count and eventually disconnect the client + * after being idle for long enough. + */ public class ClientHandle { - private RelativeTime clientTimeout; + /** + * The underlying connection to the client- + */ private Connection connection; + /** + * When do we disconnect the client after it has been idle? + */ + private RelativeTime clientTimeout; + + /** + * When referenceCount==0, the server is allowed to drop the client after a timeout. + */ private int referenceCount = 0; - private Connection.ReceiveHandle currentReceive; + /** + * Handle for canceling the receive process of this client, null if no receive is currently going on. + */ + private Cancelable currentReceive; + + /** + * Set to true if the connection to this client should not prevent the server from shutting down. + */ private boolean isMonitor; - private ClientHandle(SocketChannel accept) { - connection = new Connection(accept); + /** + * Iff true, disconnect the client as soon as possible. + * Disconnecting may sometimes not be possible immediately, for example when the reference count is not zero. + */ + private boolean disconnectRequested; + + /** + * Create a client handle. + * + * @param sock + */ + private ClientHandle(SocketChannel sock) { + connection = new Connection(sock); clientTimeout = idleTimeout; // start receiving receiveDone(true); } - /** * Notify us when the server has enough space to transmit * a message of the given size to the given client. @@ -81,36 +173,63 @@ public class Server { * @return a handle to cancel the notification */ public Cancelable notifyTransmitReady(int size, RelativeTime timeout, MessageTransmitter transmitter) { - return connection.notifyTransmitReady(0, timeout, transmitter); + return connection.notifyTransmitReady(size, timeout, transmitter); + } + + /** + * Convenience method for sending messages. + * + * @param timeout when should we give up sending the message, and call cont.cont(false) + * @param message the message to send + * @param cont called when the message has been sent successfully or on error + * @return a handle to cancel sending the message + */ + public Cancelable transmitWhenReady(final RelativeTime timeout, final GnunetMessage.Body message, final Continuation cont) { + return notifyTransmitReady(0, timeout, new MessageTransmitter() { + @Override + public void transmit(Connection.MessageSink sink) { + sink.send(message); + if (cont != null) { + cont.cont(true); + } + } + + @Override + public void handleError() { + if (cont != null) { + cont.cont(false); + } + } + }); } /** * Resume receiving from this client, we are done processing the - * current request. This function must be called from within each + * current request. This function must be called from within each * message handler (or its respective continuations). *

* The server does not automatically continue to receive messages to * support flow control. * - * @param keepClient false if connection to the client should be closed + * @param stayConnected false if connection to the client should be closed */ - public void receiveDone(boolean keepClient) { - if (keepClient) { + public void receiveDone(boolean stayConnected) { + if (stayConnected) { currentReceive = connection.receive(RelativeTime.FOREVER, new MessageReceiver() { @Override public void process(GnunetMessage.Body msg) { - if (msg instanceof UnknownMessageBody) { + if ((msg instanceof UnknownMessageBody) || !expectedMessages.contains(msg.getClass())) { if (requireFound) { logger.info("disconnecting client sending unknown message"); disconnect(); } // otherwise, just ignore it } - if (receivedMessagehandler == null) { + if (receivedMessageHandler == null) { throw new AssertionError("received message, but no handler installed"); } - receivedMessagehandler.setSender(ClientHandle.this); - receivedMessagehandler.visitAppropriate(msg); + receivedMessageHandler.setSender(ClientHandle.this); + receivedMessageHandler.visitAppropriate(msg); } @Override @@ -120,7 +239,12 @@ public class Server { } }); } else { - disconnect(); + if (referenceCount > 0) { + this.disconnectRequested = true; + } else { + System.out.println("disconnecting " + this.isMonitor); + disconnect(); + } } } @@ -129,48 +253,86 @@ public class Server { * Change the idle timeout of this particular client. */ public void setTimeout(RelativeTime newTimeout) { + this.clientTimeout = newTimeout; } + /** + * Ask the server to disconnect from the given client. + *

+ * The client will be disconnected from the server, no matter what the current reference count is. + */ public void disconnect() { connection.disconnect(); - Server.this.clients.remove(this); + // if we are in the process of destruction, to not remove, the destruction function will do this, + // removing the client handle while in destruction would yield a concurrent modification exception + if (!destroyed) { + Server.this.clientHandles.remove(this); + } for (DisconnectHandler dh : disconnectHandlers) { dh.onDisconnect(this); } + Server.this.testForSoftShutdown(); } /** - * Disable the warning the server issues if a message is not acknowledged - * in a timely fashion. Use this call if a client is intentionally delayed - * for a while. Only applies to the current message. + * Prevent the client from being disconnected. + * For every keep, there should be an additional matching drop. */ - public void disableReceiveDoneWarning() { - // todo - } - public void keep() { referenceCount++; } + + /** + * Allow to disconnect this client, if not prevented by previous calls to keep. + *

+ * A call to drop should be executed for every call to keep. + * After drop() has been executed for every matching keep(), the next call to drop() + * allows the server to disconnect the client after a timeout. + */ public void drop() { + assert referenceCount > 0; referenceCount--; - if (referenceCount == 0 && shutdownRequested) { + if (referenceCount == 0 && disconnectRequested) { disconnect(); } } + + /** + * Set the 'monitor' flag on this client. Clients which have been + * marked as 'monitors' won't prevent the server from shutting down + * once 'GNUNET_SERVER_stop_listening' has been invoked. The idea is + * that for "normal" clients we likely want to allow them to process + * their requests; however, monitor-clients are likely to 'never' + * disconnect during shutdown and thus will not be considered when + * determining if the server should continue to exist after + * 'GNUNET_SERVER_destroy' has been called. + */ public void markMonitor() { this.isMonitor = true; } + + public boolean isMonitor() { + return isMonitor; + } } - abstract static class MessageRunabout extends Runabout { + /** + * All handlers for receiving messages from clients have to inherit this class. + *

+ * MessageRunabout is a standard runabout with the added possibility of getting the sender of the message. + * This is necessary as the runabout's visit methods can have only one parameter. + */ + public abstract static class MessageRunabout extends Runabout { private ClientHandle currentSender; /** * Allows implementors of MessageRunabout to get the Client that sent the message * currently visited. + *

+ * The return value of getSender() is only valid while executing a visit method. * * @return handle of the client whose message is currently being visited */ @@ -178,39 +340,16 @@ public class Server { return currentSender; } + /** + * Private method used to set the sender for the getSender() method. + * + * @param clientHandle the client handle to set as the sender + */ private void setSender(ClientHandle clientHandle) { currentSender = clientHandle; } } - - private void doAccept(final ServerSocketChannel srv) { - Scheduler.TaskConfiguration b = new Scheduler.TaskConfiguration(RelativeTime.FOREVER, - new Scheduler.Task() { - @Override - public void run(Scheduler.RunContext ctx) { - acceptTask = null; - try { - SocketChannel cli = srv.accept(); - - if (cli != null) { - logger.debug("client connected"); - cli.configureBlocking(false); - ClientHandle clientHandle = new ClientHandle(cli); - clients.add(clientHandle); - } - - } catch (IOException e) { - throw new RuntimeException("accept failed", e); - } - doAccept(srv); - } - }); - b.selectAccept(srv); - acceptTask = b.schedule(); - } - - /** * Create a server listening on all specified addresses. * @@ -221,7 +360,6 @@ public class Server { public Server(List addresses, RelativeTime idleTimeout, boolean requireFound) { this.idleTimeout = idleTimeout; this.requireFound = requireFound; - listenSockets = new ArrayList(addresses.size()); try { for (SocketAddress addr : addresses) { ServerSocketChannel socket = ServerSocketChannel.open(); @@ -229,20 +367,53 @@ public class Server { socket.socket().bind(addr); logger.debug("socket listening on {}", addr.toString()); listenSockets.add(socket); - doAccept(socket); + addAcceptSocket(socket); } } catch (IOException e) { - throw new RuntimeException("could not bind"); + throw new RuntimeException("could not bind", e); } } + /** + * Create a server, not listening on any sockets yet for new connections. + * + * @param idleTimeout time after a client will be disconnected if idle + * @param requireFound allow unknown messages to be received without disconnecting the client in response + */ public Server(RelativeTime idleTimeout, boolean requireFound) { this.idleTimeout = idleTimeout; this.requireFound = requireFound; } - public void addAcceptSocket(ServerSocketChannel sock) { - doAccept(sock); + /** + * Accept new connections from the given server socket. + * + * @param sock the new socket to accept connections from + */ + public final void addAcceptSocket(final ServerSocketChannel sock) { + Scheduler.TaskConfiguration b = new Scheduler.TaskConfiguration(RelativeTime.FOREVER, + new Scheduler.Task() { + @Override + public void run(Scheduler.RunContext ctx) { + acceptTask = null; + try { + SocketChannel cli = sock.accept(); + + if (cli != null) { + logger.debug("client connected"); + cli.configureBlocking(false); + ClientHandle clientHandle = new ClientHandle(cli); + clientHandles.add(clientHandle); + } + + } catch (IOException e) { + throw new RuntimeException("accept failed", e); + } + addAcceptSocket(sock); + } + }); + b.selectAccept(sock); + acceptTask = b.schedule(); } /** @@ -253,10 +424,18 @@ public class Server { * @param msgRunabout handler */ public void setHandler(MessageRunabout msgRunabout) { - receivedMessagehandler = msgRunabout; + receivedMessageHandler = msgRunabout; expectedMessages = RunaboutUtil.getRunaboutVisitees(msgRunabout); } + /** + * Ask the server to notify us whenever a client disconnects. + * This handler is called whenever the actual network connection + * is closed; the reference count may be zero or larger than zero + * at this point. Note that the disconnect handler is also called when + * + * @param disconnectHandler handler to call on disconnect + */ public Cancelable notifyDisconnect(final DisconnectHandler disconnectHandler) { this.disconnectHandlers.add(disconnectHandler); return new Cancelable() { @@ -268,19 +447,63 @@ public class Server { } /** - * Stop the listen socket and get ready to shutdown the server - * once only 'monitor' clients are left. + * Stop the listen socket destroy the server as soon as only monitor clients are left. */ public void stopListening() { - shutdownRequested = true; - // todo: shut down if only monitor clients left + inSoftShutdown = true; + if (acceptTask != null) { + acceptTask.cancel(); + acceptTask = null; + } + testForSoftShutdown(); } + /** + * Disconnect all clients forcefully from the server and stop listening. + *

+ * No methods should be called on a server and its client handles after destroy() has been called. + */ public void destroy() { - for (ClientHandle h : new ArrayList(clients)) { + if (destroyed) { + return; + } + destroyed = true; + for (ClientHandle h : clientHandles) { h.disconnect(); } - acceptTask.cancel(); + clientHandles.clear(); + if (acceptTask != null) { + acceptTask.cancel(); + acceptTask = null; + } + for (ServerSocketChannel ssc : listenSockets) { + try { + ssc.close(); + } catch (IOException e) { + logger.error("closing listen socket failed", e); + } + } } + /** + * Test if we should destroy outselves. + */ + private void testForSoftShutdown() { + // do this so we don't have many recursive calls to testForSoftShutdown when shutting down + if (destroyed) { + return; + } + if (inSoftShutdown) { + System.out.println(""+clientHandles.size()); + boolean done = true; + for (ClientHandle clientHandle : this.clientHandles) { + if (!clientHandle.isMonitor) { + done = false; + } + } + if (done) { + destroy(); + } + } + } } diff --git a/src/org/gnunet/util/Service.java b/src/org/gnunet/util/Service.java index bbbba00..69742b6 100644 --- a/src/org/gnunet/util/Service.java +++ b/src/org/gnunet/util/Service.java @@ -29,14 +29,21 @@ import java.net.Inet4Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.nio.ByteBuffer; import java.nio.channels.Channel; import java.nio.channels.FileChannel; +import java.nio.channels.Pipe; import java.nio.channels.SelectableChannel; import java.util.LinkedList; /** * Server the entry point class for every gnunet-java component providing services * to other components. + * + * The configuration for the server (i.e. ports/interfaces) is loaded with the standard configuration system. + * + * Note that other processes can send signals to the service via a pipe, whose name has to be given in the + * environment variable GNUNET_OS_CONTROL_PIPE */ public abstract class Service extends Program { private static final Logger logger = LoggerFactory @@ -47,8 +54,9 @@ public abstract class Service extends Program { private RelativeTime idleTimeout; private boolean requireFound; + private Cancelable sigpipeTask; - private SelectableChannel sigpipeChannel; + private Pipe.SourceChannel sigpipeChannel; public Service(String serviceName, RelativeTime idleTimeout, boolean requireFound, String[] args) { super(args); @@ -57,63 +65,94 @@ public abstract class Service extends Program { this.requireFound = requireFound; } - + /** + * Obtain the server used by a service. Note that the server must NOT + * be destroyed by the caller. + * + * @return handle to the server for this service, NULL if there is none + */ public final Server getServer() { return s; } + /** + * Stop the service. + */ public void stop() { - + s.stopListening(); } - public void start() { - super.start(); + public void runHook() { String ip4AddrList = getConfiguration().getValueString(serviceName, "ACCEPT_FROM"); String ip6AddrList = getConfiguration().getValueString(serviceName, "ACCEPT_FROM6"); int port = (int) getConfiguration().getValueNumer(serviceName, "PORT"); LinkedList addrs = new LinkedList(); - for (String ip4Addr : ip4AddrList.split("[;]")) { - InetAddress addr = Resolver.getInetAddressFromString(ip4Addr); - addrs.add(new InetSocketAddress(addr, port)); + if (ip4AddrList != null) { + for (String ip4Addr : ip4AddrList.split("[;]")) { + InetAddress addr = Resolver.getInetAddressFromString(ip4Addr); + addrs.add(new InetSocketAddress(addr, port)); + } } - for (String ip6Addr : ip6AddrList.split("[;]")) { - InetAddress addr = Resolver.getInetAddressFromString(ip6Addr); - addrs.add(new InetSocketAddress(addr, port)); + if (ip6AddrList != null) { + for (String ip6Addr : ip6AddrList.split("[;]")) { + InetAddress addr = Resolver.getInetAddressFromString(ip6Addr); + addrs.add(new InetSocketAddress(addr, port)); + } } s = new Server(addrs, idleTimeout, requireFound); String pipeName = System.getenv("GNUNET_OS_CONTROL_PIPE"); - if (pipeName != null) { - logger.debug("service started with control pipe"); - FileChannel f; - try { - f = (new FileInputStream(pipeName)).getChannel(); - } catch (FileNotFoundException e) { - logger.error("could not open control pipe"); - } + if (pipeName != null && !pipeName.isEmpty()) { + Scheduler.FilePipe p = Scheduler.openFilePipe(new File(pipeName)); Scheduler.TaskConfiguration t = new Scheduler.TaskConfiguration(RelativeTime.FOREVER, new SigpipeTask()); + t.selectRead(p.getSource()); sigpipeTask = t.schedule(); + sigpipeChannel = p.getSource(); } - } - public class SigpipeTask implements Scheduler.Task { + run(); + } + private class SigpipeTask implements Scheduler.Task { @Override public void run(Scheduler.RunContext ctx) { - Scheduler.TaskConfiguration t = new Scheduler.TaskConfiguration(RelativeTime.FOREVER, - new SigpipeTask()); - sigpipeTask = t.schedule(); + ByteBuffer b = ByteBuffer.allocate(1); + int n; + try { + n = sigpipeChannel.read(b); + } catch (IOException e) { + logger.error("error reading signal pipe", e); + return; + } + b.flip(); + boolean stopped = false; + + if (n == 1) { + byte sig = b.get(); + // 15=sigterm + if (sig == 15) { + logger.info("service shutting down"); + getServer().stopListening(); + stopped = true; + } + } + if (!stopped) { + Scheduler.TaskConfiguration t = new Scheduler.TaskConfiguration(RelativeTime.FOREVER, this); + sigpipeTask = t.schedule(); + } else { + try { + sigpipeChannel.close(); + } catch (IOException e) { + logger.error("could not close sigpipe channel, quitting"); + System.exit(2); + } + } } } - - /** - * Override to implement the behavior of the Program. - */ - public abstract void run(); } \ No newline at end of file diff --git a/src/org/gnunet/util/Strings.java b/src/org/gnunet/util/Strings.java index 3c369b1..a35568a 100644 --- a/src/org/gnunet/util/Strings.java +++ b/src/org/gnunet/util/Strings.java @@ -26,6 +26,21 @@ package org.gnunet.util; public class Strings { private static final String encTable = "0123456789ABCDEFGHIJKLMNOPQRSTUV"; + + /** + * Convert binary data to ASCII encoding. The ASCII encoding is rather + * GNUnet specific. It was chosen such that it only uses characters + * in [0-9A-V], can be produced without complex arithmetics and uses a + * small number of characters. + * Does not append 0-terminator, but returns a pointer to the place where + * it should be placed, if needed. + * + * returned string has length ((size*8) + (((size*8) % 5) > 0 ? 5 - ((size*8) % 5) : 0)) / 5 bytes + * + * @param data data to encode + * @return pointer to the next byte in 'out' or NULL on error. + */ + public static String dataToString(byte[] data) { StringBuilder sb = new StringBuilder(); @@ -54,15 +69,25 @@ public class Strings { return sb.toString(); } - public static byte[] stringToData(String string) { - /* + /** + * Convert ASCII encoding back to data + * out_size must match exactly the size of the data before it was encoded. + * + * @param string the string to decode + * @param outSize size of the output buffer + * @return GNUNET_OK on success, GNUNET_SYSERR if result has the wrong encoding + */ + + public static byte[] stringToData(String string, int outSize) { long rpos; long wpos; long bits; long vbit; - int ret; - int shift; - int encoded_len = out_size * 8; + long ret; + long shift; + int enclen = string.length(); + int encoded_len = outSize * 8; + byte[] out = new byte[outSize]; if (encoded_len % 5 > 0) { // padding! vbit = encoded_len % 5; @@ -75,29 +100,39 @@ public class Strings { throw new AssertionError(); } - wpos = out_size; + wpos = outSize; rpos = enclen; - bits = (ret = getValue__(enc[--rpos])) >> (5 - encoded_len % 5); - if (-1 == ret) - return GNUNET_SYSERR; + bits = (ret = getValue__(string.charAt((int) (--rpos)))) >> (5 - encoded_len % 5); + if (-1 == ret) { + throw new AssertionError(); + } while (wpos > 0) { - GNUNET_assert(rpos > 0); - bits = ((ret = getValue__(enc[--rpos])) << vbit) | bits; - if (-1 == ret) - return GNUNET_SYSERR; + assert rpos > 0; + bits = ((ret = getValue__(string.charAt((int) (--rpos)))) << vbit) | bits; + if (-1 == ret) { + throw new AssertionError(); + } vbit += 5; if (vbit >= 8) { - out[--wpos] = (unsigned char)bits; + out[(int)--wpos] = (byte)((char) bits); bits >>= 8; vbit -= 8; } } - GNUNET_assert(rpos == 0); - GNUNET_assert(vbit == 0); - return GNUNET_OK; - + assert(rpos == 0); + assert(vbit == 0); + return out; } - */ - throw new UnsupportedOperationException("not yet implemented"); + + + private static int getValue__ (char a) { + if ((a >= '0') && (a <= '9')) { + return a - '0'; + } + if ((a >= 'A') && (a <= 'V')) { + return (a - 'A' + 10); + } + return -1; } + } diff --git a/src/org/gnunet/util/TESTMessage.java b/src/org/gnunet/util/TESTMessage.java deleted file mode 100644 index 44c28ce..0000000 --- a/src/org/gnunet/util/TESTMessage.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2011, 2012 Christian Grothoff (and other contributing authors) - - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, or (at your - option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. - */ - -package org.gnunet.util; - -import org.gnunet.construct.UnionCase; - -/** - * Sent back when a client sends this message to a service. - */ -@UnionCase(1) -public class TESTMessage implements GnunetMessage.Body { - // empty -} diff --git a/src/org/gnunet/util/TestMessage.java b/src/org/gnunet/util/TestMessage.java new file mode 100644 index 0000000..b08a706 --- /dev/null +++ b/src/org/gnunet/util/TestMessage.java @@ -0,0 +1,31 @@ +/* + This file is part of GNUnet. + (C) 2011, 2012 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +package org.gnunet.util; + +import org.gnunet.construct.UnionCase; + +/** + * Sent back when a client sends this message to a service. + */ +@UnionCase(1) +public class TestMessage implements GnunetMessage.Body { + // empty +} diff --git a/src/org/gnunet/util/UnknownMessageBody.java b/src/org/gnunet/util/UnknownMessageBody.java index 2270846..b978ec6 100644 --- a/src/org/gnunet/util/UnknownMessageBody.java +++ b/src/org/gnunet/util/UnknownMessageBody.java @@ -25,7 +25,7 @@ package org.gnunet.util; * is not understood, and therefore no real message body could be constructed. * * Note that this class implements GnunetMessage.Body but does not have a MessageID associated. - * This message should not be sent/received over the network as a message body. + * This message should not, and can not, be sent/received over the network directly as a message body. * * @author Florian Dold */ diff --git a/src/org/gnunet/util/getopt/Argument.java b/src/org/gnunet/util/getopt/Argument.java new file mode 100644 index 0000000..34159d0 --- /dev/null +++ b/src/org/gnunet/util/getopt/Argument.java @@ -0,0 +1,47 @@ +/* + This file is part of GNUnet. + (C) 2011, 2012 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +package org.gnunet.util.getopt; + + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for fields receiving an argument from the command line. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Argument { + public String shortname(); + public String longname(); + /** + * Possible values: "store-string", "set", "reset", "count", "store-int" + */ + public ArgumentAction action(); + /* + * Name of the Option's argument(s), empty string of option takes no arguments + * + */ + public String argumentName() default ""; + public String description(); +} diff --git a/src/org/gnunet/util/getopt/ArgumentAction.java b/src/org/gnunet/util/getopt/ArgumentAction.java new file mode 100644 index 0000000..077e71c --- /dev/null +++ b/src/org/gnunet/util/getopt/ArgumentAction.java @@ -0,0 +1,29 @@ +/* + This file is part of GNUnet. + (C) 2011, 2012 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +package org.gnunet.util.getopt; + + +/** + * Possibilities for what should happen when an argument is read from the command line + */ +public enum ArgumentAction { + SET, RESET, STORE_STRING, STORE_NUMBER +} diff --git a/src/org/gnunet/util/getopt/Option.java b/src/org/gnunet/util/getopt/Option.java deleted file mode 100644 index f2276e3..0000000 --- a/src/org/gnunet/util/getopt/Option.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2011, 2012 Christian Grothoff (and other contributing authors) - - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, or (at your - option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. - */ - -package org.gnunet.util.getopt; - - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.FIELD) -public @interface Option { - public String shortname(); - public String longname(); - /** - * Possible values: "store-string", "set", "reset", "count", "store-int" - */ - public OptionAction action(); - /* - * Name of the Option's argument(s), empty string of option takes no arguments - * - */ - public String argumentName() default ""; - public String description(); -} diff --git a/src/org/gnunet/util/getopt/OptionAction.java b/src/org/gnunet/util/getopt/OptionAction.java deleted file mode 100644 index 29b4a61..0000000 --- a/src/org/gnunet/util/getopt/OptionAction.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2011, 2012 Christian Grothoff (and other contributing authors) - - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, or (at your - option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. - */ - -package org.gnunet.util.getopt; - - -public enum OptionAction { - SET, RESET, STORE_STRING, STORE_NUMBER, INCREMENT -} diff --git a/src/org/gnunet/util/getopt/Parser.java b/src/org/gnunet/util/getopt/Parser.java index 9807146..6ecc220 100644 --- a/src/org/gnunet/util/getopt/Parser.java +++ b/src/org/gnunet/util/getopt/Parser.java @@ -22,7 +22,6 @@ package org.gnunet.util.getopt; import org.gnunet.construct.ReflectUtil; import java.lang.reflect.Field; -import java.lang.reflect.Modifier; import java.util.*; /** @@ -45,10 +44,10 @@ public class Parser { * An option together with its target field. */ static class OptionField { - Option opt; + Argument opt; Field f; - public OptionField(Option opt, Field f) { + public OptionField(Argument opt, Field f) { this.opt = opt; this.f = f; } @@ -66,7 +65,7 @@ public class Parser { private Map longOpt = new HashMap(); private Map shortOpt = new HashMap(); - private Collection

    *
  • all subclasses must be public, sadly this also means that you * can not use an anonymous inner class (!)
  • - *
  • the types to all arguments of visit methods must be public
  • + *
  • the types_length to all arguments of visit methods must be public
  • *
  • all visit methods must be public (!)
  • *
* Otherwise the visitor will die with an IllegalAccessError during execution. @@ -265,7 +265,8 @@ public class Runabout { } catch (IllegalAccessException e) { throw new RunaboutException(e.toString()); } catch (InvocationTargetException e) { - e.getCause().printStackTrace(); + System.err.println("stacktrace:"); + e.getCause().printStackTrace(System.out); throw new RunaboutException(e.getCause().toString()); } } diff --git a/src/org/grothoff/package-info.java b/src/org/grothoff/package-info.java index 19056a2..2a8b8f0 100644 --- a/src/org/grothoff/package-info.java +++ b/src/org/grothoff/package-info.java @@ -1,4 +1,4 @@ /** - * java implementation of single argument multiple dispatch + * Pure java implementation of single argument multiple dispatch */ package org.grothoff; diff --git a/test/org/gnunet/construct/ByteFillMessage.java b/test/org/gnunet/construct/ByteFillMessage.java index fe7716f..090f7d0 100644 --- a/test/org/gnunet/construct/ByteFillMessage.java +++ b/test/org/gnunet/construct/ByteFillMessage.java @@ -26,7 +26,7 @@ public class ByteFillMessage implements Message { @UInt32 public int someValue; - @ByteFill + @FillWith @UInt8 public byte[] rest; } diff --git a/test/org/gnunet/construct/ConstructTest.java b/test/org/gnunet/construct/ConstructTest.java index c7f9853..8853b1d 100644 --- a/test/org/gnunet/construct/ConstructTest.java +++ b/test/org/gnunet/construct/ConstructTest.java @@ -13,7 +13,7 @@ public class ConstructTest { @FrameSize @UInt32 public int frameSize; - @ByteFill + @FillWith @UInt8 public byte[] bytes; } diff --git a/test/org/gnunet/construct/FillParserTest.java b/test/org/gnunet/construct/FillParserTest.java new file mode 100644 index 0000000..115b567 --- /dev/null +++ b/test/org/gnunet/construct/FillParserTest.java @@ -0,0 +1,37 @@ +package org.gnunet.construct; + +import junit.framework.Assert; +import org.junit.Test; + +/** + * ... + * + * @author Florian Dold + */ +public class FillParserTest { + + public static class FillTestMessage implements Message { + @FrameSize + @UInt32 + public int size; + @FillWith + public StringTuple[] strings; + } + + @Test + public void test_fillParser() { + FillTestMessage m = new FillTestMessage(); + m.strings = new StringTuple[]{new StringTuple("foo", "bar"), new StringTuple("quux", "spam")}; + Construct.patch(m); + System.out.println(m.size); + byte[] data = Construct.toBinary(m); + Assert.assertEquals(m.size, data.length); + + FillTestMessage m2 = Construct.parseAs(data, FillTestMessage.class); + + Assert.assertEquals(m.strings.length, m2.strings.length); + + Assert.assertEquals(m.strings[0], m2.strings[0]); + Assert.assertEquals(m.strings[1], m2.strings[1]); + } +} diff --git a/test/org/gnunet/construct/FixedSizeTest.java b/test/org/gnunet/construct/FixedSizeTest.java new file mode 100644 index 0000000..573e120 --- /dev/null +++ b/test/org/gnunet/construct/FixedSizeTest.java @@ -0,0 +1,52 @@ +package org.gnunet.construct; + +import junit.framework.Assert; +import org.junit.Test; + +/** + * ... + * + * @author Florian Dold + */ +public class FixedSizeTest { + + public static class Msg implements Message { + @UInt8 + public int v; + + public Msg() { + // default ctor required by Construct + } + + public Msg(int v) { + this.v = v; + } + } + + public static class FixedSizeTestMessage implements Message { + @FixedSizeArray(length = 4) + public Msg[] msgs; + } + + + @Test + public void test_fixedNested() { + FixedSizeTestMessage m = new FixedSizeTestMessage(); + m.msgs = new Msg[]{new Msg(1), new Msg(2), new Msg(3), new Msg(4)}; + byte[] bytes = Construct.toBinary(m); + + FixedSizeTestMessage m2 = Construct.parseAs(bytes, FixedSizeTestMessage.class); + + Assert.assertEquals(m.msgs[0].v, m2.msgs[0].v); + Assert.assertEquals(m.msgs[1].v, m2.msgs[1].v); + Assert.assertEquals(m.msgs[2].v, m2.msgs[2].v); + Assert.assertEquals(m.msgs[3].v, m2.msgs[3].v); + } + + @Test(expected = AssertionError.class) + public void test_sizeMismatch() { + FixedSizeTestMessage m = new FixedSizeTestMessage(); + m.msgs = new Msg[]{new Msg(1), new Msg(2)}; + byte[] bytes = Construct.toBinary(m); + } +} diff --git a/test/org/gnunet/construct/FrameSizeTest.java b/test/org/gnunet/construct/FrameSizeTest.java new file mode 100644 index 0000000..dcce5de --- /dev/null +++ b/test/org/gnunet/construct/FrameSizeTest.java @@ -0,0 +1,50 @@ +package org.gnunet.construct; + +import junit.framework.Assert; +import org.gnunet.util.GnunetMessage; +import org.junit.Test; + +/** + * ... + * + * @author Florian Dold + */ +public class FrameSizeTest { + public static class CoordMessage implements Message { + @FrameSize + @UInt32 + public int size; + @UInt32 + public int x; + @UInt8 + public int y; + } + + public static class RecursiveMessage implements Message { + @FrameSize + @UInt32 + public int size; + + @ZeroTerminatedString + public String data; + + @NestedMessage(newFrame = true, optional = true) + public RecursiveMessage rec; + + } + + @Test + public void test_simple() { + CoordMessage m = new CoordMessage(); + Construct.patch(m); + Assert.assertEquals(9, m.size); + } + + + //@Test + public void test_recursive_1() { + RecursiveMessage rm = new RecursiveMessage(); + rm.data = "foo"; + Construct.patch(rm); + } +} diff --git a/test/org/gnunet/construct/QueryMessage.java b/test/org/gnunet/construct/QueryMessage.java index 2accd42..4bf9af5 100644 --- a/test/org/gnunet/construct/QueryMessage.java +++ b/test/org/gnunet/construct/QueryMessage.java @@ -26,6 +26,6 @@ public class QueryMessage implements Message { @UInt8 public int query; - @ByteFill + @FillWith @UInt8 public byte[] varsize; } diff --git a/test/org/gnunet/construct/SendMessageTest.java b/test/org/gnunet/construct/SendMessageTest.java new file mode 100644 index 0000000..b95cc78 --- /dev/null +++ b/test/org/gnunet/construct/SendMessageTest.java @@ -0,0 +1,34 @@ +package org.gnunet.construct; + +import org.gnunet.core.SendMessage; +import org.gnunet.util.AbsoluteTime; +import org.gnunet.util.GnunetMessage; +import org.gnunet.util.PeerIdentity; +import org.gnunet.util.TestMessage; +import org.junit.Test; + +/** + * Regression test for a message class in org.gnunet.core + * + * todo: should this test be really here? + * + * @author Florian Dold + */ +public class SendMessageTest { + + @Test + public void test_patch() { + SendMessage m = new SendMessage(); + m.deadline = AbsoluteTime.FOREVER.asMessage(); + m.peer = new PeerIdentity(); // null identity + m.payloadMessage = new GnunetMessage(); + m.payloadMessage.body = new TestMessage(); + m.payloadMessage.header = new GnunetMessage.Header(); + + GnunetMessage container = new GnunetMessage(); + container.body = m; + container.header = new GnunetMessage.Header(); + + Construct.patch(container); + } +} diff --git a/test/org/gnunet/construct/SimpleTestMessage.java b/test/org/gnunet/construct/SimpleTestMessage.java deleted file mode 100644 index e55b3b0..0000000 --- a/test/org/gnunet/construct/SimpleTestMessage.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2011, 2012 Christian Grothoff (and other contributing authors) - - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, or (at your - option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. - */ - -package org.gnunet.construct; - -public class SimpleTestMessage implements Message { - - @UInt8 - public short v1; - - @UInt8 - public short v2; - - @NestedMessage - public SimpleTestMessage2 mn; - - @FixedSizeArray(length=5) - public SimpleTestMessage2[] mns; -} diff --git a/test/org/gnunet/construct/SimpleTestMessage2.java b/test/org/gnunet/construct/SimpleTestMessage2.java deleted file mode 100644 index a6433d6..0000000 --- a/test/org/gnunet/construct/SimpleTestMessage2.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2011, 2012 Christian Grothoff (and other contributing authors) - - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, or (at your - option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. - */ - -package org.gnunet.construct; - -//@MessageId(321) -public class SimpleTestMessage2 implements Message { - - @UInt32 - public long value; - -} diff --git a/test/org/gnunet/construct/SizeTestMessage.java b/test/org/gnunet/construct/SizeTestMessage.java deleted file mode 100644 index 4710ede..0000000 --- a/test/org/gnunet/construct/SizeTestMessage.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2011, 2012 Christian Grothoff (and other contributing authors) - - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, or (at your - option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. - */ - -package org.gnunet.construct; - -public class SizeTestMessage implements Message { - - @FrameSize - @UInt16 - public long totalSize; - - @UInt16 - public long someValue; - - @ByteFill - public byte[] rest; -} diff --git a/test/org/gnunet/construct/StringTest.java b/test/org/gnunet/construct/StringTest.java new file mode 100644 index 0000000..7839f8b --- /dev/null +++ b/test/org/gnunet/construct/StringTest.java @@ -0,0 +1,50 @@ +package org.gnunet.construct; + +import junit.framework.Assert; +import org.junit.Test; + +/** + * ... + * + * @author Florian Dold + */ +public class StringTest { + public static class StrMsg implements Message { + @FrameSize + @UInt32 + public int len; + @ZeroTerminatedString(optional = false) + public String str1; + @ZeroTerminatedString(optional = true) + public String str2; + } + + + @Test + public void test_empty() { + StrMsg m = new StrMsg(); + m.str1 = ""; + m.str2 = ""; + Construct.patch(m); + byte[] data = Construct.toBinary(m); + Assert.assertEquals(4+1+1, data.length); + StrMsg m2 = Construct.parseAs(data, StrMsg.class); + Assert.assertEquals("", m2.str1); + Assert.assertEquals("", m2.str2); + } + + @Test + public void test_null() { + StrMsg m = new StrMsg(); + m.str1 = ""; + m.str2 = null; + Construct.patch(m); + byte[] data = Construct.toBinary(m); + Assert.assertEquals(4+1, data.length); + Assert.assertEquals(4+1, m.len); + StrMsg m2 = Construct.parseAs(data, StrMsg.class); + Assert.assertEquals("", m2.str1); + Assert.assertEquals(null, m2.str2); + } +} + diff --git a/test/org/gnunet/construct/StringTuple.java b/test/org/gnunet/construct/StringTuple.java new file mode 100644 index 0000000..085b7a9 --- /dev/null +++ b/test/org/gnunet/construct/StringTuple.java @@ -0,0 +1,29 @@ +package org.gnunet.construct; + +/** +* ... +* +* @author Florian Dold +*/ +public class StringTuple implements Message { + @ZeroTerminatedString + public String str1; + @ZeroTerminatedString + public String str2; + + public StringTuple() { + // empty default ctor needed by Construct + } + public StringTuple(String str1, String str2) { + this.str1 = str1; + this.str2 = str2; + } + @Override + public boolean equals(Object other) { + if (!(other instanceof StringTuple)) { + return false; + } + StringTuple otherT = (StringTuple) other; + return otherT.str1.equals(this.str1) && otherT.str2.equals(this.str2); + } +} diff --git a/test/org/gnunet/construct/VarTestMessage.java b/test/org/gnunet/construct/VarTestMessage.java deleted file mode 100644 index 25e5bbc..0000000 --- a/test/org/gnunet/construct/VarTestMessage.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2011, 2012 Christian Grothoff (and other contributing authors) - - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, or (at your - option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. - */ - -package org.gnunet.construct; - -public class VarTestMessage implements Message { - @UInt16 - public long length; - - @VariableSizeArray(lengthField="length") - public SimpleTestMessage2[] msgs; -} diff --git a/test/org/gnunet/construct/VariableSizeArrayTest.java b/test/org/gnunet/construct/VariableSizeArrayTest.java new file mode 100644 index 0000000..f1d19c1 --- /dev/null +++ b/test/org/gnunet/construct/VariableSizeArrayTest.java @@ -0,0 +1,33 @@ +package org.gnunet.construct; + +import junit.framework.Assert; +import org.junit.Test; + +/** + * ... + * + * @author Florian Dold + */ +public class VariableSizeArrayTest { + public static class VariableTestMessage implements Message { + @UInt32 + public int num; + @VariableSizeArray(lengthField = "num") + public StringTuple[] msgs; + } + + @Test + public void test_variableSizeArray() { + VariableTestMessage m = new VariableTestMessage(); + m.msgs = new StringTuple[]{new StringTuple("foo", "bar"), new StringTuple("quux", "baz"), new StringTuple("spam", "eggs")}; + Construct.patch(m); + Assert.assertEquals(3, m.num); + byte[] data = Construct.toBinary(m); + VariableTestMessage m2 = Construct.parseAs(data, VariableTestMessage.class); + Assert.assertEquals(m2.num, 3); + Assert.assertEquals(m.msgs[0], m2.msgs[0]); + Assert.assertEquals(m.msgs[1], m2.msgs[1]); + Assert.assertEquals(m.msgs[2], m2.msgs[2]); + + } +} diff --git a/test/org/gnunet/core/CoreTest.java b/test/org/gnunet/core/CoreTest.java index e4a9142..5e066ed 100644 --- a/test/org/gnunet/core/CoreTest.java +++ b/test/org/gnunet/core/CoreTest.java @@ -26,18 +26,15 @@ import org.gnunet.testing.TestingSubsystem; import org.gnunet.util.*; import org.grothoff.Runabout; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; import static org.junit.Assert.assertTrue; public class CoreTest { - TestingSetup testing = new TestingSetup(); - @Test(timeout = 500) public void test_core_init() { - TestingSubsystem ts = testing.startSubsystem("core"); + TestingSubsystem ts = new TestingSubsystem("core"); final Wrapper res = new Wrapper(false); @@ -68,13 +65,13 @@ public class CoreTest { @Test(timeout = 5000) public void test_core_echo() { - TestingSubsystem ts = testing.startSubsystem("core"); + TestingSubsystem ts = new TestingSubsystem("core"); final Wrapper gotResponse = new Wrapper(false); final Core core = new Core(ts.getConfiguration()); core.setMessageHandler(new Runabout() { - public void visit(TESTMessage t) { + public void visit(TestMessage t) { gotResponse.set(true); core.disconnect(); } @@ -86,7 +83,7 @@ public class CoreTest { core.notifyTransmitReady(0, RelativeTime.FOREVER, myIdentity, 4, new MessageTransmitter() { @Override public void transmit(Connection.MessageSink sink) { - sink.send(new TESTMessage()); + sink.send(new TestMessage()); } @Override diff --git a/test/org/gnunet/dht/DHTTest.java b/test/org/gnunet/dht/DHTTest.java index b684aef..6c93356 100644 --- a/test/org/gnunet/dht/DHTTest.java +++ b/test/org/gnunet/dht/DHTTest.java @@ -33,9 +33,7 @@ public class DHTTest { public void test_dht_put() { final Wrapper putFinished = new Wrapper(true); - TestingSetup testing = new TestingSetup(); - - TestingSubsystem ts = testing.startSubsystem("dht"); + TestingSubsystem ts = new TestingSubsystem("dht"); final DistributedHashTable dht = new DistributedHashTable(ts.getConfiguration()); dht.put(new HashCode("gnj-test"), new byte[]{1, 2, 3}, 1, EnumSet.noneOf(RouteOption.class), diff --git a/test/org/gnunet/nse/NSETest.java b/test/org/gnunet/nse/NSETest.java new file mode 100644 index 0000000..c3e6512 --- /dev/null +++ b/test/org/gnunet/nse/NSETest.java @@ -0,0 +1,56 @@ +/* + This file is part of GNUnet. + (C) 2011, 2012 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +package org.gnunet.nse; + +import org.gnunet.testing.TestingSetup; +import org.gnunet.testing.TestingSubsystem; +import org.gnunet.util.AbsoluteTime; +import org.gnunet.util.Scheduler; +import org.gnunet.util.Wrapper; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * @author Florian Dold + */ +public class NSETest { + @Test + public void test_nse() { + final Wrapper gotResult = new Wrapper(false); + TestingSubsystem ts = new TestingSubsystem("nse"); + + final NetworkSizeEstimation nse = new NetworkSizeEstimation(ts.getConfiguration()); + nse.subscribe(new NetworkSizeEstimation.Subscriber() { + @Override + public void update(AbsoluteTime timestamp, double estimate, double deviation) { + assertNotNull(timestamp); + gotResult.set(true); + nse.disconnect(); + } + }); + + Scheduler.run(); + + assertTrue(gotResult.get()); + } +} diff --git a/test/org/gnunet/statistics/StatisticsTest.java b/test/org/gnunet/statistics/StatisticsTest.java index ddba478..780a2e1 100644 --- a/test/org/gnunet/statistics/StatisticsTest.java +++ b/test/org/gnunet/statistics/StatisticsTest.java @@ -27,9 +27,6 @@ import org.gnunet.util.*; import org.junit.Test; public class StatisticsTest { - TestingSetup testing = new TestingSetup(); - - public interface Next { void next(); } @@ -64,7 +61,8 @@ public class StatisticsTest { @Test(timeout = 1000) public void test_simple() { - final TestingSubsystem ts = testing.startSubsystem("statistics"); + Program.configureLogging("DEBUG", null); + final TestingSubsystem ts = new TestingSubsystem("statistics"); final Statistics stat = new Statistics(ts.getConfiguration()); @@ -97,7 +95,7 @@ public class StatisticsTest { @Test(timeout = 1000) public void test_statistics_get_set() { - final TestingSubsystem ts = testing.startSubsystem("statistics"); + final TestingSubsystem ts = new TestingSubsystem("statistics"); final AssertionList assertions = new AssertionList(); @@ -149,7 +147,7 @@ public class StatisticsTest { @Test(timeout = 1000) public void test_watch() { - final TestingSubsystem ts = testing.startSubsystem("statistics"); + final TestingSubsystem ts = new TestingSubsystem("statistics"); final Wrapper updates = new Wrapper(0); diff --git a/test/org/gnunet/testing/TestingSetupTest.java b/test/org/gnunet/testing/TestingSetupTest.java index b23b543..2bfe0ae 100644 --- a/test/org/gnunet/testing/TestingSetupTest.java +++ b/test/org/gnunet/testing/TestingSetupTest.java @@ -29,8 +29,7 @@ public class TestingSetupTest { @Test(timeout = 1000) public void test_testing() { // could be any service, just use statistics - TestingSetup testing = new TestingSetup(); - TestingSubsystem ts = testing.startSubsystem("statistics"); + TestingSubsystem ts = new TestingSubsystem("statistics"); String port = ts.getConfiguration().getValueString("statistics", "PORT"); org.junit.Assert.assertTrue(port != null); @@ -39,16 +38,13 @@ public class TestingSetupTest { @Test(expected = TestingSetup.SetupException.class) public void test_no_service() { - TestingSetup testing = new TestingSetup(); - testing.startSubsystem("foobar _ !!!"); + new TestingSubsystem("foobar _ !!!"); } @Test(timeout = 1000) public void test_restart() { - // could be any service, just use statistics - TestingSetup testing = new TestingSetup(); - TestingSubsystem ts = testing.startSubsystem("statistics"); - //ts.restart(); + TestingSubsystem ts = new TestingSubsystem("statistics"); + ts.restart(); ts.destroy(); } } diff --git a/test/org/gnunet/util/ClientServerTest.java b/test/org/gnunet/util/ClientServerTest.java index f073a3c..f8b5d4e 100644 --- a/test/org/gnunet/util/ClientServerTest.java +++ b/test/org/gnunet/util/ClientServerTest.java @@ -1,10 +1,16 @@ package org.gnunet.util; +import com.google.common.collect.Lists; +import org.gnunet.construct.UInt32; +import org.gnunet.construct.UnionCase; import org.gnunet.testing.TestingServer; import org.gnunet.testing.TestingSetup; import org.junit.Assert; import org.junit.Test; +import java.net.*; +import java.util.ArrayList; + /** * ... * @@ -12,20 +18,26 @@ import org.junit.Test; */ public class ClientServerTest { + @Test + public void test_start_stop() { + Program.configureLogging("DEBUG", null); + final TestingServer srv = new TestingServer(); + srv.server.stopListening(); + } + /** * Test if the server receives a message sent by a client. */ - @Test(timeout = 1000) + @Test public void test_testing_server() { - final TestingSetup setup = new TestingSetup(); Program.configureLogging("DEBUG", null); - final TestingServer srv = setup.createServer(); + final TestingServer srv = new TestingServer(); final Wrapper gotMessage = new Wrapper(false); srv.server.setHandler(new Server.MessageRunabout() { - public void visit(TESTMessage tm) { + public void visit(TestMessage tm) { gotMessage.set(true); srv.server.destroy(); } @@ -39,7 +51,7 @@ public class ClientServerTest { @Override public void transmit(Connection.MessageSink sink) { System.out.println("ntr!"); - sink.send(new TESTMessage()); + sink.send(new TestMessage()); } @Override @@ -57,13 +69,12 @@ public class ClientServerTest { /** * Test what happens when a client calls notifyTransmitReady, but does not send - * a message in the callback but disconnects. + * a message in the callback and disconnects. */ @Test(timeout = 1000) public void test_premature_disconnect() { - final TestingSetup setup = new TestingSetup(); Program.configureLogging("DEBUG", null); - final TestingServer srv = setup.createServer(); + final TestingServer srv = new TestingServer(); srv.server.notifyDisconnect(new Server.DisconnectHandler() { @Override @@ -79,7 +90,7 @@ public class ClientServerTest { cli.notifyTransmitReady(RelativeTime.FOREVER,true, 0, new MessageTransmitter() { @Override public void transmit(Connection.MessageSink sink) { - sink.send(new TESTMessage()); + sink.send(new TestMessage()); cli.disconnect(); } @@ -91,4 +102,146 @@ public class ClientServerTest { } }); } + + + @Test + public void test_receiveDone() { + Program.configureLogging("DEBUG", null); + final TestingServer srv = new TestingServer(); + + final Wrapper msgCount = new Wrapper(0); + + srv.server.setHandler(new Server.MessageRunabout() { + public void visit(TestMessage tm) { + msgCount.set(msgCount.get() + 1); + if (msgCount.get() == 3) { + getSender().receiveDone(false); + srv.server.stopListening(); + } else { + getSender().receiveDone(true); + } + } + }); + + Scheduler.run(new Scheduler.Task() { + @Override + public void run(Scheduler.RunContext ctx) { + final Client cli = srv.createClient(); + + cli.transmitWhenReady(RelativeTime.FOREVER, new TestMessage(), new Continuation() { + @Override + public void cont(boolean success) { + cli.transmitWhenReady(RelativeTime.FOREVER, new TestMessage(), new Continuation() { + @Override + public void cont(boolean success) { + cli.transmitWhenReady(RelativeTime.FOREVER, new TestMessage(), null); + } + }); + } + }); + } + }); + } + + @Test + public void test_acceptFromAddresses() { + Program.configureLogging("DEBUG", null); + + InetAddress localhost = null; + try { + localhost = Inet4Address.getLocalHost(); + } catch (UnknownHostException e) { + Assert.fail(); + } + + // does this work on all operating systems? + SocketAddress addr = new InetSocketAddress(localhost, 0); + + Server server = new Server(Lists.newArrayList(addr), RelativeTime.FOREVER, false); + + server.destroy(); + + } + + + @Test + public void test_keep_drop() { + Program.configureLogging("DEBUG", null); + final TestingServer srv = new TestingServer(); + + final Wrapper msgCount = new Wrapper(0); + + + + srv.server.setHandler(new Server.MessageRunabout() { + public void visit(TestMessage tm) { + srv.server.stopListening(); + if (msgCount.get() == 0) { + getSender().keep(); + getSender().drop(); + getSender().receiveDone(true); + } else if (msgCount.get() == 1) { + getSender().receiveDone(false); + + } else { + Assert.fail(); + } + + msgCount.set(msgCount.get() + 1); + } + }); + + Scheduler.run(new Scheduler.Task() { + @Override + public void run(Scheduler.RunContext ctx) { + final Client cli = srv.createClient(); + cli.transmitWhenReady(new TestMessage(), new Continuation() { + @Override + public void cont(boolean success) { + cli.transmitWhenReady(new TestMessage(), null); + } + }); + } + }); + } + + + + /** + * test if markMonitor / soft shutdown works. + */ + @Test + public void test_monitor_clients() { + Program.configureLogging("DEBUG", null); + final TestingServer srv = new TestingServer(); + + final Wrapper msgCount = new Wrapper(0); + + srv.server.setHandler(new Server.MessageRunabout() { + public void visit(TestMessage tm) { + if (msgCount.get() == 0) { + getSender().markMonitor(); + getSender().receiveDone(true); + } else if (msgCount.get() == 1) { + srv.server.stopListening(); + getSender().receiveDone(false); + } else { + Assert.fail(); + } + + msgCount.set(msgCount.get() + 1); + } + }); + + Scheduler.run(new Scheduler.Task() { + @Override + public void run(Scheduler.RunContext ctx) { + final Client cli1 = srv.createClient(); + final Client cli2 = srv.createClient(); + + cli1.transmitWhenReady(new TestMessage(), null); + cli2.transmitWhenReady(new TestMessage(), null); + } + }); + } } diff --git a/test/org/gnunet/util/FilePipeExample.java b/test/org/gnunet/util/FilePipeExample.java index 4b72163..94696c9 100644 --- a/test/org/gnunet/util/FilePipeExample.java +++ b/test/org/gnunet/util/FilePipeExample.java @@ -13,8 +13,9 @@ import java.nio.ByteBuffer; public class FilePipeExample { public static void main(String... args) { + Program.configureLogging("DEBUG", null); - final Scheduler.FilePipe fp = Scheduler.createFilePipe(new File("test.pipe")); + final Scheduler.FilePipe fp = Scheduler.openFilePipe(new File("test.pipe")); Scheduler.addRead(RelativeTime.FOREVER, fp.getSource(), new Scheduler.Task() { diff --git a/test/org/gnunet/util/MeshTest.java b/test/org/gnunet/util/MeshTest.java new file mode 100644 index 0000000..919d812 --- /dev/null +++ b/test/org/gnunet/util/MeshTest.java @@ -0,0 +1,36 @@ +package org.gnunet.util; + +import org.gnunet.mesh.Mesh; +import org.gnunet.testing.TestingSubsystem; +import org.junit.Test; + +/** + * ... + * + * @author Florian Dold + */ +public class MeshTest { + + @Test + public void test_tunnel_create() { + /* + Program.configureLogging("DEBUG", null); + TestingSubsystem ts = new TestingSubsystem("mesh"); + + Mesh m = new Mesh(ts.getConfiguration(), null, null, new RunaboutMessageReceiver() { + public void visit(TestMessage tm) { + + } + @Override + public void handleError() { + + } + }); + + m.createTunnel(null, null); + + Sc + Sheduler.run(); + */ + } +} diff --git a/test/org/gnunet/util/ResolverTest.java b/test/org/gnunet/util/ResolverTest.java index 07f694a..ae56b79 100644 --- a/test/org/gnunet/util/ResolverTest.java +++ b/test/org/gnunet/util/ResolverTest.java @@ -43,9 +43,7 @@ public class ResolverTest { final Wrapper finished1 = new Wrapper(true); final Wrapper finished2 = new Wrapper(true); - TestingSetup testing = new TestingSetup(); - - TestingSubsystem ts = testing.startSubsystem("resolver"); + TestingSubsystem ts = new TestingSubsystem("resolver"); Resolver r = Resolver.getInstance(); diff --git a/test/org/gnunet/util/ServerExample.java b/test/org/gnunet/util/ServerExample.java index be74b61..a344e2a 100644 --- a/test/org/gnunet/util/ServerExample.java +++ b/test/org/gnunet/util/ServerExample.java @@ -23,7 +23,6 @@ package org.gnunet.util; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Arrays; -import java.util.LinkedList; import static org.gnunet.util.Server.*; @@ -43,13 +42,13 @@ public class ServerExample { RelativeTime.MINUTE, false); s.setHandler(new Server.MessageRunabout() { - public void visit(TESTMessage tm) { + public void visit(TestMessage tm) { System.out.println("got TEST message"); final Server.ClientHandle sender = getSender(); sender.notifyTransmitReady(4, RelativeTime.FOREVER, new MessageTransmitter() { @Override public void transmit(Connection.MessageSink sink) { - sink.send(new TESTMessage()); + sink.send(new TestMessage()); System.out.println("TEST message sent"); sender.receiveDone(true); } diff --git a/test/org/gnunet/util/StringsTest.java b/test/org/gnunet/util/StringsTest.java new file mode 100644 index 0000000..49cbfe9 --- /dev/null +++ b/test/org/gnunet/util/StringsTest.java @@ -0,0 +1,20 @@ +package org.gnunet.util; + +import org.junit.Assert; +import org.junit.Test; + +/** + * ... + * + * @author Florian Dold + */ +public class StringsTest { + @Test + public void test_inverse() { + byte[] data = "asdfgASDD$!123".getBytes(); + String str = Strings.dataToString(data); + byte[] data2 = Strings.stringToData(str, data.length); + Assert.assertArrayEquals(data, data2); + } +} + diff --git a/test/org/gnunet/util/getopt/GetoptTest.java b/test/org/gnunet/util/getopt/GetoptTest.java index 81a10a8..e60757e 100644 --- a/test/org/gnunet/util/getopt/GetoptTest.java +++ b/test/org/gnunet/util/getopt/GetoptTest.java @@ -25,8 +25,8 @@ import org.junit.Assert; import org.junit.Test; class Target { - @Option( - action = OptionAction.STORE_STRING, + @Argument( + action = ArgumentAction.STORE_STRING, shortname = "s", longname = "string", argumentName = "SOME_STRING", @@ -34,16 +34,16 @@ class Target { ) String someString; - @Option( - action = OptionAction.SET, + @Argument( + action = ArgumentAction.SET, shortname = "y", longname = "set", description = "enable, default disabled" ) boolean set = false; - @Option( - action = OptionAction.RESET, + @Argument( + action = ArgumentAction.RESET, shortname = "n", longname = "reset", description = "disable, default enabled" @@ -51,21 +51,20 @@ class Target { boolean reset = true; - @Option( - action = OptionAction.INCREMENT, - shortname = "i", - longname = "inc", - description = "increment a counter" - ) - int counter = 0; - - @Option( - action = OptionAction.STORE_NUMBER, + @Argument( + action = ArgumentAction.STORE_NUMBER, shortname = "w", longname = "value", description = "some value" ) int intVal = 0; + + static int someConstant = 42; +} + +class InvalidTarget { + @Argument(action = ArgumentAction.SET, shortname = "foo", longname = "bar", description = "bla bla") + boolean foo; } public class GetoptTest { @@ -74,12 +73,7 @@ public class GetoptTest { Target t = new Target(); Parser p = new Parser(t); - t.someString = null; - // argument after shortopt - p.parse(new String[]{"-s", "foo"}); - Assert.assertEquals("foo", t.someString); - t.someString = null; // argument directly with shortopt @@ -88,6 +82,12 @@ public class GetoptTest { t.someString = null; + // argument after shortopt + p.parse(new String[]{"-s", "foo"}); + Assert.assertEquals("foo", t.someString); + + t.someString = null; + p.parse(new String[]{"--string=foo"}); Assert.assertEquals("foo", t.someString); @@ -186,6 +186,21 @@ public class GetoptTest { Assert.assertArrayEquals(new String[]{"foo", "bar", "--reset", "baz"}, rest); } + + @Test(expected = Parser.ArgumentError.class) + public void test_missingLongopt() { + Target t = new Target(); + Parser p = new Parser(t); + p.parse(new String[]{"--foobar"}); + } + + @Test(expected = Parser.ArgumentError.class) + public void test_missingShortopt_1() { + Target t = new Target(); + Parser p = new Parser(t); + p.parse(new String[]{"-x"}); + } + @Test public void test_long() { Target t = new Target(); @@ -196,6 +211,9 @@ public class GetoptTest { rest = p.parse(new String[]{"-w5"}); Assert.assertEquals(5, t.intVal); + rest = p.parse(new String[]{"-w", "5"}); + Assert.assertEquals(5, t.intVal); + rest = p.parse(new String[]{"--value=6"}); Assert.assertEquals(6, t.intVal); @@ -213,4 +231,57 @@ public class GetoptTest { } Assert.assertTrue(thrown); } + + + @Test(expected = Parser.ArgumentError.class) + public void test_missingNumberArgument_short() { + Target t = new Target(); + Parser p = new Parser(t); + + p.parse(new String[]{"-w"}); + Assert.assertEquals(5, t.intVal); + } + + @Test(expected = Parser.ArgumentError.class) + public void test_missingNumberArgument_long() { + Target t = new Target(); + Parser p = new Parser(t); + + + p.parse(new String[]{"--w"}); + Assert.assertEquals(5, t.intVal); + } + + + @Test(expected = Parser.ArgumentError.class) + public void test_invalidNumberFormat() { + Target t = new Target(); + Parser p = new Parser(t); + + String[] rest; + + rest = p.parse(new String[]{"-w", "abc"}); + Assert.assertEquals(5, t.intVal); + } + + + @Test + public void test_dashRest() { + Target t = new Target(); + Parser p = new Parser(t); + + String[] rest; + + rest = p.parse(new String[]{"-w", "123", "-"}); + Assert.assertArrayEquals(new String[]{"-"}, rest); + } + + + @Test(expected = AssertionError.class) + public void test_invalid() { + InvalidTarget it = new InvalidTarget(); + Parser p = new Parser(it); + + p.parse(new String[]{"-foo"}); + } } diff --git a/test/org/nse/NSETest.java b/test/org/nse/NSETest.java deleted file mode 100644 index 47e4448..0000000 --- a/test/org/nse/NSETest.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - This file is part of GNUnet. - (C) 2011, 2012 Christian Grothoff (and other contributing authors) - - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, or (at your - option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. - */ - -package org.nse; - -import org.gnunet.nse.NetworkSizeEstimation; -import org.gnunet.testing.TestingSetup; -import org.gnunet.testing.TestingSubsystem; -import org.gnunet.util.AbsoluteTime; -import org.gnunet.util.Scheduler; -import org.gnunet.util.Wrapper; -import org.junit.Test; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -/** - * @author Florian Dold - */ -public class NSETest { - @Test - public void test_nse() { - final Wrapper gotResult = new Wrapper(false); - TestingSetup testing = new TestingSetup(); - TestingSubsystem ts = testing.startSubsystem("nse"); - - final NetworkSizeEstimation nse = new NetworkSizeEstimation(ts.getConfiguration()); - nse.subscribe(new NetworkSizeEstimation.Subscriber() { - @Override - public void update(AbsoluteTime timestamp, double estimate, double deviation) { - assertNotNull(timestamp); - gotResult.set(true); - nse.disconnect(); - } - }); - - Scheduler.run(); - - assertTrue(gotResult.get()); - } -} diff --git a/tools/build b/tools/build index 225be8e..998756d 100755 --- a/tools/build +++ b/tools/build @@ -12,6 +12,9 @@ set -e BASEDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"/.. +OUTDIR="$BASEDIR/build/" + + # collect all source files SOURCES_PROD=`find "$BASEDIR/src/" -name "*.java"` SOURCES_TEST=`find "$BASEDIR/test/" -name "*.java"` diff --git a/tools/coverage b/tools/coverage index 4834adc..8e5afce 100755 --- a/tools/coverage +++ b/tools/coverage @@ -6,9 +6,6 @@ INSTRUMENT_CMD="sh $BASEDIR/cobertura/cobertura-instrument.sh --datafile $BASEDI echo $INSTRUMENT_CMD -set -x - - case "$1" in instrument) echo "instrumenteing" -- cgit v1.2.3