gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r28880 - in gnunet-java: . gradle gradle/wrapper src src/ma


From: gnunet
Subject: [GNUnet-SVN] r28880 - in gnunet-java: . gradle gradle/wrapper src src/main src/main/java src/main/java/org src/main/java/org/gnunet src/main/java/org/gnunet/consensus src/main/java/org/gnunet/construct src/main/java/org/gnunet/construct/parsers src/main/java/org/gnunet/core src/main/java/org/gnunet/dht src/main/java/org/gnunet/hello src/main/java/org/gnunet/mesh src/main/java/org/gnunet/mq src/main/java/org/gnunet/nse src/main/java/org/gnunet/peerinfo src/main/java/org/gnunet/requests src/main/java/org/gnunet/statistics src/main/java/org/gnunet/testbed src/main/java/org/gnunet/testing src/main/java/org/gnunet/transport src/main/java/org/gnunet/util src/main/java/org/gnunet/util/getopt src/main/java/org/gnunet/voting src/main/java/org/gnunet/voting/simulation src/main/java/org/grothoff src/main/resources src/main/resources/org src/main/resources/org/gnunet src/main/resources/org/gnunet/construct src/test src/test/java src/test/java/org src/test/java/org/gnunet src/test/java/org/gn unet/con struct src/test/java/org/gnunet/core src/test/java/org/gnunet/dht src/test/java/org/gnunet/mesh src/test/java/org/gnunet/nse src/test/java/org/gnunet/peerinfo src/test/java/org/gnunet/statistics src/test/java/org/gnunet/testing src/test/java/org/gnunet/util src/test/java/org/gnunet/util/getopt src/test/java/org/grothoff
Date: Tue, 27 Aug 2013 19:16:18 +0200

Author: dold
Date: 2013-08-27 19:16:18 +0200 (Tue, 27 Aug 2013)
New Revision: 28880

Added:
   gnunet-java/gradle.properties
   gnunet-java/gradle/
   gnunet-java/gradle/wrapper/
   gnunet-java/gradle/wrapper/gradle-wrapper.jar
   gnunet-java/gradle/wrapper/gradle-wrapper.properties
   gnunet-java/gradlew
   gnunet-java/src/main/
   gnunet-java/src/main/java/
   gnunet-java/src/main/java/org/
   gnunet-java/src/main/java/org/gnunet/
   gnunet-java/src/main/java/org/gnunet/consensus/
   gnunet-java/src/main/java/org/gnunet/consensus/ConcludeCallback.java
   gnunet-java/src/main/java/org/gnunet/consensus/ConcludeDoneMessage.java
   gnunet-java/src/main/java/org/gnunet/consensus/ConcludeMessage.java
   gnunet-java/src/main/java/org/gnunet/consensus/Consensus.java
   gnunet-java/src/main/java/org/gnunet/consensus/ConsensusElement.java
   gnunet-java/src/main/java/org/gnunet/consensus/InsertDoneCallback.java
   gnunet-java/src/main/java/org/gnunet/consensus/InsertElementMessage.java
   gnunet-java/src/main/java/org/gnunet/consensus/NewElementCallback.java
   gnunet-java/src/main/java/org/gnunet/consensus/NewElementMessage.java
   gnunet-java/src/main/java/org/gnunet/construct/
   gnunet-java/src/main/java/org/gnunet/construct/Construct.java
   gnunet-java/src/main/java/org/gnunet/construct/DoubleValue.java
   gnunet-java/src/main/java/org/gnunet/construct/FillWith.java
   gnunet-java/src/main/java/org/gnunet/construct/FixedSizeArray.java
   gnunet-java/src/main/java/org/gnunet/construct/FixedSizeIntegerArray.java
   gnunet-java/src/main/java/org/gnunet/construct/FrameSize.java
   gnunet-java/src/main/java/org/gnunet/construct/Int16.java
   gnunet-java/src/main/java/org/gnunet/construct/Int32.java
   gnunet-java/src/main/java/org/gnunet/construct/Int64.java
   gnunet-java/src/main/java/org/gnunet/construct/Int8.java
   gnunet-java/src/main/java/org/gnunet/construct/IntegerFill.java
   gnunet-java/src/main/java/org/gnunet/construct/Message.java
   
gnunet-java/src/main/java/org/gnunet/construct/MessageIdAnnotationProcessor.java
   gnunet-java/src/main/java/org/gnunet/construct/MessageLoader.java
   gnunet-java/src/main/java/org/gnunet/construct/MessageUnion.java
   gnunet-java/src/main/java/org/gnunet/construct/MsgMap.txt
   gnunet-java/src/main/java/org/gnunet/construct/NestedMessage.java
   
gnunet-java/src/main/java/org/gnunet/construct/ProtocolViolationException.java
   gnunet-java/src/main/java/org/gnunet/construct/ReflectUtil.java
   gnunet-java/src/main/java/org/gnunet/construct/UInt16.java
   gnunet-java/src/main/java/org/gnunet/construct/UInt32.java
   gnunet-java/src/main/java/org/gnunet/construct/UInt64.java
   gnunet-java/src/main/java/org/gnunet/construct/UInt8.java
   gnunet-java/src/main/java/org/gnunet/construct/Union.java
   gnunet-java/src/main/java/org/gnunet/construct/UnionCase.java
   gnunet-java/src/main/java/org/gnunet/construct/VariableSizeArray.java
   gnunet-java/src/main/java/org/gnunet/construct/VariableSizeIntegerArray.java
   gnunet-java/src/main/java/org/gnunet/construct/ZeroTerminatedString.java
   gnunet-java/src/main/java/org/gnunet/construct/package-info.java
   gnunet-java/src/main/java/org/gnunet/construct/parsers/
   gnunet-java/src/main/java/org/gnunet/construct/parsers/DoubleParser.java
   gnunet-java/src/main/java/org/gnunet/construct/parsers/FillParser.java
   
gnunet-java/src/main/java/org/gnunet/construct/parsers/FixedSizeArrayParser.java
   
gnunet-java/src/main/java/org/gnunet/construct/parsers/FixedSizeIntegerArrayParser.java
   gnunet-java/src/main/java/org/gnunet/construct/parsers/IntegerFillParser.java
   gnunet-java/src/main/java/org/gnunet/construct/parsers/IntegerParser.java
   gnunet-java/src/main/java/org/gnunet/construct/parsers/IntegerUtil.java
   gnunet-java/src/main/java/org/gnunet/construct/parsers/NestedParser.java
   gnunet-java/src/main/java/org/gnunet/construct/parsers/Parser.java
   gnunet-java/src/main/java/org/gnunet/construct/parsers/SequenceParser.java
   gnunet-java/src/main/java/org/gnunet/construct/parsers/StringParser.java
   gnunet-java/src/main/java/org/gnunet/construct/parsers/UnionParser.java
   
gnunet-java/src/main/java/org/gnunet/construct/parsers/VariableSizeArrayParser.java
   
gnunet-java/src/main/java/org/gnunet/construct/parsers/VariableSizeIntegerArrayParser.java
   gnunet-java/src/main/java/org/gnunet/construct/parsers/package-info.java
   gnunet-java/src/main/java/org/gnunet/core/
   gnunet-java/src/main/java/org/gnunet/core/ConnectHandler.java
   gnunet-java/src/main/java/org/gnunet/core/ConnectNotifyMessage.java
   gnunet-java/src/main/java/org/gnunet/core/Core.java
   gnunet-java/src/main/java/org/gnunet/core/DisconnectHandler.java
   gnunet-java/src/main/java/org/gnunet/core/DisconnectNotifyMessage.java
   gnunet-java/src/main/java/org/gnunet/core/HeaderNotify.java
   gnunet-java/src/main/java/org/gnunet/core/InitCallback.java
   gnunet-java/src/main/java/org/gnunet/core/InitMessage.java
   gnunet-java/src/main/java/org/gnunet/core/InitReplyMessage.java
   gnunet-java/src/main/java/org/gnunet/core/MessageNotify.java
   gnunet-java/src/main/java/org/gnunet/core/NotifyInboundTrafficMessage.java
   gnunet-java/src/main/java/org/gnunet/core/NotifyOutboundTrafficMessage.java
   gnunet-java/src/main/java/org/gnunet/core/RequestIdentification.java
   gnunet-java/src/main/java/org/gnunet/core/SendMessage.java
   gnunet-java/src/main/java/org/gnunet/core/SendMessageReady.java
   gnunet-java/src/main/java/org/gnunet/core/SendMessageRequest.java
   gnunet-java/src/main/java/org/gnunet/core/package-info.java
   gnunet-java/src/main/java/org/gnunet/dht/
   gnunet-java/src/main/java/org/gnunet/dht/BlockType.java
   gnunet-java/src/main/java/org/gnunet/dht/ClientGetMessage.java
   gnunet-java/src/main/java/org/gnunet/dht/ClientGetStopMessage.java
   gnunet-java/src/main/java/org/gnunet/dht/ClientPutConfirmationMessage.java
   gnunet-java/src/main/java/org/gnunet/dht/ClientPutMessage.java
   gnunet-java/src/main/java/org/gnunet/dht/ClientResultMessage.java
   gnunet-java/src/main/java/org/gnunet/dht/DistributedHashTable.java
   gnunet-java/src/main/java/org/gnunet/dht/MonitorGetHandler.java
   gnunet-java/src/main/java/org/gnunet/dht/MonitorGetMessage.java
   gnunet-java/src/main/java/org/gnunet/dht/MonitorGetRespMessage.java
   gnunet-java/src/main/java/org/gnunet/dht/MonitorGetResponseHandler.java
   gnunet-java/src/main/java/org/gnunet/dht/MonitorPutHandler.java
   gnunet-java/src/main/java/org/gnunet/dht/MonitorPutMessage.java
   gnunet-java/src/main/java/org/gnunet/dht/MonitorStartStop.java
   gnunet-java/src/main/java/org/gnunet/dht/ResultCallback.java
   gnunet-java/src/main/java/org/gnunet/dht/RouteOption.java
   gnunet-java/src/main/java/org/gnunet/dht/package-info.java
   gnunet-java/src/main/java/org/gnunet/hello/
   gnunet-java/src/main/java/org/gnunet/hello/HelloMessage.java
   gnunet-java/src/main/java/org/gnunet/hello/package-info.java
   gnunet-java/src/main/java/org/gnunet/mesh/
   gnunet-java/src/main/java/org/gnunet/mesh/ClientConnectMessage.java
   gnunet-java/src/main/java/org/gnunet/mesh/ConnectHandler.java
   gnunet-java/src/main/java/org/gnunet/mesh/DataMessage.java
   gnunet-java/src/main/java/org/gnunet/mesh/DisconnectHandler.java
   gnunet-java/src/main/java/org/gnunet/mesh/InboundTunnelHandler.java
   gnunet-java/src/main/java/org/gnunet/mesh/LocalAckMessage.java
   gnunet-java/src/main/java/org/gnunet/mesh/Mesh.java
   gnunet-java/src/main/java/org/gnunet/mesh/MeshRunabout.java
   gnunet-java/src/main/java/org/gnunet/mesh/TunnelCreateMessage.java
   gnunet-java/src/main/java/org/gnunet/mesh/TunnelDestroyMessage.java
   gnunet-java/src/main/java/org/gnunet/mesh/TunnelEndHandler.java
   gnunet-java/src/main/java/org/gnunet/mesh/TunnelNotificationMessage.java
   gnunet-java/src/main/java/org/gnunet/mesh/package-info.java
   gnunet-java/src/main/java/org/gnunet/mq/
   gnunet-java/src/main/java/org/gnunet/mq/Envelope.java
   gnunet-java/src/main/java/org/gnunet/mq/MessageQueue.java
   gnunet-java/src/main/java/org/gnunet/mq/NotifySentHandler.java
   gnunet-java/src/main/java/org/gnunet/nse/
   gnunet-java/src/main/java/org/gnunet/nse/NetworkSizeEstimation.java
   gnunet-java/src/main/java/org/gnunet/nse/StartMessage.java
   gnunet-java/src/main/java/org/gnunet/nse/UpdateMessage.java
   gnunet-java/src/main/java/org/gnunet/nse/package-info.java
   gnunet-java/src/main/java/org/gnunet/peerinfo/
   gnunet-java/src/main/java/org/gnunet/peerinfo/InfoEnd.java
   gnunet-java/src/main/java/org/gnunet/peerinfo/InfoMessage.java
   gnunet-java/src/main/java/org/gnunet/peerinfo/ListAllPeersMessage.java
   gnunet-java/src/main/java/org/gnunet/peerinfo/ListPeerMessage.java
   gnunet-java/src/main/java/org/gnunet/peerinfo/PeerInfo.java
   gnunet-java/src/main/java/org/gnunet/peerinfo/PeerProcessor.java
   gnunet-java/src/main/java/org/gnunet/peerinfo/RsaPublicKeyBinaryEncoded.java
   gnunet-java/src/main/java/org/gnunet/peerinfo/package-info.java
   gnunet-java/src/main/java/org/gnunet/requests/
   gnunet-java/src/main/java/org/gnunet/requests/MatchingRequestContainer.java
   gnunet-java/src/main/java/org/gnunet/requests/RequestContainer.java
   gnunet-java/src/main/java/org/gnunet/requests/SequentialRequestContainer.java
   gnunet-java/src/main/java/org/gnunet/requests/package-info.java
   gnunet-java/src/main/java/org/gnunet/statistics/
   gnunet-java/src/main/java/org/gnunet/statistics/GetMessage.java
   gnunet-java/src/main/java/org/gnunet/statistics/GetRequest.java
   gnunet-java/src/main/java/org/gnunet/statistics/GetResponseEndMessage.java
   gnunet-java/src/main/java/org/gnunet/statistics/GetResponseMessage.java
   gnunet-java/src/main/java/org/gnunet/statistics/SetMessage.java
   gnunet-java/src/main/java/org/gnunet/statistics/SetRequest.java
   gnunet-java/src/main/java/org/gnunet/statistics/Statistics.java
   gnunet-java/src/main/java/org/gnunet/statistics/StatisticsReceiver.java
   gnunet-java/src/main/java/org/gnunet/statistics/StatisticsWatcher.java
   gnunet-java/src/main/java/org/gnunet/statistics/WatchMessage.java
   gnunet-java/src/main/java/org/gnunet/statistics/WatchRequest.java
   gnunet-java/src/main/java/org/gnunet/statistics/WatchResponseMessage.java
   gnunet-java/src/main/java/org/gnunet/statistics/package-info.java
   gnunet-java/src/main/java/org/gnunet/testbed/
   gnunet-java/src/main/java/org/gnunet/testbed/Controller.java
   gnunet-java/src/main/java/org/gnunet/testbed/ControllerEventCallback.java
   gnunet-java/src/main/java/org/gnunet/testbed/ControllerInitMessage.java
   gnunet-java/src/main/java/org/gnunet/testbed/ControllerProc.java
   gnunet-java/src/main/java/org/gnunet/testbed/ControllerStatusCallback.java
   gnunet-java/src/main/java/org/gnunet/testbed/HelperInitMessage.java
   gnunet-java/src/main/java/org/gnunet/testbed/HelperReplyMessage.java
   gnunet-java/src/main/java/org/gnunet/testbed/Host.java
   gnunet-java/src/main/java/org/gnunet/testbed/Operation.java
   gnunet-java/src/main/java/org/gnunet/testbed/OperationCompletionCallback.java
   gnunet-java/src/main/java/org/gnunet/testbed/Peer.java
   gnunet-java/src/main/java/org/gnunet/testbed/PeerChurnCallback.java
   gnunet-java/src/main/java/org/gnunet/testbed/PeerCreateCallback.java
   gnunet-java/src/main/java/org/gnunet/testing/
   gnunet-java/src/main/java/org/gnunet/testing/TestingFixture.java
   gnunet-java/src/main/java/org/gnunet/testing/TestingServer.java
   gnunet-java/src/main/java/org/gnunet/testing/TestingSetup.java
   gnunet-java/src/main/java/org/gnunet/testing/TestingSubsystem.java
   gnunet-java/src/main/java/org/gnunet/testing/package-info.java
   gnunet-java/src/main/java/org/gnunet/transport/
   gnunet-java/src/main/java/org/gnunet/transport/AddressIterateMessage.java
   gnunet-java/src/main/java/org/gnunet/transport/BlacklistCallback.java
   gnunet-java/src/main/java/org/gnunet/transport/BlacklistInitMessage.java
   gnunet-java/src/main/java/org/gnunet/transport/HelloUpdateCallback.java
   gnunet-java/src/main/java/org/gnunet/transport/PeerIterateCallback.java
   gnunet-java/src/main/java/org/gnunet/transport/RequestConnectMessage.java
   gnunet-java/src/main/java/org/gnunet/transport/StartMessage.java
   gnunet-java/src/main/java/org/gnunet/transport/Transport.java
   gnunet-java/src/main/java/org/gnunet/transport/TryConnectCallback.java
   gnunet-java/src/main/java/org/gnunet/util/
   gnunet-java/src/main/java/org/gnunet/util/ATSInformation.java
   gnunet-java/src/main/java/org/gnunet/util/AbsoluteTime.java
   gnunet-java/src/main/java/org/gnunet/util/AbsoluteTimeMessage.java
   gnunet-java/src/main/java/org/gnunet/util/Cancelable.java
   gnunet-java/src/main/java/org/gnunet/util/Client.java
   gnunet-java/src/main/java/org/gnunet/util/Configuration.java
   gnunet-java/src/main/java/org/gnunet/util/Connection.java
   gnunet-java/src/main/java/org/gnunet/util/Continuation.java
   gnunet-java/src/main/java/org/gnunet/util/GnunetMessage.java
   gnunet-java/src/main/java/org/gnunet/util/HashCode.java
   gnunet-java/src/main/java/org/gnunet/util/Helper.java
   gnunet-java/src/main/java/org/gnunet/util/MessageReceiver.java
   gnunet-java/src/main/java/org/gnunet/util/MessageTransmitter.java
   gnunet-java/src/main/java/org/gnunet/util/PeerIdentity.java
   gnunet-java/src/main/java/org/gnunet/util/Program.java
   gnunet-java/src/main/java/org/gnunet/util/RelativeTime.java
   gnunet-java/src/main/java/org/gnunet/util/RelativeTimeMessage.java
   gnunet-java/src/main/java/org/gnunet/util/Resolver.java
   gnunet-java/src/main/java/org/gnunet/util/RunaboutMessageReceiver.java
   gnunet-java/src/main/java/org/gnunet/util/RunaboutUtil.java
   gnunet-java/src/main/java/org/gnunet/util/Scheduler.java
   gnunet-java/src/main/java/org/gnunet/util/Server.java
   gnunet-java/src/main/java/org/gnunet/util/Service.java
   gnunet-java/src/main/java/org/gnunet/util/Strings.java
   gnunet-java/src/main/java/org/gnunet/util/TestMessage.java
   gnunet-java/src/main/java/org/gnunet/util/UnknownMessageBody.java
   gnunet-java/src/main/java/org/gnunet/util/getopt/
   gnunet-java/src/main/java/org/gnunet/util/getopt/Argument.java
   gnunet-java/src/main/java/org/gnunet/util/getopt/ArgumentAction.java
   gnunet-java/src/main/java/org/gnunet/util/getopt/Parser.java
   gnunet-java/src/main/java/org/gnunet/util/getopt/package-info.java
   gnunet-java/src/main/java/org/gnunet/util/package-info.java
   gnunet-java/src/main/java/org/gnunet/voting/
   gnunet-java/src/main/java/org/gnunet/voting/CertificateAuthorityService.java
   gnunet-java/src/main/java/org/gnunet/voting/ElectionCallTool.java
   gnunet-java/src/main/java/org/gnunet/voting/ElectionSpecification.java
   gnunet-java/src/main/java/org/gnunet/voting/TallyAuthorityService.java
   gnunet-java/src/main/java/org/gnunet/voting/VotingTool.java
   gnunet-java/src/main/java/org/gnunet/voting/simulation/
   gnunet-java/src/main/java/org/gnunet/voting/simulation/Authority.java
   gnunet-java/src/main/java/org/gnunet/voting/simulation/Ballot.java
   gnunet-java/src/main/java/org/gnunet/voting/simulation/BogusAuthority.java
   gnunet-java/src/main/java/org/gnunet/voting/simulation/CallForVoters.java
   gnunet-java/src/main/java/org/gnunet/voting/simulation/CryptoUtil.java
   gnunet-java/src/main/java/org/gnunet/voting/simulation/Cyphertext.java
   
gnunet-java/src/main/java/org/gnunet/voting/simulation/ElectionSupervisor.java
   gnunet-java/src/main/java/org/gnunet/voting/simulation/GroupPublicKey.java
   gnunet-java/src/main/java/org/gnunet/voting/simulation/TallyKeyShare.java
   
gnunet-java/src/main/java/org/gnunet/voting/simulation/TransmitShareVerification.java
   gnunet-java/src/main/java/org/gnunet/voting/simulation/Voter.java
   gnunet-java/src/main/java/org/gnunet/voting/simulation/VotingParameters.java
   gnunet-java/src/main/java/org/gnunet/voting/simulation/VotingSimulation.java
   gnunet-java/src/main/java/org/grothoff/
   gnunet-java/src/main/java/org/grothoff/Runabout.java
   gnunet-java/src/main/java/org/grothoff/package-info.java
   gnunet-java/src/main/resources/
   gnunet-java/src/main/resources/org/
   gnunet-java/src/main/resources/org/gnunet/
   gnunet-java/src/main/resources/org/gnunet/construct/
   gnunet-java/src/main/resources/org/gnunet/construct/MsgMap.txt
   gnunet-java/src/test/
   gnunet-java/src/test/java/
   gnunet-java/src/test/java/org/
   gnunet-java/src/test/java/org/gnunet/
   gnunet-java/src/test/java/org/gnunet/construct/
   gnunet-java/src/test/java/org/gnunet/construct/ByteFillMessage.java
   gnunet-java/src/test/java/org/gnunet/construct/ConstructTest.java
   gnunet-java/src/test/java/org/gnunet/construct/DoubleTest.java
   gnunet-java/src/test/java/org/gnunet/construct/FillParserTest.java
   gnunet-java/src/test/java/org/gnunet/construct/FixedSizeTest.java
   gnunet-java/src/test/java/org/gnunet/construct/FrameSizeTest.java
   gnunet-java/src/test/java/org/gnunet/construct/IntMessage.java
   gnunet-java/src/test/java/org/gnunet/construct/OptionalUnionTest.java
   gnunet-java/src/test/java/org/gnunet/construct/PrivateMemberMessage.java
   gnunet-java/src/test/java/org/gnunet/construct/QueryMessage.java
   gnunet-java/src/test/java/org/gnunet/construct/SendMessageTest.java
   gnunet-java/src/test/java/org/gnunet/construct/StringMessage.java
   gnunet-java/src/test/java/org/gnunet/construct/StringTest.java
   gnunet-java/src/test/java/org/gnunet/construct/StringTuple.java
   gnunet-java/src/test/java/org/gnunet/construct/VariableSizeArrayTest.java
   gnunet-java/src/test/java/org/gnunet/construct/VariableSizeMessage.java
   gnunet-java/src/test/java/org/gnunet/core/
   gnunet-java/src/test/java/org/gnunet/core/CoreTest.java
   gnunet-java/src/test/java/org/gnunet/dht/
   gnunet-java/src/test/java/org/gnunet/dht/DHTTest.java
   gnunet-java/src/test/java/org/gnunet/mesh/
   gnunet-java/src/test/java/org/gnunet/mesh/MeshTest.java
   gnunet-java/src/test/java/org/gnunet/nse/
   gnunet-java/src/test/java/org/gnunet/nse/NSETest.java
   gnunet-java/src/test/java/org/gnunet/peerinfo/
   gnunet-java/src/test/java/org/gnunet/peerinfo/PeerInfoTest.java
   gnunet-java/src/test/java/org/gnunet/statistics/
   gnunet-java/src/test/java/org/gnunet/statistics/StatisticsTest.java
   gnunet-java/src/test/java/org/gnunet/testing/
   gnunet-java/src/test/java/org/gnunet/testing/TestingSetupTest.java
   gnunet-java/src/test/java/org/gnunet/util/
   gnunet-java/src/test/java/org/gnunet/util/Assertion.java
   gnunet-java/src/test/java/org/gnunet/util/AssertionList.java
   gnunet-java/src/test/java/org/gnunet/util/ClientServerTest.java
   gnunet-java/src/test/java/org/gnunet/util/FilePipeExample.java
   gnunet-java/src/test/java/org/gnunet/util/ResolverTest.java
   gnunet-java/src/test/java/org/gnunet/util/ServerExample.java
   gnunet-java/src/test/java/org/gnunet/util/StringsTest.java
   gnunet-java/src/test/java/org/gnunet/util/TimeTest.java
   gnunet-java/src/test/java/org/gnunet/util/Wrapper.java
   gnunet-java/src/test/java/org/gnunet/util/getopt/
   gnunet-java/src/test/java/org/gnunet/util/getopt/GetoptTest.java
   gnunet-java/src/test/java/org/grothoff/
   gnunet-java/src/test/java/org/grothoff/RunaboutBenchmark.java
Removed:
   gnunet-java/src/org/
   gnunet-java/test/
Modified:
   gnunet-java/ISSUES
   gnunet-java/README
   gnunet-java/build.gradle
Log:
- adapted source tree structure to gradle/maven conventions
- added gradle wrapper
- fixes to adapt to GNUnet changes (new time unit, ...)
- helper process in util
- started implementing testbed
- skeleton for voting tools
- use new mq api
- implemented some more transport api
- mesh




Modified: gnunet-java/ISSUES
===================================================================
--- gnunet-java/ISSUES  2013-08-27 16:48:59 UTC (rev 28879)
+++ gnunet-java/ISSUES  2013-08-27 17:16:18 UTC (rev 28880)
@@ -1,16 +1,125 @@
-* after loads of nice-to-find bugs, i'm fairly confident consensus and set 
work ok now ...
-* gnunet-java needs some work / updating, but that's necessary for voting 
anyway
+* gnunet-testing-run-service merged into gnunet-testing
+ * the whole program runs without scheduler ... shouldn't be a problem, right?
+* i'm getting a lot of warnings from the autotools (newer version)
 
-* multihashmap_create (0) should be allowed ... hard to miss otherwise
 
-* testBED with java
+* probably found a bug in (openjdk7's) warning suppression
 
-for voting:
-* how is an 'election' created / published to other peers?
- * who's allowed to vote?
-* setting up the secret shared private key / cooperative decryption (service 
of its own?)
-* casting votes
-* tallying and getting the results
+* various issues with time unit change now fixed
+ * test cases timed out ... exponential backoff unit mismatch was the cause
 
-util-29321 INFO Accepting connection from `<unbound UNIX client>': 0xae2d00
-^^ why is the 'INFO'?
+
+* i found the "correct" way to handle junit4/scheduler: test teardown 
annotations (@After/@Before)
+ * tests using the scheduler should inherit org.gnunet.testing.TestingFixture
+ * other test cases are now unaffected
+
+* i experimented around with message / request queues
+ * everything now uses an mq/envelope mechanism very similar to GNUNET_MQ
+  * difference: in gnunet-java, client, mesh tunnel, etc. _are_ message queues
+ * there are request containers
+  * sequential (e.g. statistics set/get) and matching (stat. watch, core ntr)
+
+
+* for testing org.gnunet.mesh.Mesh:
+ * how should the peer's identity accessed from java?
+   GNUNET_CRYPTO_ecc_key_get_public in java?
+ * => java mesh api currently can't be tested without using Core
+      (mesh _used_ to allow peerid=0, but does not anymore)
+
+* core:
+ * the second test case seems to hang for a few seconds sometimes,
+   is this core's key generation?
+ * i just increased the test timeout, works fine no, works fine now ...
+
+
+* transport API:
+ * discuss transport api strangeness
+  * hellos are passed around as message headers in the C API (for 
"copyability"?), what should java do?
+   * GnunetMessage or HelloMessage?
+  * I get my own hello (for get_hello) only after I send init => bad?!
+  * currently, a transport handle is required for blacklist/active address 
query
+   * should there be a seperate 
org.gnunet.transport.(Blacklist|ActiveAddressQuery)?
+ * testing the blacklist api?
+
+
+statistics:
+ * queing etc. now implemented with mq / new requests api
+ * how do I test correct behavior on reconnect automatically?
+  * as statistics refuses to shut down when there's a (non-monitor) connection
+
+configuration:
+* what about newline in config options? they are written as "\\\n", right?
+
+testbed:
+
+
+habitability check ... what's the best way to do this in java?
+ * threads ....
+
+* starting a controller on a host updates the host's config?
+* why do i have to send the controller hostname when connecting to a 
controller?
+ * 
+in what extent does controller_disconnect block? (see doxygen comment)
+
+
+GNUNET_TESTBED_HelperInit has a very "nice" format, but consensus can handle 
it ...
+ * one string is zero terminated, the other not ...
+ * currently the non-zero-terminated string is just implemented as 
@VariableSizeIntegerArray
+  * do we need a @VariableSizeString?
+ * java's Deflater has the worst api imaginable ...
+
+* why is the host list an array?
+ * creating a host with a large (e.g. random) id creates huge array
+
+  while (id >= new_size)
+    new_size += HOST_LIST_GROW_STEP;
+  if (new_size != host_list_size)
+    GNUNET_array_grow (host_list, host_list_size, new_size);
+
+* gnunet-java has now org.gnunet.util.Helper, which is needed for testbed
+ * which is "simply" a message queue with some extra methods
+ * java async process IO _requires_ threading (or using e.g. the bloated 
apache commons)
+  * Scheduler now is (or should be ;) thread safe
+ * i'm not sure what to do about process stopping/killing in java
+  * only kill is implemented, i'm not sure stop() is possible in java
+
+
+* the latest version of cobertura is very large (~5MB)
+* discuss dependencies in general, discuss maven repositories and classpath 
with gradle repos
+ * developer can specfiy custom repositories in the build config
+  * e.g. when the jar is probably available in the local system
+ * see gradle showDeps for getting at the jar's locations
+ * could have a gradle copyDeps/installDeps
+ * make gradle create wrappers with correct classpath, either for development 
or
+   shipping
+
+* why not run multiple gnunet-java "applications" in one JVM?
+ * would require changes the the scheduler (non-static)
+ * handy for running larger testbeds and nice for command line tool startup 
times
+ * nailgun nails (nailgun is apache2 licensed)
+
+* changed some paths to be more compliant with java (=intellij,gradle,maven) 
conventions
+* the gradle wrapper is checked into svn (as recommended by gradle docs)
+
+* should voting stay in the main gnunet-java tree, or be an extension?
+ * being an extension would force me to maintain good compatibility of the
+   extension template
+
+
+voting
+ * what should be used for signatures, voter IDs?
+ * the voting ca could selectively deny voters their permission
+  * is this what we want to have "per default"?
+ * review VotingTool/ElectionCallTool
+
+
+TODO:
+request cancellation, request overlapping
+testbed
+review all existing gnj stuff
+voting
+gnj coverage
+merge testing tool
+fix sam cobertura
+
+

Modified: gnunet-java/README
===================================================================
--- gnunet-java/README  2013-08-27 16:48:59 UTC (rev 28879)
+++ gnunet-java/README  2013-08-27 17:16:18 UTC (rev 28880)
@@ -1,7 +1,7 @@
 Building gnunet-java
 ====================
 
-For building gnunet-java, gradle>=1.5 is required, see http://gradle.org/.
+For building gnunet-java, gradle>=1.7 is required, see http://gradle.org/.
 
 
 Extending gnunet-java

Modified: gnunet-java/build.gradle
===================================================================
--- gnunet-java/build.gradle    2013-08-27 16:48:59 UTC (rev 28879)
+++ gnunet-java/build.gradle    2013-08-27 17:16:18 UTC (rev 28880)
@@ -6,41 +6,29 @@
 
 buildDir = "$projectDir/build-gradle"
 
+// specify where to get jars
 repositories {
   flatDir {
     dirs 'lib'
   }
+  mavenCentral()
 }
 
 dependencies {
-  compile name: 'guava', version: '12.0'
-  compile name: 'junit', version: '4.10'
+  compile group: 'com.google.guava', name: 'guava', version: '14.0.1'
+  compile group: 'junit', name: 'junit', version: '4.11'
   compile name: 'log4j', version: '1.2.16'
   compile name: 'slf4j-api', version: '1.6.4'
   compile name: 'slf4j-log4j12', version: '1.6.4'
 }
 
-sourceSets {
-  main {
-    java {
-      srcDir 'src'
-    }
-    resources {
-      srcDir 'src'
-    }
-  }
-  test {
-    java {
-      srcDir 'test'
-    }
-    resources {
-      srcDir 'test'
-    }
-  }
+
+task showClasspath << {
+  System.out.println(configurations.compile.asPath);
 }
 
-task install (dependsOn: 'build', type: Copy) {
-
+task showDeps << {
+  configurations.compile.each { File file -> println file.getPath() }
 }
 
 
@@ -97,20 +85,26 @@
 
 reportCoverage.dependsOn testCoverage
 
+
+task createResourcesDir << {
+  def myDir = new File('src/main/resources')  
+  myDir.mkdirs()
+}
+
 /*
 TODO: should we really use the compile task for this?
 */
-task msgtypes (type: JavaCompile) {
+task msgtypes (type: JavaCompile, dependsOn: 'createResourcesDir') {
   description = "Updates the index of GNUnet message types known to 
gnunet-java."
   classpath = project.sourceSets.main.runtimeClasspath
   source = files(project.sourceSets.main.allJava)
   options.setCompilerArgs(["-processor", 
"org.gnunet.construct.MessageIdAnnotationProcessor",
                            "-proc:only",
-                           "-s", "src"])
+                           // generated "source" files should resources, tell 
the annotation processor!
+                           "-s", "src/main/resources"])
   destinationDir = file("$buildDir/classes/main/")
 }
 
-
 project.ext.installerFile = "$projectDir/gnunet-java-installer.jar"
 
 task installer (type: Exec) {
@@ -131,3 +125,15 @@
   }
   installer.commandLine "$izpack", "$projectDir/izpack-installer.xml", "-o", 
"$installerFile"
 }
+
+
+task wrapper(type: Wrapper) {
+  gradleVersion = '1.7'
+}
+
+if (hasProperty("xlint")) {
+  tasks.withType(Compile) {
+    options.compilerArgs << "-Xlint:unchecked"
+  }
+}
+

Added: gnunet-java/gradle/wrapper/gradle-wrapper.jar
===================================================================
(Binary files differ)

Index: gnunet-java/gradle/wrapper/gradle-wrapper.jar
===================================================================
--- gnunet-java/gradle/wrapper/gradle-wrapper.jar       2013-08-27 16:48:59 UTC 
(rev 28879)
+++ gnunet-java/gradle/wrapper/gradle-wrapper.jar       2013-08-27 17:16:18 UTC 
(rev 28880)

Property changes on: gnunet-java/gradle/wrapper/gradle-wrapper.jar
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Added: gnunet-java/gradle/wrapper/gradle-wrapper.properties
===================================================================
--- gnunet-java/gradle/wrapper/gradle-wrapper.properties                        
        (rev 0)
+++ gnunet-java/gradle/wrapper/gradle-wrapper.properties        2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,6 @@
+#Thu Aug 22 19:26:08 CEST 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.7-bin.zip

Added: gnunet-java/gradle.properties
===================================================================
--- gnunet-java/gradle.properties                               (rev 0)
+++ gnunet-java/gradle.properties       2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1 @@
+org.gradle.daemon=true

Added: gnunet-java/gradlew
===================================================================
--- gnunet-java/gradlew                         (rev 0)
+++ gnunet-java/gradlew 2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to 
pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 
'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" 
\"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### 
Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### 
Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" 
"$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" 
"$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" 
"$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the 
shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "address@hidden" -classpath "$CLASSPATH" 
org.gradle.wrapper.GradleWrapperMain "$@"


Property changes on: gnunet-java/gradlew
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Added: gnunet-java/src/main/java/org/gnunet/consensus/ConcludeCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/ConcludeCallback.java        
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/consensus/ConcludeCallback.java        
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,5 @@
+package org.gnunet.consensus;
+
+public interface ConcludeCallback {
+    void onConcludeDone();
+}

Added: gnunet-java/src/main/java/org/gnunet/consensus/ConcludeDoneMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/ConcludeDoneMessage.java     
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/consensus/ConcludeDoneMessage.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,13 @@
+package org.gnunet.consensus;
+
+
+import org.gnunet.construct.MessageUnion;
+import org.gnunet.construct.UnionCase;
+
+/**
+ * Notify the client that conclude has finished.
+ * Direction: service -> client
+ */
address@hidden(525)
+public class ConcludeDoneMessage implements MessageUnion {
+}

Added: gnunet-java/src/main/java/org/gnunet/consensus/ConcludeMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/ConcludeMessage.java         
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/consensus/ConcludeMessage.java 
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,19 @@
+package org.gnunet.consensus;
+
+import org.gnunet.construct.FillWith;
+import org.gnunet.construct.UInt16;
+import org.gnunet.construct.UInt8;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
+/**
+ * Notify the client of a new element.
+ *
+ * Direction: service -> client
+ *
+ * @author Florian Dold
+ */
address@hidden(524)
+public class ConcludeMessage implements GnunetMessage.Body {
+    /* empty body */
+}
\ No newline at end of file

Added: gnunet-java/src/main/java/org/gnunet/consensus/Consensus.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/Consensus.java               
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/consensus/Consensus.java       
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,128 @@
+package org.gnunet.consensus;
+
+import org.gnunet.mq.Envelope;
+import org.gnunet.mq.MessageQueue;
+import org.gnunet.mq.NotifySentHandler;
+import org.gnunet.util.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Multi-peer set reconciliation.
+ */
+public class Consensus {
+    /**
+     * Class logger.
+     */
+    private static final Logger logger = LoggerFactory
+            .getLogger(Consensus.class);
+
+    /**
+     * Callback for new elements arriving from the service.
+     * Also used to notify of consensus failure.
+     */
+    private final NewElementCallback newElementCallback;
+
+    /**
+     * Client connected to the consensus service.
+     */
+    private Client client;
+
+    /**
+     * Called when conclude has finished.
+     */
+    private ConcludeCallback concludeCallback;
+
+    /**
+     * Message dispatch for messages from the consensus service.
+     */
+    private class ConsensusMessageReceiver extends RunaboutMessageReceiver {
+        public void visit(ConcludeDoneMessage m) {
+            if (null == concludeCallback)
+            {
+                logger.error("unexpected conclude done message");
+                return;
+            }
+            concludeCallback.onConcludeDone();
+        }
+
+        public void visit(NewElementMessage m) {
+            ConsensusElement element = new ConsensusElement();
+            element.element_type = m.element_type;
+            element.data = m.element_data;
+            newElementCallback.onNewElement(element);
+        }
+
+        @Override
+        public void handleError() {
+            newElementCallback.onNewElement(null);
+        }
+    }
+
+    /**
+     * Create a consensus session.  The set being reconciled is initially
+     * empty.  Only reconcile with other peers after
+     * GNUNET_CONSENSUS_reconcile has been called.
+     *
+     * @param num_peers number of peers in the session
+     * @param peers array of peers participating in this consensus session
+     *              Inclusion of the local peer is optional.
+     * @param sessionId session identifier
+     *                   Allows a group of peers to have more than consensus 
session.
+     * @param newElementCallback callback, called when a new element is added 
to the set by
+     *                    another peer
+     */
+    public Consensus(Configuration cfg, int num_peers, PeerIdentity[] peers, 
HashCode sessionId,
+                     NewElementCallback newElementCallback) {
+        client = new Client("consensus", cfg);
+        client.installReceiver(new ConsensusMessageReceiver());
+        this.newElementCallback = newElementCallback;
+    }
+
+    /**
+     * Insert an element into the consensus set.
+     *
+     * @param element element to insert in the consnesus
+     * @param idc called when the element has been sent to the service
+     */
+    public void insertElement (ConsensusElement element, final 
InsertDoneCallback idc) {
+        InsertElementMessage m = new InsertElementMessage();
+        m.element_data = element.data;
+        m.element_type = element.element_type;
+        Envelope ev = new Envelope(m);
+        ev.notifySent(new NotifySentHandler() {
+            @Override
+            public void onSent() {
+                idc.onInsertDone();
+            }
+        });
+        client.send(ev);
+    }
+
+    /**
+     * We are done with inserting new elements into the consensus;
+     * try to conclude the consensus within a given time window.
+     * After conclude has been called, no further elements may be
+     * inserted by the client.
+     * @param concludeCallback called when the consensus has concluded
+     */
+    public void conclude(ConcludeCallback concludeCallback) {
+        if (null == concludeCallback)
+            throw new AssertionError("conclude with empty callback");
+        if (null != this.concludeCallback)
+            throw new AssertionError("called conclude twice");
+        this.concludeCallback = concludeCallback;
+        ConcludeMessage m = new ConcludeMessage();
+        client.send(m);
+    }
+
+    /**
+     * Destroy a consensus handle.
+     * Free all state associated with
+     * it, no longer call any of the callbacks.
+     */
+    public void destroy() {
+        client.disconnect();
+        client = null;
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/consensus/ConsensusElement.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/ConsensusElement.java        
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/consensus/ConsensusElement.java        
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,16 @@
+package org.gnunet.consensus;
+
+
+public class ConsensusElement {
+    /**
+     * Type of the element.
+     * 0 <= element_type <= 2^16
+     */
+    int element_type;
+
+    /**
+     * Data for the element.
+     * 0 <= data.length <= 2^16
+     */
+    byte[] data;
+}

Added: gnunet-java/src/main/java/org/gnunet/consensus/InsertDoneCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/InsertDoneCallback.java      
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/consensus/InsertDoneCallback.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,5 @@
+package org.gnunet.consensus;
+
+public interface InsertDoneCallback {
+    void onInsertDone();
+}

Added: gnunet-java/src/main/java/org/gnunet/consensus/InsertElementMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/InsertElementMessage.java    
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/consensus/InsertElementMessage.java    
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,23 @@
+package org.gnunet.consensus;
+
+import org.gnunet.construct.FillWith;
+import org.gnunet.construct.UInt16;
+import org.gnunet.construct.UInt8;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
+/**
+ * Send an element to the service, insert it into the consensus set.
+ *
+ * Direction: client -> service
+ *
+ * @author Florian Dold
+ */
address@hidden(521)
+public class InsertElementMessage implements GnunetMessage.Body {
+    @UInt16
+    public int element_type;
+    @FillWith
+    @UInt8
+    public byte[] element_data;
+}
\ No newline at end of file

Added: gnunet-java/src/main/java/org/gnunet/consensus/NewElementCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/NewElementCallback.java      
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/consensus/NewElementCallback.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,5 @@
+package org.gnunet.consensus;
+
+public interface NewElementCallback {
+    void onNewElement(ConsensusElement element);
+}

Added: gnunet-java/src/main/java/org/gnunet/consensus/NewElementMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/consensus/NewElementMessage.java       
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/consensus/NewElementMessage.java       
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,20 @@
+package org.gnunet.consensus;
+
+import org.gnunet.construct.*;
+import org.gnunet.util.GnunetMessage;
+
+/**
+ * Notify the client of a new element.
+ *
+ * Direction: service -> client
+ *
+ * @author Florian Dold
+ */
address@hidden(523)
+public class NewElementMessage implements GnunetMessage.Body {
+    @UInt16
+    public int element_type;
+    @FillWith
+    @UInt8
+    public byte[] element_data;
+}
\ No newline at end of file

Added: gnunet-java/src/main/java/org/gnunet/construct/Construct.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/Construct.java               
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/Construct.java       
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,482 @@
+/*
+ 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 org.gnunet.construct.parsers.*;
+import org.grothoff.Runabout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.nio.ByteBuffer;
+import java.util.*;
+
+
+/*
+Wanted syntax (not fully implemented yet)
+- @(U)Int<n> => signed or unsigned fixnum, represented by n bits
+- @NestedMessage => nested message
+- @FillWith @(U)Int<n> => fill the rest of the message with the specified 
fixnum, annotation valid on primitive arrays
+- @FillWith @NestedMessage => fill the rest of the message with the specified 
fixnum, annotation valid on message arrays
+ of the wanted type
+- @VariableSizeArray(lengthField = "<field>") => same syntax as @FillWith
+- @FixedSizeArray(length = n) => same syntax as @FillWith
+- @DoubleValue / @Float => floating point number, should also work with the 
array annotations
+- @FrameSize => specifies the fixnum that determines the containing frame's 
size
+- @ZeroTerminatedString => self-explanatory
+- @Constructable => annotation on a class that implements the 
ConstructableMessage interface,
+ providing methods to serialize/unserialize itself.
+*/
+
+
+/**
+ * Parse and write the binary representation of java classes, as defined by 
org.gnunet.construct.*-annotations
+ * on their members.
+ *
+ * @author Christian Grothoff
+ * @author Florian Dold
+ */
address@hidden("unchecked")
+public class Construct {
+    private static final Logger logger = LoggerFactory
+            .getLogger(Construct.class);
+
+
+    private static Map<Class<? extends Message>, Parser> parserCache = new 
HashMap<Class<? extends Message>,
+            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
+     * 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 extends Message> T parseAs(ByteBuffer srcBuf, Class<T> c) 
{
+        T m = ReflectUtil.justInstantiate(c);
+
+        try {
+            getParser(c).parse(srcBuf, 0, m, m, null);
+        } catch (ProtocolViolationException e) {
+            e.augmentPath("on " + c);
+        }
+
+        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 extends Message> T parseAs(byte[] srcBuf, Class<T> c) {
+        return parseAs(ByteBuffer.wrap(srcBuf), c);
+    }
+
+    /**
+     * Create a Parser for a sub-class of Message. The result is always cached.
+     *
+     * @param c annotated sub-class of message
+     * @return a parser
+     */
+    public static Parser getParser(Class<? extends Message> c) {
+
+        if (parserCache.containsKey(c)) {
+            return parserCache.get(c);
+        }
+
+        Parser p = getParser(c, new ParserGenerator());
+
+        parserCache.put(c, p);
+
+        return p;
+    }
+
+    private static List<Field> getMessageFields(Class c) {
+        LinkedList<Field> fields = new 
LinkedList<Field>(Arrays.asList(c.getDeclaredFields()));
+        while ((c = c.getSuperclass()) != null && 
Message.class.isAssignableFrom(c)) {
+            // fields of the superclass have to be parsed *before* the subclass
+            fields.addAll(0, Arrays.asList(c.getDeclaredFields()));
+        }
+        return fields;
+    }
+
+    private static Parser getParser(Class<? extends Message> c,
+                                    final ParserGenerator pg) {
+
+
+        SequenceParser parser = new SequenceParser();
+
+        if (!Modifier.isPublic(c.getModifiers())) {
+            throw new AssertionError(String.format("Construct Message %s not 
declared public", c));
+        }
+
+        for (Field f : getMessageFields(c)) {
+            pg.c = c;
+            Annotation[] as = f.getAnnotations();
+            if (as.length == 0 || f.isSynthetic() || 
Modifier.isStatic(f.getModifiers())) {
+                continue;
+            }
+            if (!Modifier.isPublic(f.getModifiers())) {
+                throw new AssertionError(String.format("Field %s of Message %s 
not declared public", f, c));
+            }
+            pg.field = f;
+            pg.annotations = as;
+            pg.annotationsIdx = 0;
+
+            pg.visitAppropriate(as[0]);
+
+            parser.add(pg.parser);
+        }
+
+        parser.setFrameSizePath(pg.frameSizePath);
+
+        return parser;
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    static class ParserGenerator extends Runabout {
+
+        // the field we are currently generating a parser for
+        Field field;
+        // all annotations on the field
+        Annotation[] annotations;
+        // the index of the annotation we are supposed to process right now
+        int annotationsIdx;
+
+        // the message class for which the parser is generated
+        Class c;
+
+        // the parser we are actually generating, used by the caller, set as
+        // return value of
+        // the runabout invocation
+        Parser parser;
+
+        // where are we currently, seen from the root message object
+        List<Field> path = new LinkedList<Field>();
+
+        // path of the object that has a frame size field
+        List<Field> frameSizePath;
+
+        private ParserGenerator() {
+        }
+
+        public void visit(Union u) {
+            parser = new UnionParser(u.optional(), (Class<MessageUnion>) 
field.getType(),
+                    ReflectUtil.getFieldPathFromString(u.tag(), c), field);
+        }
+
+        public void visit(FrameSize ts) {
+
+            frameSizePath = new LinkedList<Field>(path);
+            frameSizePath.add(field);
+
+            if (annotationsIdx != 0) {
+                throw new AssertionError(
+                        "FrameSize must be the first annotation on a Field");
+            }
+
+            annotationsIdx++;
+            if (annotationsIdx >= annotations.length) {
+                throw new AssertionError(
+                        "FrameSize must be followed by an numeric parser");
+            }
+            visitAppropriate(annotations[annotationsIdx]);
+
+        }
+
+        public void visit(UInt8 i) {
+            parser = new IntegerParser(1, IntegerParser.UNSIGNED, field);
+        }
+
+        public void visit(UInt16 i) {
+            parser = new IntegerParser(2, IntegerParser.UNSIGNED, field);
+        }
+
+        public void visit(UInt32 i) {
+            parser = new IntegerParser(4, IntegerParser.UNSIGNED, field);
+        }
+
+        public void visit(UInt64 i) {
+            parser = new IntegerParser(8, IntegerParser.UNSIGNED, field);
+        }
+
+        public void visit(Int8 i) {
+            parser = new IntegerParser(1, IntegerParser.SIGNED, field);
+        }
+
+        public void visit(Int16 i) {
+            parser = new IntegerParser(2, IntegerParser.SIGNED, field);
+        }
+
+        public void visit(Int32 i) {
+            parser = new IntegerParser(4, IntegerParser.SIGNED, field);
+        }
+
+        public void visit(Int64 i) {
+            parser = new IntegerParser(8, IntegerParser.SIGNED, field);
+        }
+
+
+        public void visit(ZeroTerminatedString zts) {
+            parser = new StringParser(zts.charset(), zts.optional(), field);
+        }
+
+        public void visit(IntegerFill i) {
+            parser = new IntegerFillParser(field, i.signed(), i.bitSize() / 8);
+        }
+
+        public void visit(NestedMessage n) {
+            if (!Message.class.isAssignableFrom(field.getType())) {
+                throw new AssertionError("@NestedMessage only works on 
messages, " + field.getType()
+                        + " is not a message (origin: " + c + ")");
+            }
+
+            Field nestedField = field;
+
+            if (n.newFrame()) {
+                Parser p = getParser((Class<Message>) nestedField.getType());
+
+                parser = new NestedParser(p, n.optional(), nestedField, true);
+
+            } else {
+                Field old_f = field;
+                List<Field> old_path = new ArrayList<Field>(path);
+                Class old_c = c;
+
+                path.add(field);
+
+                Parser p = getParser((Class<Message>) nestedField.getType(), 
this);
+
+                path = old_path;
+                c = old_c;
+
+                parser = new NestedParser(p, n.optional(), old_f, false);
+            }
+        }
+
+        public void visit(FixedSizeArray fsa) {
+            Field f = field;
+            int elemNumber = fsa.length();
+
+            getParser((Class<? extends Message>) field.getType()
+                    .getComponentType(), this);
+
+            parser = new FixedSizeArrayParser(elemNumber, parser, 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(DoubleValue d) {
+            if (!field.getType().equals(java.lang.Double.TYPE)) {
+                throw new AssertionError("@DoubleValue target must be a 
primitive 'double' field");
+            }
+            parser = new DoubleParser(field);
+        }
+
+        public void visit(FillWith fw) {
+            annotationsIdx++;
+            // if there's no further annotation, act like there is @Nested
+            if (annotationsIdx >= annotations.length) {
+                Parser p = getParser((Class<? extends Message>) 
field.getType().getComponentType());
+                parser = new FillParser(p, field);
+            } else {
+                FillWithParserRunabout r = new FillWithParserRunabout(field);
+                r.visitAppropriate(annotations[annotationsIdx]);
+                if (r.p == null) {
+                    throw new AssertionError();
+                }
+                parser = r.p;
+            }
+        }
+
+        public void visit(VariableSizeArray vsa) {
+            Parser p = getParser((Class<? extends Message>) 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);
+
+            } catch (SecurityException e) {
+                throw new AssertionError(
+                        String.format(
+                                "VariableSizeArray: length field '%s' not 
declared public",
+                                vsa.lengthField()));
+            } catch (NoSuchFieldException e) {
+                throw new AssertionError(String.format(
+                        "VariableSizeArray: length field '%s' does not exist 
in class %s",
+                        vsa.lengthField(), c));
+            }
+        }
+
+
+        public void visit(VariableSizeIntegerArray a) {
+            try {
+                parser = new 
VariableSizeIntegerArrayParser(c.getField(a.lengthField()), field, a.signed(), 
a.bitSize() / 8);
+            } catch (NoSuchFieldException e) {
+                throw new AssertionError(String.format(
+                        "VariableSizeIntegerArray: length field '%s' does not 
exist in class %s",
+                        a.lengthField(), c));
+            }
+        }
+
+        /*
+         * We override this to improve the error message, otherwise obfuscated 
by internal java proxy objects
+         */
+        @Override
+        public void visitDefault(Object obj) {
+            if (obj instanceof Annotation) {
+                Annotation ann = (Annotation) obj;
+                throw new AssertionError("invalid Construct annotation: " + 
ann.annotationType().getName());
+            } else {
+                throw new AssertionError();
+            }
+        }
+    }
+
+
+    private static class FillWithParserRunabout extends Runabout {
+        public Parser p;
+        private Field f;
+
+        public FillWithParserRunabout(Field f) {
+            this.f = f;
+        }
+
+        public void visit(Int8 x) {
+            p = new IntegerFillParser(f, true, 1);
+        }
+        public void visit(Int16 x) {
+            p = new IntegerFillParser(f, true, 2);
+        }
+        public void visit(Int32 x) {
+            p = new IntegerFillParser(f, true, 4);
+        }
+        public void visit(UInt8 x) {
+            p = new IntegerFillParser(f, false, 1);
+        }
+        public void visit(UInt16 x) {
+            p = new IntegerFillParser(f, false, 2);
+        }
+        public void visit(UInt32 x) {
+            p = new IntegerFillParser(f, false, 4);
+        }
+        public void visit(NestedMessage n) {
+            Parser componentParser = getParser((Class<? extends Message>) 
f.getType().getComponentType());
+            p = new FillParser(componentParser, f);
+        }
+
+    }
+
+    /**
+     * Serialize a given message object to a binary byte array. The fields of
+     * the object are expected to be annotated with annotations from the
+     * construct package.
+     *
+     * @param dstBuf where to write the binary object data
+     * @param msg    object to serialize
+     * @return number of bytes written to data, -1 on error
+     */
+    public static int write(ByteBuffer dstBuf, Message msg) {
+        Parser p = getParser(msg.getClass());
+        return p.write(dstBuf, msg);
+    }
+
+    /**
+     * Compute the exact size of a serialized message.
+     *
+     * @param m object to serialize
+     * @return number of bytes required to store the message in binary form
+     */
+    public static int getSize(Message m) {
+        if (m == null) {
+            return 0;
+        }
+        Parser p = getParser(m.getClass());
+        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);
+        write(buf, m);
+        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);
+    }
+
+    /**
+     * Get the minimum static size for the message, determinable even if the 
message's members
+     * are not filled in.
+     *
+     * @param m the message of interest
+     * @return the static minimum size of the message
+     */
+    public static int getStaticSize(Message m) {
+        Parser p = getParser(m.getClass());
+        return p.getStaticSize();
+    }
+
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/DoubleValue.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/DoubleValue.java             
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/DoubleValue.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,34 @@
+/*
+ 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;
+
+/**
+ * A number stored in the IEEE 754 double-precision binary floating-point 
format.
+ */
address@hidden(RetentionPolicy.RUNTIME)
address@hidden(ElementType.FIELD)
+public @interface DoubleValue {
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/FillWith.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/FillWith.java                
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/FillWith.java        
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,37 @@
+/*
+ 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 messages filling the rest of the frame
+ * 
+ * @author Florian Dold
+ * 
+ */
address@hidden(RetentionPolicy.RUNTIME)
address@hidden(ElementType.FIELD)
+public @interface FillWith {
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/FixedSizeArray.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/FixedSizeArray.java          
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/FixedSizeArray.java  
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,37 @@
+/*
+ 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 messages with static size.
+ * 
+ * @author Florian Dold
+ */
address@hidden(RetentionPolicy.RUNTIME)
address@hidden(ElementType.FIELD)
+public @interface FixedSizeArray {
+    int length();
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/FixedSizeIntegerArray.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/FixedSizeIntegerArray.java   
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/FixedSizeIntegerArray.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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
+ */
address@hidden(RetentionPolicy.RUNTIME)
address@hidden(ElementType.FIELD)
+public @interface FixedSizeIntegerArray {
+    int length();
+    int bitSize();
+    boolean signed();
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/FrameSize.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/FrameSize.java               
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/FrameSize.java       
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,34 @@
+/*
+ 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.*;
+
+/**
+ * Marker for the field storing the size of the enclosing frame in bytes.
+ * 
+ * @author Florian Dold
+ * 
+ */
address@hidden(RetentionPolicy.RUNTIME)
address@hidden(ElementType.FIELD)
+public @interface FrameSize {
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/Int16.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/Int16.java                   
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/Int16.java   2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,38 @@
+/*
+ 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;
+
+/**
+ * Signed 16-bit integer value.
+ * 
+ * @author Florian Dold
+ *
+ */
address@hidden(RetentionPolicy.RUNTIME)
address@hidden(ElementType.FIELD)
+public @interface Int16 {
+
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/Int32.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/Int32.java                   
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/Int32.java   2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,38 @@
+/*
+ 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;
+
+/**
+ * Signed 32-bit integer value.
+ * 
+ * @author Florian Dold
+ *
+ */
address@hidden(RetentionPolicy.RUNTIME)
address@hidden(ElementType.FIELD)
+public @interface Int32 {
+
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/Int64.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/Int64.java                   
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/Int64.java   2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,37 @@
+/*
+ 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;
+
+/**
+ * Signed 64-bit integer value.
+ *
+ */
+
address@hidden(RetentionPolicy.RUNTIME)
address@hidden(ElementType.FIELD)
+public @interface Int64 {
+
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/Int8.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/Int8.java                    
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/Int8.java    2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,40 @@
+/*
+ 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;
+
+
+/**
+ * Signed 8-bit integer value.
+ * 
+ * @author Florian Dold
+ *
+ */
+
address@hidden(RetentionPolicy.RUNTIME)
address@hidden(ElementType.FIELD)
+public @interface Int8 {
+
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/IntegerFill.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/IntegerFill.java             
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/IntegerFill.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,37 @@
+/*
+ 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 the rest of the message with integers of the specified kind. The 
annotation may only be present on the
+ * last serialized field of message.
+ */
address@hidden(RetentionPolicy.RUNTIME)
address@hidden(ElementType.FIELD)
+public @interface IntegerFill {
+    boolean signed();
+    int bitSize();
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/Message.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/Message.java                 
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/Message.java 2013-08-27 
17:16:18 UTC (rev 28880)
@@ -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.construct;
+
+/**
+ * Base interface for all messages (anything that 'Construct' can serialize or
+ * deserialize). Really just an annotation, but also for sanity checking by the
+ * compiler.
+ */
+public interface Message {
+}

Added: 
gnunet-java/src/main/java/org/gnunet/construct/MessageIdAnnotationProcessor.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/construct/MessageIdAnnotationProcessor.java
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/construct/MessageIdAnnotationProcessor.java
    2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,137 @@
+/*
+ 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 com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Table;
+
+import javax.annotation.processing.*;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+import javax.tools.Diagnostic;
+import javax.tools.FileObject;
+import javax.tools.StandardLocation;
+import java.io.IOException;
+import java.io.Writer;
+import java.lang.Integer;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+
+/**
+ * Creates a resource file 'MsgMap.txt' in the package 'org.gnunet.construct' 
of the source tree.
+ */
address@hidden("org.gnunet.construct.UnionCase")
address@hidden(SourceVersion.RELEASE_6)
+public class MessageIdAnnotationProcessor extends AbstractProcessor {
+    private final Table<String, Integer, String> idToMember = 
HashBasedTable.create();
+
+    @Override
+    public boolean process(Set<? extends TypeElement> typeElements, 
RoundEnvironment roundEnvironment) {
+        if (roundEnvironment.errorRaised()) {
+            return false;
+        }
+
+        Types types = processingEnv.getTypeUtils();
+        Elements elements = processingEnv.getElementUtils();
+
+        if (roundEnvironment.processingOver()) {
+            Filer filer = processingEnv.getFiler();
+            FileObject outfile;
+            try {
+                outfile = filer.createResource(StandardLocation.SOURCE_OUTPUT, 
"org.gnunet.construct", "MsgMap.txt");
+            } catch (IOException e) {
+                
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Could not 
create MsgMap.txt");
+                return false;
+            }
+
+            try {
+                Writer w = outfile.openWriter();
+                for (Table.Cell<String, Integer, String> cell : 
idToMember.cellSet()) {
+                    w.write(cell.getRowKey() + '|' + cell.getColumnKey() + '=' 
+ cell.getValue() + '\n');
+                }
+
+                DateFormat fmt = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+
+                w.write("# generated " + fmt.format(new Date()) + '\n');
+                w.close();
+            } catch (IOException e) {
+                
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Could not 
write MsgMap.txt");
+            }
+
+            processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, 
"message map written to " + outfile.toUri());
+
+        } else {
+            for (Element e : 
roundEnvironment.getElementsAnnotatedWith(UnionCase.class)) {
+                UnionCase ann = e.getAnnotation(UnionCase.class);
+                // get the uppermost parent class that implements 
MessageUnion. This is the union type.
+                // processingEnv.getElementUtils().
+                
//processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "element :" + 
e.toString());
+                List<? extends TypeMirror> parents = 
processingEnv.getTypeUtils().directSupertypes(e.asType());
+                TypeMirror msg = 
elements.getTypeElement("org.gnunet.construct.MessageUnion").asType();
+                TypeMirror unionInterface = null;
+                for (TypeMirror p : parents) {
+                    if (types.isSubtype(p, msg)) {
+                        unionInterface = p;
+                        break;
+                    }
+                }
+                if (unionInterface == null) {
+                    
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, String.format(
+                            "class %s annotated with @UnionCase does not 
implement an interface inheriting MessageUnion", e.getSimpleName()));
+                    return false;
+                }
+                String unionName = 
getClassName(types.asElement(unionInterface));
+                idToMember.put(unionName, ann.value(), getClassName(e));
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Get the fully qualified class name, where packages are seperated with 
'.', and
+     * inner classes are separated with '$'
+     *
+     * @param e the Element representing a class
+     * @return the fully qualified class name
+     */
+    private String getClassName(Element e) {
+
+        assert e.getKind().isClass();
+
+        String name = e.getSimpleName().toString();
+        String pkg = 
processingEnv.getElementUtils().getPackageOf(e).getQualifiedName().toString() + 
'.';
+
+        String outer = "";
+
+        while (((e = e.getEnclosingElement()) != null) && 
e.getKind().isClass()) {
+            outer = String.format("%s$%s", e.getSimpleName(), outer);
+        }
+
+        return pkg + outer + name;
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/MessageLoader.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/MessageLoader.java           
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/MessageLoader.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,215 @@
+/*
+ *
+ * This file is part of GNUnet.
+ * (C) 2011 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 2, 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 com.google.common.base.Charsets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * 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 {
+
+    }
+
+    /**
+     * Maps a class and tag to the corresponding union case.
+     * <p/>
+     * XXX: how much of generics is too much?
+     */
+    private static Map<Class<? extends MessageUnion>, Map<Integer, Class<? 
extends MessageUnion>>> unionmap
+            = new HashMap<Class<? extends MessageUnion>, Map<Integer, Class<? 
extends MessageUnion>>>(100);
+
+    /*
+     * Maps a union interface and union case to the corresponding tag.
+     */
+    private static Map<Class<? extends MessageUnion>, Map<Class<? extends 
MessageUnion>, Integer>> tagmap
+            = new HashMap<Class<? extends MessageUnion>, Map<Class<? extends 
MessageUnion>, Integer>>(100);
+
+
+    static {
+        ClassLoader classLoader = MessageLoader.class.getClassLoader();
+        Enumeration<URL> resources;
+        try {
+            resources = 
classLoader.getResources("org/gnunet/construct/MsgMap.txt");
+        } catch (IOException e) {
+            throw new RuntimeException("something went wrong with loading 
MsgMap.txt");
+        }
+
+        while (resources.hasMoreElements()) {
+            loadMessageMap(resources.nextElement());
+        }
+
+        if (tagmap.isEmpty()) {
+            logger.warn("message map empty");
+        }
+
+    }
+
+    public static void loadMessageMap(URL loc) {
+        if (loc == null) {
+            throw new RuntimeException("could not load message map");
+        }
+        BufferedReader in = null;
+        try {
+            in = new BufferedReader(new InputStreamReader(loc.openStream(), 
Charsets.UTF_8));
+            String line;
+            while ((line = in.readLine()) != null) {
+                // skip empty lines and comments
+                if (line.isEmpty() || line.charAt(0) == '#') {
+                    continue;
+                }
+                String[] m = line.split("=");
+                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]);
+                    logger.debug(m[1]);
+                    logger.debug("split in " + left.length);
+                    throw new RuntimeException("invalid message map format 
(left hand side)");
+                }
+                int id = java.lang.Integer.parseInt(left[1].trim());
+                String unionCaseName = m[1].trim();
+                String unionInterfaceName = left[0];
+
+                Class<? extends MessageUnion> unionInterface = 
loadClass(unionInterfaceName);
+                Class<? extends MessageUnion> unionCase = 
loadClass(unionCaseName);
+
+                if (!unionmap.containsKey(unionInterface)) {
+                    unionmap.put(unionInterface, new HashMap<Integer, Class<? 
extends MessageUnion>>(5));
+                }
+                unionmap.get(unionInterface).put(id, unionCase);
+
+
+                if (!tagmap.containsKey(unionInterface)) {
+                    tagmap.put(unionInterface, new HashMap<Class<? extends 
MessageUnion>, Integer>(5));
+                }
+                tagmap.get(unionInterface).put(unionCase, id);
+
+            }
+        } catch (IOException e) {
+            throw new RuntimeException("could not read message map");
+        } finally {
+            maybeClose(in);
+        }
+    }
+
+    private static void maybeClose(Closeable in) {
+        try {
+            if (in != null) {
+                in.close();
+            }
+        } catch (IOException e) {
+            throw new RuntimeException("error closing stream: " + 
e.getMessage());
+        }
+    }
+
+
+    @SuppressWarnings("unchecked")
+    private static Class<? extends MessageUnion> loadClass(String className) {
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        Class<MessageUnion> msgClass;
+        try {
+            msgClass = (Class<MessageUnion>) cl.loadClass(className);
+        } catch (ClassNotFoundException e) {
+            throw new AssertionError(String.format("message class '%s' not 
found in classpath", className));
+        } catch (ClassCastException e) {
+            throw new AssertionError(String.format("Class %s does not inherit 
from MessageUnion", className));
+        }
+        return msgClass;
+    }
+
+    public static Class<? extends MessageUnion> getUnionClass(Class<? extends 
MessageUnion> unionInterface, int tag) {
+        Map<Integer, Class<? extends MessageUnion>> map = 
unionmap.get(unionInterface);
+        if (map == null) {
+            throw new UnknownUnionException("don't know how to handle unions 
of type '" + unionInterface + "'");
+        }
+
+        Class<? extends MessageUnion> cls = map.get(tag);
+        if (cls == null) {
+            throw new ProtocolViolationException("don't know how to translate 
message of type " + tag);
+        }
+
+        return cls;
+    }
+
+
+    public static int getUnionTag(Class<? extends MessageUnion> 
unionInterface, Class<? extends MessageUnion> unionCase) {
+        Map<Class<? extends MessageUnion>, Integer> map = 
tagmap.get(unionInterface);
+        if (map == null) {
+            throw new AssertionError(String.format("%s is not a known union 
type", unionInterface));
+        }
+        if (!map.containsKey(unionCase)) {
+            throw new AssertionError(String.format("%s is not a known instance 
of %s", unionCase, unionInterface));
+        }
+        return map.get(unionCase);
+    }
+
+    public static void registerUnionCase(Class<? extends MessageUnion> 
unionInterface,
+                                         Class<? extends MessageUnion> 
unionCase, int tag) {
+        if (!unionmap.containsKey(unionInterface)) {
+            unionmap.put(unionInterface, new HashMap<Integer, Class<? extends 
MessageUnion>>(5));
+        }
+        unionmap.get(unionInterface).put(tag, unionCase);
+
+
+        if (!tagmap.containsKey(unionInterface)) {
+            tagmap.put(unionInterface, new HashMap<Class<? extends 
MessageUnion>, Integer>(5));
+        }
+        tagmap.get(unionInterface).put(unionCase, tag);
+
+
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/MessageUnion.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/MessageUnion.java            
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/MessageUnion.java    
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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.
+ */
+
+package org.gnunet.construct;
+
+/**
+ * Marker interface for message unions
+ */
+public interface MessageUnion extends Message {
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/MsgMap.txt
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/MsgMap.txt                   
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/MsgMap.txt   2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,46 @@
+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|274=org.gnunet.mesh.TunnelDestroyMessage
+org.gnunet.util.GnunetMessage$Body|1=org.gnunet.util.TestMessage
+org.gnunet.util.GnunetMessage$Body|70=org.gnunet.core.NotifyInboundTrafficMessage
+org.gnunet.util.GnunetMessage$Body|273=org.gnunet.mesh.TunnelCreateMessage
+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|64=org.gnunet.core.InitMessage
+org.gnunet.util.GnunetMessage$Body|4=org.gnunet.util.Resolver$GetMessage
+org.gnunet.util.GnunetMessage$Body|65=org.gnunet.core.InitReplyMessage
+org.gnunet.util.GnunetMessage$Body|5=org.gnunet.util.Resolver$ResolverResponse
+org.gnunet.util.GnunetMessage$Body|143=org.gnunet.dht.ClientGetMessage
+org.gnunet.util.GnunetMessage$Body|67=org.gnunet.core.ConnectNotifyMessage
+org.gnunet.util.GnunetMessage$Body|142=org.gnunet.dht.ClientPutMessage
+org.gnunet.util.GnunetMessage$Body|76=org.gnunet.core.SendMessage
+org.gnunet.util.GnunetMessage$Body|286=org.gnunet.mesh.LocalAckMessage
+org.gnunet.util.GnunetMessage$Body|74=org.gnunet.core.SendMessageRequest
+org.gnunet.util.GnunetMessage$Body|75=org.gnunet.core.SendMessageReady
+org.gnunet.util.GnunetMessage$Body|153=org.gnunet.dht.MonitorStartStop
+org.gnunet.util.GnunetMessage$Body|155=org.gnunet.dht.ClientPutConfirmationMessage
+org.gnunet.util.GnunetMessage$Body|323=org.gnunet.nse.UpdateMessage
+org.gnunet.util.GnunetMessage$Body|260=org.gnunet.mesh.DataMessage
+org.gnunet.util.GnunetMessage$Body|321=org.gnunet.nse.StartMessage
+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|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
+org.gnunet.util.GnunetMessage$Body|170=org.gnunet.statistics.GetResponseMessage
+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|374=org.gnunet.transport.RequestConnectMessage
+org.gnunet.util.GnunetMessage$Body|173=org.gnunet.statistics.WatchResponseMessage
+org.gnunet.util.GnunetMessage$Body|172=org.gnunet.statistics.WatchMessage
+org.gnunet.util.GnunetMessage$Body|524=org.gnunet.consensus.ConcludeMessage
+org.gnunet.util.GnunetMessage$Body|521=org.gnunet.consensus.InsertElementMessage
+org.gnunet.util.GnunetMessage$Body|523=org.gnunet.consensus.NewElementMessage
+org.gnunet.util.GnunetMessage$Body|360=org.gnunet.transport.StartMessage
+org.gnunet.construct.MessageUnion|525=org.gnunet.consensus.ConcludeDoneMessage
+# generated 2013/08/22 21:14:59

Added: gnunet-java/src/main/java/org/gnunet/construct/NestedMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/NestedMessage.java           
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/NestedMessage.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,39 @@
+/*
+ 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;
+
+/**
+ * Embed another constructable message.
+ * 
+ * @author Florian Dold
+ * 
+ */
address@hidden(RetentionPolicy.RUNTIME)
address@hidden(ElementType.FIELD)
+public @interface NestedMessage {
+    boolean newFrame() default false;
+    boolean optional() default false;
+}
\ No newline at end of file

Added: 
gnunet-java/src/main/java/org/gnunet/construct/ProtocolViolationException.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/construct/ProtocolViolationException.java  
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/construct/ProtocolViolationException.java  
    2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,40 @@
+/*
+ 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.util.LinkedList;
+
+/**
+ * Thrown when a received message is invalid.
+ * 
+ * @author Florian Dold
+ *
+ */
+public class ProtocolViolationException extends RuntimeException {
+    public ProtocolViolationException(String s) {
+        super(s);
+    }
+
+    public ProtocolViolationException augmentPath(String pathMessage) {
+        return new ProtocolViolationException(this.getMessage() + "\n" + 
pathMessage);
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/ReflectUtil.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/ReflectUtil.java             
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/ReflectUtil.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,297 @@
+/*
+ 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.Integer;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utilities for convenient use of the java reflection API.
+ * All methods only throw non-checked exceptions.
+ */
+public class ReflectUtil {
+    public static <T> T justInstantiate(Class<T> c) {
+        try {
+            return c.getConstructor().newInstance();
+        } catch (InstantiationException e) {
+            throw new AssertionError("Cannot instantiate " + c);
+        } catch (IllegalAccessException e) {
+            throw new AssertionError(
+                    String.format("Cannot instantiate Message %s (illegal 
access)", c));
+        } catch (NoSuchMethodException e) {
+            if (c.isMemberClass()) {
+                throw new AssertionError(String.format("Can not instantiate 
non-static member class %s", c));
+            } else {
+                throw new AssertionError(
+                        String.format("No suitable default constructor for 
class %s", c));
+            }
+        } catch (InvocationTargetException e) {
+            throw new AssertionError(
+                    String.format("Exception thrown while constructing object 
of class %s", c));
+        }
+    }
+
+    public static void justSetArray(Object arr, int i, long v) {
+        Class t = arr.getClass().getComponentType();
+        if (t.equals(Long.TYPE)) {
+            Array.setLong(arr, i, v);
+        } else if (t.equals(Integer.TYPE)) {
+            Array.setInt(arr, i, (int) v);
+        } else if (t.equals(Short.TYPE)) {
+            Array.setShort(arr, i, (short) v);
+        } else if (t.equals(Byte.TYPE)) {
+            Array.setByte(arr, i, (byte) v);
+        } else if (t.equals(Character.TYPE)) {
+            Array.setChar(arr, i, (char) v);
+        }
+    }
+
+    public static long justGetArrayLong(Object arr, int i) {
+        return Array.getLong(arr, i);
+    }
+
+    /**
+     * 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
+    }
+
+    /**
+     * Convenience wrapper for a field that stores a numeric value.
+     */
+    public static class NumField {
+        final private Field targetField;
+        final private NumFieldType targetType;
+        
+        
+        public NumFieldType getNumFieldType() {
+            return targetType;
+        }
+
+        public NumField(Field f) {
+            this.targetField = f;
+            if (f.getType().equals(Long.TYPE)) {
+                targetType = NumFieldType.LONG_PRIM;
+            } else if (f.getType().equals(Integer.TYPE)) {
+                targetType = NumFieldType.INT_PRIM;
+            } else if (f.getType().equals(Short.TYPE)) {
+                targetType = NumFieldType.SHORT_PRIM;
+            } else if (f.getType().equals(Byte.TYPE)) {
+                targetType = NumFieldType.BYTE_PRIM;
+            } else if (f.getType().equals(Character.TYPE)) {
+                targetType = NumFieldType.CHAR_PRIM;
+            } else if (f.getType().equals(BigInteger.class)) {
+                targetType = NumFieldType.BIGNUM;
+            } else {
+                throw new AssertionError(
+                        "expected numeric type, got: " + f.getType());
+            }
+        }
+
+        public void set(Object obj, long val) {
+            try {
+                switch (targetType) {
+                    case LONG_PRIM:
+                        targetField.setLong(obj, val);
+                        break;
+                    case INT_PRIM:
+                        targetField.setInt(obj, (int) val);
+                        break;
+                    case SHORT_PRIM:
+                        targetField.setShort(obj, (short) val);
+                        break;
+                    case BYTE_PRIM:
+                        targetField.setByte(obj, (byte) val);
+                        break;
+                    case CHAR_PRIM:
+                        targetField.setChar(obj, (char) val);
+                        break;
+                    case BIGNUM:
+                        targetField.set(obj, BigInteger.valueOf(val));
+                        break;
+                }
+            } catch (IllegalArgumentException e) {
+                throw new AssertionError("cannot access field");
+            } catch (IllegalAccessException e) {
+                throw new AssertionError("cannot access field");
+            }
+        }
+        
+        public void set(Object obj, BigInteger val) {
+            try {
+                targetField.set(obj, val);
+            } catch (IllegalAccessException e) {
+                throw new AssertionError("cannot access field");
+            }
+        }
+
+        public long get(Object obj) {
+            try {
+                switch (targetType) {
+                    case LONG_PRIM:
+                        return targetField.getLong(obj);
+                    case INT_PRIM:
+                        return targetField.getInt(obj);
+                    case SHORT_PRIM:
+                        return targetField.getShort(obj);
+                    case BYTE_PRIM:
+                        return targetField.getByte(obj);
+                    case CHAR_PRIM:
+                        return targetField.getChar(obj);
+                    case BIGNUM:
+                        throw new RuntimeException("get() called on NumField 
that is a BigInteger");
+                    default:
+                        throw new AssertionError("unreachable");
+                }
+            } catch (IllegalAccessException e) {
+                throw new AssertionError("cannot access field");
+            }
+        }
+        
+        public BigInteger getBig(Object obj) {
+            if (isBig()) {
+                return (BigInteger) justGet(obj, targetField);
+            } else {
+                return BigInteger.valueOf(this.get(obj));
+            }
+        }
+
+        public boolean isBig() {
+            return targetType.equals(NumFieldType.BIGNUM);
+        }
+    }
+
+
+    public static Object followFieldPath(List<Field> fl, Object obj,
+                                         int depth) {
+        for (int i = 0; i < depth; ++i) {
+            try {
+                obj = fl.get(i).get(obj);
+            } catch (IllegalArgumentException e) {
+                throw new RuntimeException(e);
+            } catch (IllegalAccessException e) {
+                throw new AssertionError("cannot access field " + fl.get(i)
+                        + " of " + obj.getClass());
+            }
+        }
+        return obj;
+    }
+
+    public static Object followFieldPath(List<Field> fl, Object obj) {
+        return followFieldPath(fl, obj, fl.size());
+    }
+
+    public static Object followFieldPathToParent(List<Field> fl, Object obj) {
+        return followFieldPath(fl, obj, fl.size() - 1);
+    }
+
+    public static Object justGet(Object obj, Field f) {
+        try {
+            return f.get(obj);
+        } catch (IllegalAccessException e) {
+            throw new AssertionError(
+                    String.format("Cannot access private field '%s' in class 
%s", f, obj.getClass()));
+        } catch (IllegalArgumentException e) {
+            throw new AssertionError("Cannot access field '" + f.getName() + 
"' in class " + obj.getClass());
+        }
+    }
+
+    public static void justSet(Object obj, Field f, Object val) {
+        try {
+            f.set(obj, val);
+        } catch (IllegalAccessException e) {
+            throw new AssertionError(
+                    String.format("Cannot access private field %s in class 
%s", f, obj.getClass()));
+        }
+    }
+
+
+    public static int justGetInt(Object obj, List<Field> path) {
+        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) {
+                throw new AssertionError("cannot access field " + path.get(i)
+                        + " of " + obj.getClass());
+            }
+        }
+
+        try {
+            return path.get(path.size() - 1).getInt(obj);
+        } catch (IllegalAccessException e) {
+            throw new AssertionError("cannot access field " + 
path.get(path.size() - 1)
+                    + " of " + obj.getClass());
+        }
+    }
+
+    public static void justSetInt(Object obj, List<Field> path, int val) {
+        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) {
+                throw new AssertionError("cannot access field " + path.get(i)
+                        + " of " + obj.getClass());
+            }
+        }
+
+        try {
+            path.get(path.size() - 1).setInt(obj, val);
+        } catch (IllegalAccessException e) {
+            throw new AssertionError("cannot access field " + 
path.get(path.size() - 1)
+                    + " of " + obj.getClass());
+        }
+    }
+
+
+    public static List<Field> getFieldPathFromString(final String p, final 
Class root) {
+        Class current = root;
+
+        String[] components = p.split("[.]");
+
+        List<Field> fp = new ArrayList<Field>(components.length);
+        for (String member : components) {
+            Field f;
+            try {
+                f = current.getField(member);
+            } catch (NoSuchFieldException e) {
+                throw new AssertionError("invalid field path, component " + 
member + " not found");
+            }
+
+            fp.add(f);
+
+            current = f.getType();
+        }
+
+        return fp;
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/UInt16.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/UInt16.java                  
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/UInt16.java  2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,40 @@
+/*
+ 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;
+
+
+/**
+ * Unsigned 16-bit integer value
+ * 
+ * @author Florian Dold
+ *
+ */
+
address@hidden(RetentionPolicy.RUNTIME)
address@hidden(ElementType.FIELD)
+public @interface UInt16 {
+
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/UInt32.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/UInt32.java                  
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/UInt32.java  2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,39 @@
+/*
+ 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;
+
+/**
+ * Unsigned 32-bit integer value
+ * 
+ * @author Florian Dold
+ *
+ */
+
address@hidden(RetentionPolicy.RUNTIME)
address@hidden(ElementType.FIELD)
+public @interface UInt32 {
+
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/UInt64.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/UInt64.java                  
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/UInt64.java  2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,39 @@
+/*
+ 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;
+
+/**
+ * Unsigned 64-bit integer value
+ * 
+ * @author Florian Dold
+ *
+ */
+
address@hidden(RetentionPolicy.RUNTIME)
address@hidden(ElementType.FIELD)
+public @interface UInt64 {
+
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/UInt8.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/UInt8.java                   
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/UInt8.java   2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,41 @@
+
+/*
+ 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;
+
+
+/**
+ * Unsigned 8-bit integer value.
+ * 
+ * @author Florian Dold
+ *
+ */
+
address@hidden(RetentionPolicy.RUNTIME)
address@hidden(ElementType.FIELD)
+public @interface UInt8 {
+
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/Union.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/Union.java                   
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/Union.java   2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,37 @@
+/*
+ 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;
+
+/**
+ * A field that stores a union, whose cases are discriminated by the field 
specified with address@hidden tag}.
+ */
address@hidden(ElementType.FIELD)
address@hidden(RetentionPolicy.RUNTIME)
+public @interface Union {
+    String tag();
+    boolean optional() default false;
+}
\ No newline at end of file

Added: gnunet-java/src/main/java/org/gnunet/construct/UnionCase.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/UnionCase.java               
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/UnionCase.java       
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,39 @@
+/*
+ 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;
+
+/**
+ * Annotation for messages that are a case of a union, with a distinct value 
to discriminate the case.
+ *
+ * Classes annotated by address@hidden UnionCase} should implement
+ * the marker interface for their respective union type.
+ */
address@hidden(RetentionPolicy.SOURCE)
address@hidden(ElementType.TYPE)
+public @interface UnionCase {
+    int value();
+}
\ No newline at end of file

Added: gnunet-java/src/main/java/org/gnunet/construct/VariableSizeArray.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/VariableSizeArray.java       
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/VariableSizeArray.java       
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,39 @@
+/*
+ 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 messages, where the length of the array is specified in a
+ * field of the containing message.
+ * 
+ * @author Florian Dold
+ * 
+ */
address@hidden(RetentionPolicy.RUNTIME)
address@hidden(ElementType.FIELD)
+public @interface VariableSizeArray {
+    String lengthField();
+}

Added: 
gnunet-java/src/main/java/org/gnunet/construct/VariableSizeIntegerArray.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/construct/VariableSizeIntegerArray.java    
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/construct/VariableSizeIntegerArray.java    
    2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,41 @@
+/*
+ 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 integers, where the length of the array is specified in a
+ * field of the containing message.
+ * 
+ * @author Florian Dold
+ * 
+ */
address@hidden(RetentionPolicy.RUNTIME)
address@hidden(ElementType.FIELD)
+public @interface VariableSizeIntegerArray {
+    String lengthField();
+    boolean signed();
+    int bitSize();
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/ZeroTerminatedString.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/ZeroTerminatedString.java    
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/ZeroTerminatedString.java    
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,37 @@
+/*
+ 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.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A zero-terminated string with the specified encoding.
+ * The default encoding is UTF-8.
+ * 
+ * @author Florian Dold
+ * 
+ */
address@hidden(RetentionPolicy.RUNTIME)
+public @interface ZeroTerminatedString {
+    String charset() default "UTF-8";
+    boolean optional() default false;
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/package-info.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/package-info.java            
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/package-info.java    
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,24 @@
+/*
+ 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.
+ */
+
+/**
+ * Write and read an object's binary representation, as determined by its 
members annotations.
+ */
+package org.gnunet.construct;

Added: gnunet-java/src/main/java/org/gnunet/construct/parsers/DoubleParser.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/parsers/DoubleParser.java    
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/parsers/DoubleParser.java    
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,75 @@
+/*
+ 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 java.lang.reflect.Field;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+public class DoubleParser implements Parser {
+    
+    private final Field targetField;
+    
+    public DoubleParser(Field f) {
+        targetField = f;
+    }
+
+    @Override
+    public int getSize(Message srcObj) {
+        return Double.SIZE / 8;
+    }
+
+    @Override
+    public int parse(ByteBuffer srcBuf, int frameOffset, Message frameObj, 
Message dstObj, List<Field> frameSizePath) {
+        double d = srcBuf.getDouble();
+        try {
+            targetField.setDouble(dstObj, d);
+        } catch (IllegalAccessException e) {
+            throw new AssertionError("cannot access field (should have been 
caught in Construct)");
+        }
+        return Double.SIZE / 8;
+    }
+
+    @Override
+    public int write(ByteBuffer dstBuf, Message srcObj) {
+        double d;
+        try {
+            d = targetField.getDouble(srcObj);
+        } catch (IllegalAccessException e) {
+            throw new AssertionError("field does not exist (should be caught 
in Construct)");
+        }
+        dstBuf.putDouble(d);
+        return 8;
+    }
+
+    @Override
+    public void patch(Message m, int frameSize, List<Field> frameSizePath, 
Message frameObj) {
+        // nothing to do here
+    }
+
+    @Override
+    public int getStaticSize() {
+        return Double.SIZE / 8;
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/parsers/FillParser.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/parsers/FillParser.java      
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/parsers/FillParser.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,126 @@
+/*
+ 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.ArrayList;
+import java.util.List;
+
+/**
+ * Parse an array that takes up all the available space.
+ *
+ * @author Florian Dold
+ */
+public class FillParser implements Parser {
+    private final Parser elemParser;
+
+    private final Field targetField;
+
+    public FillParser(Parser p, Field field) {
+        targetField = field;
+        elemParser = p;
+    }
+
+    @Override
+    public int getSize(final Message src) {
+        int size = 0;
+        final Object arr = ReflectUtil.justGet(src, targetField);
+
+        if (arr == null) {
+            throw new RuntimeException("array not initialized");
+        }
+
+        for (int i = 0; i < Array.getLength(arr); ++i) {
+            size += elemParser.getSize((Message) Array.get(arr, i));
+        }
+        return size;
+    }
+
+    @Override
+    public int parse(ByteBuffer srcBuf, int frameOffset,
+                     Message frameObj, final Message dstObj, List<Field> 
frameSizePath) {
+
+        if (frameSizePath == null) {
+            throw new AssertionError("FillParser expects a non-null 
frameSizePath. Does the message have a @FrameSizePath annotation?");
+        }
+
+
+
+        final int frameSize = ReflectUtil.justGetInt(dstObj, frameSizePath);
+        int remaining = frameOffset + frameSize - srcBuf.position();
+        int size = 0;
+
+        Class<?> elemType = targetField.getType().getComponentType();
+
+
+        ArrayList<Message> list = new ArrayList<Message>(10);
+
+        while (remaining > 0) {
+            @SuppressWarnings("unchecked")
+            Message next = ReflectUtil.justInstantiate((Class<Message>) 
targetField.getType().getComponentType());
+            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((Object[]) arr));
+        } catch (IllegalAccessException e) {
+            throw new AssertionError("cannot access field");
+        }
+
+        return size;
+    }
+
+    @Override
+    public int write(final ByteBuffer dstBuf, final Message src) {
+        int size = 0;
+        final Object arr = ReflectUtil.justGet(src, targetField);
+        for (int i = 0; i < Array.getLength(arr); ++i) {
+            size += elemParser.write(dstBuf, (Message) Array.get(arr, i));
+        }
+        return size;
+    }
+
+    @Override
+    public void patch(Message m, int frameSize, List<Field> frameSizePath, 
Message frameObj) {
+        if (frameSizePath == null) {
+            throw new AssertionError();
+        }
+        ReflectUtil.justSetInt(frameObj, frameSizePath, frameSize);
+
+        // todo: patch nested messages
+    }
+
+    @Override
+    public int getStaticSize() {
+        // not known
+        return 0;
+    }
+
+}

Added: 
gnunet-java/src/main/java/org/gnunet/construct/parsers/FixedSizeArrayParser.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/construct/parsers/FixedSizeArrayParser.java
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/construct/parsers/FixedSizeArrayParser.java
    2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,106 @@
+/*
+ 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 FixedSizeArrayParser implements Parser {
+
+    private final Parser elemParser;
+
+    private final Field targetField;
+
+    private final int elemNumber;
+
+    public FixedSizeArrayParser(final int elemNumber,
+                                final Parser elemParser, final Field f) {
+        targetField = f;
+        this.elemNumber = elemNumber;
+        this.elemParser = elemParser;
+    }
+
+    @Override
+    public int getSize(final Message srcObj) {
+        int size = 0;
+        final Object arr = ReflectUtil.justGet(srcObj, targetField);
+
+        if (arr == null) {
+            throw new RuntimeException("array not initialized");
+        }
+
+        for (int i = 0; i < Array.getLength(arr); ++i) {
+            size += elemParser.getSize((Message) Array.get(arr, i));
+        }
+        return size;
+    }
+
+    @Override
+    public int parse(ByteBuffer srcBuf, int frameOffset,
+                     Message frameObj, final Message dstObj, List<Field> 
frameSizePath) {
+        int size = 0;
+
+        final Object arr = 
Array.newInstance(targetField.getType().getComponentType(), elemNumber);
+        ReflectUtil.justSet(dstObj, targetField, arr);
+
+        for (int i = 0; i < elemNumber; ++i) {
+            @SuppressWarnings("unchecked")
+            Message elemObj = 
ReflectUtil.justInstantiate((Class<Message>)targetField.getType().getComponentType());
+            Array.set(arr, i, elemObj);
+
+            size += elemParser.parse(srcBuf, frameOffset - size, frameObj, 
elemObj, null);
+        }
+
+        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) {
+            size += elemParser.write(dstBuf, (Message) Array.get(arr, i));
+        }
+        return size;
+    }
+
+    @Override
+    public void patch(Message m, int frameSize, List<Field> frameSizePath, 
Message frameObj) {
+        final Object arr = ReflectUtil.justGet(m, targetField);
+        for (int i = 0; i < Array.getLength(arr); ++i) {
+            elemParser.patch((Message) Array.get(arr, i), frameSize, null, 
frameObj);
+        }
+    }
+
+    @Override
+    public int getStaticSize() {
+        return elemNumber * elemParser.getStaticSize();
+    }
+}

Added: 
gnunet-java/src/main/java/org/gnunet/construct/parsers/FixedSizeIntegerArrayParser.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/construct/parsers/FixedSizeIntegerArrayParser.java
                             (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/construct/parsers/FixedSizeIntegerArrayParser.java
     2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,106 @@
+/*
+ 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.ProtocolViolationException;
+import org.gnunet.construct.ReflectUtil;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.nio.BufferUnderflowException;
+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<Field> 
frameSizePath) {
+        int size = 0;
+
+        @SuppressWarnings("unchecked")
+        final Class<Message> arrayElementType = (Class<Message>) 
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;
+            try {
+                v = IntegerUtil.readLong(srcBuf, signed, byteSize);
+            } catch (BufferUnderflowException e) {
+                throw new ProtocolViolationException("fixed size array 
underflow: " + targetField.toString());
+            }
+            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<Field> frameSizePath, 
Message frameObj) {
+        // nothing to patch!
+    }
+
+    @Override
+    public int getStaticSize() {
+        return elemNumber * byteSize;
+    }
+}

Added: 
gnunet-java/src/main/java/org/gnunet/construct/parsers/IntegerFillParser.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/construct/parsers/IntegerFillParser.java   
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/construct/parsers/IntegerFillParser.java   
    2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,113 @@
+/*
+ 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 IntegerFillParser implements Parser {
+    private final Field targetField;
+    private final boolean signed;
+    private final int byteSize;
+
+    public IntegerFillParser(Field targetField,
+                             boolean signed, int byteSize) {
+
+        this.targetField = targetField;
+        this.signed = signed;
+        this.byteSize = byteSize;
+    }
+
+
+    @Override
+    public int getSize(Message srcObj) {
+        final Object arr = ReflectUtil.justGet(srcObj, targetField);
+
+        if (arr == null) {
+            throw new RuntimeException("array not initialized");
+        }
+
+        return byteSize * Array.getLength(arr);
+    }
+
+    @Override
+    public int parse(ByteBuffer srcBuf, int frameStart, Message frameObj, 
Message dstObj, List<Field> frameSizePath) {
+        if (frameSizePath == null) {
+            throw new AssertionError("IntegerFillParser expects a non-null 
frameSizePath. Did you specify a @FrameSize field?");
+        }
+        final int frameSize = ReflectUtil.justGetInt(frameObj, frameSizePath);
+        int remaining = frameStart + frameSize - srcBuf.position();
+
+        int elemNumber = remaining / byteSize;
+
+        @SuppressWarnings("unchecked")
+        final Class<Message> arrayElementType = (Class<Message>) 
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
+    public int write(ByteBuffer dstBuf, Message srcObj) {
+        final Object arr = ReflectUtil.justGet(srcObj, targetField);
+
+        if (arr == null) {
+            throw new RuntimeException("array not initialized");
+        }
+
+        for (int i = 0; i < Array.getLength(arr); ++i) {
+            IntegerUtil.writeLong(Array.getLong(arr, i), dstBuf, signed, 
byteSize);
+        }
+
+        return getSize(srcObj);
+    }
+
+    @Override
+    public void patch(Message m, int frameSize, List<Field> frameSizePath, 
Message frameObj) {
+        if (frameSizePath == null) {
+            throw new AssertionError("IntegerFillParser expects a non-null 
frameSizePath. Did you specify a @FrameSize field?");
+        }
+        ReflectUtil.justSetInt(frameObj, frameSizePath, frameSize);
+    }
+
+    @Override
+    public int getStaticSize() {
+        // not known
+        return 0;
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/parsers/IntegerParser.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/parsers/IntegerParser.java   
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/parsers/IntegerParser.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,95 @@
+/*
+ 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;
+
+/**
+ *
+ * todo: error checking on numeric overflow
+ */
+public class IntegerParser implements Parser {
+
+    public static final boolean UNSIGNED = false;
+    public static final boolean SIGNED = true;
+
+    private final int byteSize;
+
+    private final boolean isSigned;
+
+    private final ReflectUtil.NumField targetField;
+
+
+    public IntegerParser(final int byteSize, final boolean isSigned,
+                         final Field f) {
+        this.byteSize = byteSize;
+        this.isSigned = isSigned;
+
+        targetField = new ReflectUtil.NumField(f);
+    }
+
+    @Override
+    public int getSize(final Message srcObj) {
+        return byteSize;
+    }
+
+    @Override
+    public int parse(final ByteBuffer srcBuf, int frameOffset, Message 
frameObj, final Message dstObj, List<Field>
+            frameSizePath) {
+        if (targetField.isBig()) {
+            targetField.set(dstObj, IntegerUtil.readBigInteger(srcBuf, 
isSigned, byteSize));
+        } else {
+            targetField.set(dstObj, IntegerUtil.readLong(srcBuf, isSigned, 
byteSize));
+        }
+        return byteSize;
+    }
+
+    @Override
+    public int write(final ByteBuffer dstBuf, final Message srcObj) {
+        if (targetField.isBig()) {
+            IntegerUtil.writeBitInteger(targetField.getBig(srcObj), dstBuf, 
isSigned, byteSize);
+        } else {
+            // todo: error checking on numeric overflow, if requested
+            IntegerUtil.writeLong(targetField.get(srcObj), dstBuf, isSigned, 
byteSize);
+        }
+        return byteSize;
+    }
+
+    @Override
+    public void patch(Message m, int frameSize, List<Field> frameSizePath, 
Message frameObj) {
+        // todo: optimize this!
+        /*
+        if (frameSizePath != null) {
+            ReflectUtil.justSetInt(frameObj, frameSizePath, frameSize);
+        }
+        */
+    }
+
+    @Override
+    public int getStaticSize() {
+        return byteSize;
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/parsers/IntegerUtil.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/parsers/IntegerUtil.java     
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/parsers/IntegerUtil.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,92 @@
+/*
+ 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 java.math.BigInteger;
+import java.nio.ByteBuffer;
+
+public class IntegerUtil {
+    public static long readLong(ByteBuffer srcBuf, boolean isSigned, int 
byteSize) {
+        long val = 0;
+
+        final int first = srcBuf.position();
+        final int last = first + byteSize - 1;
+
+        // read all bytes except the last
+        while (srcBuf.position() != last) {
+            byte b = srcBuf.get();
+            // byte b may be signed, if so interpret it as unsigned byte; 
store it in an int
+            int s = b >= 0 ? b : (256 + b);
+
+            val |= s;
+            val <<= 8;
+        }
+
+        // read the last byte, we don't have to shift val after that
+        byte b = srcBuf.get();
+        int s = b >= 0 ? b : (256 + b);
+        val |= s;
+
+        if (isSigned) {
+            // explicitly OR sign bit to the right place if the source buffer 
is
+            // too large
+            long sign = (srcBuf.get(first) & 0x80);
+            val |= (sign << 7);
+        }
+
+        return val;
+    }
+
+    public static void writeLong(final long val, final ByteBuffer dstBuf, 
boolean isSigned, int byteSize) {
+        long myval = val;
+
+        // position of the last byte we are responsible to write
+        int last = dstBuf.position() + byteSize - 1;
+
+        while (last >= dstBuf.position()) {
+            dstBuf.put(last, (byte) (myval & 0xFF));
+            myval >>>= 8;
+            last -= 1;
+        }
+
+        if (isSigned) {
+            // a long has 8 bytes, shift by 7 bytes (non-arithmetically) to 
get the sign
+            byte sign = (byte) ((val >>> (7*8)) & 0x80);
+            // remove the sign bit from the buffer
+            dstBuf.put(dstBuf.position() + byteSize - 1, (byte) 
(dstBuf.get(dstBuf.position() + byteSize - 1) & ~sign));
+            // ... and put it in the right place (lowest byte)
+            dstBuf.put(dstBuf.position(), (byte) 
(dstBuf.get(dstBuf.position()) | sign));
+
+        }
+
+        dstBuf.position(dstBuf.position() + byteSize);
+    }
+
+
+    public static void writeBitInteger(BigInteger big, ByteBuffer dstBuf, 
boolean isSigned, int byteSize) {
+        throw new UnsupportedOperationException("not yet implemented");
+    }
+
+    public static BigInteger readBigInteger(final ByteBuffer srcBuf, boolean 
isSigned, int byteSize) {
+        throw new UnsupportedOperationException("not yet implemented");
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/parsers/NestedParser.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/parsers/NestedParser.java    
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/parsers/NestedParser.java    
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,119 @@
+/*
+ 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.ProtocolViolationException;
+import org.gnunet.construct.ReflectUtil;
+
+import java.lang.reflect.Field;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+
+public class NestedParser implements Parser {
+    private final Field targetField;
+
+    private final Parser nestedParser;
+    private boolean newFrame;
+
+    boolean optional;
+
+    public NestedParser(final Parser p, boolean optional, final Field f, 
boolean newFrame) {
+        targetField = f;
+        this.optional = optional;
+        this.nestedParser = p;
+        this.newFrame = newFrame;
+    }
+
+    @Override
+    public int getSize(final Message src) {
+        Message inner = (Message) ReflectUtil.justGet(src, targetField);
+        if (inner == null) {
+            if (optional)
+                return 0;
+            throw new AssertionError(String.format("empty non-optional nested 
message in field '%s'", targetField));
+        }
+        return nestedParser.getSize(inner);
+    }
+
+    @Override
+    public int parse(final ByteBuffer srcBuf, int frameOffset, Message 
frameObj, final Message dstObj, List<Field>
+            frameSizePath) {
+        if (newFrame) {
+            frameObj = dstObj;
+            frameOffset = 0;
+        }
+
+        if (optional) {
+            if (frameSizePath == null) {
+                throw new AssertionError("optional nested message needs 
@FrameSize");
+            }
+
+            int remaining = frameOffset + ReflectUtil.justGetInt(frameObj, 
frameSizePath) - srcBuf.position();
+            if (remaining < 0) {
+                throw new ProtocolViolationException("remaining size 
negative");
+            }
+            if (remaining == 0) {
+                if (!optional) {
+                    throw new ProtocolViolationException("not optional");
+                }
+                ReflectUtil.justSet(dstObj, targetField, null);
+                return 0;
+            }
+        }
+
+        ReflectUtil.justSet(dstObj, targetField, 
ReflectUtil.justInstantiate(targetField.getType()));
+
+        try {
+            return nestedParser.parse(srcBuf, frameOffset,
+                 frameObj, (Message) ReflectUtil.justGet(dstObj, targetField), 
frameSizePath);
+        } catch (ProtocolViolationException e) {
+            throw e.augmentPath("nested parser on " + targetField.toString());
+        }
+    }
+
+    @Override
+    public int write(final ByteBuffer dstBuf, final Message src) {
+        Object nestedMessage = ReflectUtil.justGet(src, targetField);
+        if (nestedMessage == null) {
+            return 0;
+        }
+        return nestedParser.write(dstBuf, (Message) nestedMessage);
+    }
+
+    @Override
+    public void patch(Message m, int frameSize, List<Field> frameSizePath, 
Message frameObj) {
+        Message nestedMessage = (Message) ReflectUtil.justGet(m, targetField);
+
+        if (newFrame) {
+            nestedParser.patch(nestedMessage, 
nestedParser.getSize(nestedMessage), null, nestedMessage);
+        } else {
+            nestedParser.patch(nestedMessage, frameSize, frameSizePath, 
frameObj);
+        }
+    }
+
+    @Override
+    public int getStaticSize() {
+        return nestedParser.getStaticSize();
+    }
+
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/parsers/Parser.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/parsers/Parser.java          
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/parsers/Parser.java  
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,78 @@
+/*
+ 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 java.lang.reflect.Field;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+
+public interface Parser {
+    /**
+     * Compute the exact size of the object's binary representation in bytes.
+     * 
+     * @param srcObj a message object with all fields filled out appropriately
+     * @return the exact size of the object's binary representation in bytes
+     */
+    public int getSize(Message srcObj);
+
+
+    /**
+     * Parse from a ByteBuffer into a destination object.
+     *
+     * @param srcBuf the buffer containing the binary data to construct this 
object
+     * @param frameStart start of the current frame, relative to the beginning 
of srcBuf
+     * @param frameObj the object containing the dstObj, dstObj if dstObj 
itself is the frame object
+     * @param dstObj the object whose members are written according according 
to the data in srcBuf
+     * @param frameSizePath
+     * @return number of byres read from srcBuf
+     */
+    public int parse(ByteBuffer srcBuf, int frameStart, Message frameObj, 
Message dstObj, List<Field> frameSizePath);
+
+    /**
+     * 
+     * @param dstBuf destination buffer for the binary representation of the 
object
+     * @param srcObj object to serialize to binary form
+     * @return number of bytes written to buf (todo: we are using a ByteBuffer 
now, this is obsolete)
+     */
+    public int write(ByteBuffer dstBuf, Message srcObj);
+
+    /**
+     * Parser-dependent method; sets members of the Message m (or Messages 
nested in m) which are
+     * values inferable by the parser.
+     * Examples: Union tags, size fields.
+     *
+     * @param m the message object to patch
+     * @param frameSize the size of the containing message
+     * @param frameSizePath
+     * @param frameObj the object containing the message (and possibly size 
fields)
+     */
+    public void patch(Message m, int frameSize, List<Field> frameSizePath, 
Message frameObj);
+
+    /**
+     * Return a lower bound for the size of the message in bytes
+     *
+     * @return minimum static size of the message in bytes
+     */
+    int getStaticSize();
+}

Added: 
gnunet-java/src/main/java/org/gnunet/construct/parsers/SequenceParser.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/parsers/SequenceParser.java  
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/parsers/SequenceParser.java  
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,106 @@
+/*
+ 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.ProtocolViolationException;
+import org.gnunet.construct.ReflectUtil;
+
+import java.lang.reflect.Field;
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A Sequence of Parsers that operate on the same object.
+ * @author Florian Dold
+ *
+ */
+public class SequenceParser implements Parser {
+
+    private final List<Parser> childParsers = new LinkedList<Parser>();
+    private List<Field> myFrameSizePath;
+
+    public SequenceParser() {
+    }
+
+    public void add(final Parser p) {
+        childParsers.add(p);
+    }
+
+    @Override
+    public int getSize(final Message src) {
+        int size = 0;
+        for (final Parser p : childParsers) {
+            size += p.getSize(src);
+        }
+        return size;
+    }
+
+    @Override
+    public int parse(final ByteBuffer srcBuf, int frameOffset,
+                     Message frameObj, final Message dst, List<Field> 
frameSizePath) {
+        int size = 0;
+        for (final Parser p : childParsers) {
+            try {
+                size += p.parse(srcBuf, frameOffset, frameObj, dst,
+                        frameSizePath == null ? myFrameSizePath : 
frameSizePath);
+            } catch (ProtocolViolationException e) {
+                throw e.augmentPath("(sequence parser)");
+            }
+        }
+        return size;
+    }
+
+    @Override
+    public int write(final ByteBuffer dstBuf, final Message src) {
+        int size = 0;
+        for (final Parser p : childParsers) {
+            size += p.write(dstBuf, src);
+        }
+        return size;
+    }
+
+    @Override
+    public void patch(Message m, int frameSize, List<Field> 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);
+        }
+    }
+
+    @Override
+    public int getStaticSize() {
+        int accum = 0;
+        for (Parser p : childParsers) {
+            accum += p.getStaticSize();
+        }
+        return accum;
+    }
+
+    public void setFrameSizePath(List<Field> frameSizePath) {
+        this.myFrameSizePath = frameSizePath;
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/parsers/StringParser.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/parsers/StringParser.java    
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/parsers/StringParser.java    
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,145 @@
+/*
+ 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.ProtocolViolationException;
+import org.gnunet.construct.ReflectUtil;
+
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Field;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+public class StringParser implements Parser {
+
+    private final String cset;
+    private final boolean optional;
+    private final Field targetField;
+
+    public StringParser(final String charset, boolean optional, final Field f) 
{
+        this.targetField = f;
+        this.optional = optional;
+        this.cset = charset;
+    }
+
+    @Override
+    public int getSize(final Message srcObj) {
+        final String s = (String) ReflectUtil.justGet(srcObj, targetField);
+        if (s == null) {
+            if (optional) {
+                return 0;
+            } else {
+                throw new AssertionError("non-optional string cannot be null");
+            }
+        }
+        try {
+            final byte[] b = s.getBytes(cset);
+            return b.length + 1;
+        } catch (final UnsupportedEncodingException e) {
+            throw new RuntimeException();
+        }
+    }
+
+    @Override
+    public int parse(final ByteBuffer srcBuf, int frameOffset, Message 
frameObj, final Message dstObj, List<Field>
+            frameSizePath) {
+
+        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;
+            }
+        }
+
+        int length = 0;
+
+        while (srcBuf.get(srcBuf.position() + length) != 0) {
+            length++;
+        }
+
+        final byte[] stringData = new byte[length];
+        
+        srcBuf.get(stringData);
+
+        if (srcBuf.get() != 0) {
+            throw new AssertionError("programming error");
+        }
+
+        String str;
+        try {
+            str = new String(stringData, cset);
+        } catch (final UnsupportedEncodingException e) {
+            throw new RuntimeException();
+        }
+
+        ReflectUtil.justSet(dstObj, targetField, str);
+
+        return length + 1;
+    }
+
+    @Override
+    public int write(final ByteBuffer dstBuf, final Message srcObj) {
+        String s = (String) ReflectUtil.justGet(srcObj, targetField);
+        
+        if (s == null) {
+            if (!optional) {
+                throw new AssertionError("non-optional string cannot be null");
+            }
+            return 0;
+        }
+        
+        byte[] b;
+        try {
+            b = s.getBytes(cset);
+        } catch (final UnsupportedEncodingException e) {
+            throw new RuntimeException();
+        }
+
+        dstBuf.put(b);
+        dstBuf.put((byte) 0);
+
+        // +1 for the 0-byte
+        return b.length + 1;
+    }
+
+    @Override
+    public void patch(Message m, int frameSize, List<Field> frameSizePath, 
Message frameObj) {
+        if (frameSizePath != null) {
+            ReflectUtil.justSetInt(frameObj, frameSizePath, frameSize);
+        }
+    }
+
+    @Override
+    public int getStaticSize() {
+        return optional ? 0 : 1;
+    }
+
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/parsers/UnionParser.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/parsers/UnionParser.java     
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/parsers/UnionParser.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,143 @@
+/*
+ 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.*;
+import org.gnunet.construct.ProtocolViolationException;
+
+import java.lang.reflect.Field;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+
+
+// unchecked casts are necessary
address@hidden("unchecked")
+public class UnionParser implements Parser {
+
+    private final Field targetField;
+
+    private final List<Field> unionTagPath;
+    private final ReflectUtil.NumField unionTagField;
+    private final Class<? extends MessageUnion> unionType;
+
+    boolean optional;
+
+    public UnionParser(boolean optional, Class<? extends MessageUnion> 
unionType,
+                       List<Field> unionTagPath, Field f) {
+        targetField = f;
+        this.optional = optional;
+        this.unionTagPath = unionTagPath;
+        this.unionTagField = new 
ReflectUtil.NumField(unionTagPath.get(unionTagPath.size() - 1));
+        this.unionType = unionType;
+    }
+
+    @Override
+    public int getSize(final Message src) {
+        Object target = ReflectUtil.justGet(src, targetField);
+        if (target == null) {
+            if (optional) {
+                return 0;
+            } else {
+                throw new AssertionError("non-optional union member must not 
be null");
+            }
+        }
+        Class cls = ReflectUtil.justGet(src, targetField).getClass();
+
+        Parser parser = Construct.getParser(cls);
+
+        return parser.getSize((Message)ReflectUtil.justGet(src, targetField));
+    }
+
+    @Override
+    public int parse(final ByteBuffer srcBuf, int frameOffset, Message 
frameObj, final Message dstObj, List<Field>
+            frameSizePath) {
+        if (optional) {
+            if (frameSizePath == null) {
+                throw new AssertionError("missing @FrameSize");
+            }
+
+            int remaining = frameOffset + ReflectUtil.justGetInt(frameObj, 
frameSizePath) - srcBuf.position();
+            if (remaining <= 0) {
+                if (!optional) {
+                    throw new ProtocolViolationException("not optional");
+                }
+                ReflectUtil.justSet(dstObj, targetField, null);
+                return 0;
+            }
+        }
+
+        long unionTag = 
unionTagField.get(ReflectUtil.followFieldPathToParent(unionTagPath, dstObj));
+
+        final Class cls;
+
+        cls = MessageLoader.getUnionClass(unionType, (int) unionTag);
+
+        ReflectUtil.justSet(dstObj, targetField, 
ReflectUtil.justInstantiate(cls));
+
+        final Message theUnion = (Message) ReflectUtil.justGet(dstObj, 
targetField);
+
+        Parser parser = Construct.getParser(cls);
+
+        return parser.parse(srcBuf, frameOffset, frameObj, theUnion, 
frameSizePath);
+    }
+
+    @Override
+    public int write(final ByteBuffer dstBuf, final Message src) {
+        final Object target = ReflectUtil.justGet(src, targetField);
+
+        if (target == null) {
+            if (optional) {
+                return 0;
+            } else {
+                throw new AssertionError("non-optional union member must not 
be null");
+            }
+        }
+
+        final Class currentUnionClass = target.getClass();
+        final Parser p = Construct.getParser(currentUnionClass);
+
+        return p.write(dstBuf, (Message) ReflectUtil.justGet(src, 
targetField));
+    }
+
+    @SuppressWarnings("unchecked")
+    public int getTag(Message m) {
+        return MessageLoader.getUnionTag(unionType, (Class<MessageUnion>) 
ReflectUtil.justGet(m, targetField).getClass());
+    }
+
+    @Override
+    public void patch(Message m, int frameSize, List<Field> frameSizePath, 
Message frameObj) {
+        final Class currentUnionClass = ReflectUtil.justGet(m, 
targetField).getClass();
+        final Parser p = Construct.getParser(currentUnionClass);
+
+        p.patch((Message) ReflectUtil.justGet(m, targetField), frameSize, 
frameSizePath, frameObj);
+        
+        unionTagField.set(ReflectUtil.followFieldPathToParent(unionTagPath, m),
+               getTag(m));
+    }
+
+    @Override
+    public int getStaticSize() {
+        // we can't say anything about the static size
+        // todo: in a more elaborate implementation, try all union members
+        return 0;
+    }
+}

Added: 
gnunet-java/src/main/java/org/gnunet/construct/parsers/VariableSizeArrayParser.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/construct/parsers/VariableSizeArrayParser.java
                         (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/construct/parsers/VariableSizeArrayParser.java
 2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,106 @@
+/*
+ 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.ProtocolViolationException;
+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 VariableSizeArrayParser implements Parser {
+    private final Field targetField;
+    private final Parser elemParser;
+    private ReflectUtil.NumField sizeField;
+
+
+    public VariableSizeArrayParser(final Parser elemParser, Field sizeField, 
Field arrayField) {
+        targetField = arrayField;
+        this.elemParser = elemParser;
+        this.sizeField = new ReflectUtil.NumField(sizeField);
+    }
+
+    @Override
+    public int getSize(final Message src) {
+        int size = 0;
+        final Object arr = ReflectUtil.justGet(src, targetField);
+
+        if (arr == null) {
+            throw new RuntimeException("array not initialized");
+        }
+
+        for (int i = 0; i < Array.getLength(arr); ++i) {
+            size += elemParser.getSize((Message) Array.get(arr, i));
+        }
+        return size;
+    }
+
+    @Override
+    public int parse(final ByteBuffer srcBuf, int frameOffset, Message 
frameObj, final Message dstObj, List<Field>
+            frameSizePath) {
+        final int elemNumber = (int) sizeField.get(dstObj);
+
+        @SuppressWarnings("unchecked")
+        final Class<Message> arrayElementType = (Class<Message>) 
targetField.getType().getComponentType();
+
+        int size = 0;
+
+        final Object arr = Array.newInstance(arrayElementType, elemNumber);
+        ReflectUtil.justSet(dstObj, targetField, arr);
+
+        for (int i = 0; i < elemNumber; ++i) {
+            Message elemObj;
+
+            elemObj = ReflectUtil.justInstantiate(arrayElementType);
+
+            Array.set(arr, i, elemObj);
+
+            size += elemParser.parse(srcBuf, frameOffset - size, null, 
elemObj, null);
+        }
+
+        return size;
+    }
+
+    @Override
+    public int write(final ByteBuffer dstBuf, final Message src) {
+        int size = 0;
+        final Object arr = ReflectUtil.justGet(src, targetField);
+        for (int i = 0; i < Array.getLength(arr); ++i) {
+            size += elemParser.write(dstBuf, (Message) Array.get(arr, i));
+        }
+        return size;
+    }
+
+    @Override
+    public void patch(Message m, int frameSize, List<Field> frameSizePath, 
Message frameObj) {
+        int size = Array.getLength(ReflectUtil.justGet(m, targetField));
+        sizeField.set(m, size);
+    }
+
+    @Override
+    public int getStaticSize() {
+        return 0;
+    }
+
+}

Added: 
gnunet-java/src/main/java/org/gnunet/construct/parsers/VariableSizeIntegerArrayParser.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/construct/parsers/VariableSizeIntegerArrayParser.java
                          (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/construct/parsers/VariableSizeIntegerArrayParser.java
  2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,103 @@
+/*
+ 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 VariableSizeIntegerArrayParser implements Parser {
+    private final Field targetField;
+    private ReflectUtil.NumField sizeField;
+    private int byteSize;
+    private boolean signed;
+
+
+    public VariableSizeIntegerArrayParser(Field sizeField, Field arrayField,
+                                          boolean signed, int byteSize) {
+        targetField = arrayField;
+        this.sizeField = new ReflectUtil.NumField(sizeField);
+        this.signed = signed;
+        this.byteSize = byteSize;
+    }
+
+    @Override
+    public int getSize(final Message src) {
+        final Object arr = ReflectUtil.justGet(src, targetField);
+
+        if (arr == null) {
+            throw new RuntimeException("array not initialized");
+        }
+
+        return Array.getLength(arr) * (byteSize);
+    }
+
+    @Override
+    public int parse(final ByteBuffer srcBuf, int frameOffset, Message 
frameObj, final Message dstObj, List<Field>
+            frameSizePath) {
+        final int elemNumber = (int) sizeField.get(dstObj);
+
+
+        @SuppressWarnings("unchecked")
+        final Class<Message> arrayElementType = (Class<Message>) 
targetField.getType().getComponentType();
+
+        if (!arrayElementType.isPrimitive()) {
+            throw new AssertionError("VariableSizeIntegerArray 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 byteSize * elemNumber;
+    }
+
+    @Override
+    public int write(final ByteBuffer dstBuf, final Message src) {
+        int size = 0;
+        final Object arr = ReflectUtil.justGet(src, targetField);
+        for (int i = 0; i < Array.getLength(arr); ++i) {
+            IntegerUtil.writeLong(ReflectUtil.justGetArrayLong(arr, i), 
dstBuf, signed, byteSize);
+            size += byteSize;
+        }
+        return size;
+    }
+
+    @Override
+    public void patch(Message m, int frameSize, List<Field> frameSizePath, 
Message frameObj) {
+        int size = Array.getLength(ReflectUtil.justGet(m, targetField));
+        sizeField.set(m, size);
+    }
+
+    @Override
+    public int getStaticSize() {
+        return 0;
+    }
+
+}

Added: gnunet-java/src/main/java/org/gnunet/construct/parsers/package-info.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/parsers/package-info.java    
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/construct/parsers/package-info.java    
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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;

Added: gnunet-java/src/main/java/org/gnunet/core/ConnectHandler.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/core/ConnectHandler.java               
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/core/ConnectHandler.java       
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,30 @@
+/*
+ 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.core;
+
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * Called when a new peer (with a compatible set of messages) connects to core
+ */
+public interface ConnectHandler {
+    void onConnect(PeerIdentity peerIdentity);
+}

Added: gnunet-java/src/main/java/org/gnunet/core/ConnectNotifyMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/core/ConnectNotifyMessage.java         
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/core/ConnectNotifyMessage.java 
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,54 @@
+/*
+ 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.core;
+
+import org.gnunet.construct.*;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * Message sent by the service to clients to notify them
+ * about a peer connecting.
+ */
address@hidden(67)
+public class ConnectNotifyMessage implements GnunetMessage.Body {
+    /**
+     * Number of ATS key-value pairs that follow this struct
+     * (excluding the 0-terminator).
+     */
+    @UInt32
+    public long atsCount;
+
+    /**
+     * Identity of the connecting peer.
+     */
+    @NestedMessage
+    public PeerIdentity peer;
+
+
+    @FillWith @UInt8
+    public byte[] atsInfo;
+
+
+    //@FillWith
+    //public ATSInformation[] atsInformation;
+
+}

Added: gnunet-java/src/main/java/org/gnunet/core/Core.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/core/Core.java                         
(rev 0)
+++ gnunet-java/src/main/java/org/gnunet/core/Core.java 2013-08-27 17:16:18 UTC 
(rev 28880)
@@ -0,0 +1,347 @@
+/*
+ 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.core;
+
+import com.google.common.collect.Maps;
+import org.gnunet.construct.Construct;
+import org.gnunet.construct.MessageLoader;
+import org.gnunet.mq.Envelope;
+import org.gnunet.requests.MatchingRequestContainer;
+import org.gnunet.requests.RequestContainer;
+import org.gnunet.util.*;
+import org.grothoff.Runabout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+
+
+/**
+ * API for the gnunet core service.
+ * <p/>
+ * Sends messages to connected peers.
+ */
+public class Core {
+    /**
+     * Logger for org.gnunet.Core.
+     */
+    private static final Logger logger = LoggerFactory
+            .getLogger(Core.class);
+
+    /**
+     * Client for connecting to the core service
+     */
+    private final Client client;
+
+    /*
+     * set to null once connected for the first time
+     */
+    private InitCallback initCallback;
+
+    /*
+     * Callback for traffic notifications. null if not interested.
+     */
+    private HeaderNotify notifyOutboundHeaders;
+
+    /*
+     * Callback for traffic notifications. null if not interested.
+     */
+    private HeaderNotify notifyInboundHeaders;
+
+    /*
+     * Callback for traffic notifications. null if not interested.
+     */
+    private MessageNotify notifyOutboundMessages;
+
+    /*
+     * Callback for traffic notifications. null if not interested.
+     */
+    private MessageNotify notifyInboundMessages;
+
+    /*
+     * Callbacks for connect events
+     */
+    private ConnectHandler connectHandler;
+
+    /**
+     * Callback for disconnect events.
+     */
+    private DisconnectHandler disconnectHandler;
+
+    /**
+     * Messages we are interested in.
+     * Per default we are interested in all messages => specific interest set 
is empty.
+     */
+    private int[] interested = new int[0];
+
+    /**
+     * Handler for the messages we are interested in.
+     */
+    private Runabout messageHandler;
+
+    /**
+     * Peers that we were notified about being connected to them.
+     * Every connected peer is mapped to a generator for unique request IDs.
+     */
+    private HashMap<PeerIdentity, Integer> connectedPeers = Maps.newHashMap();
+
+    /**
+     * Request container for notify transmit requests.
+     */
+    private MatchingRequestContainer<RequestIdentification, 
NotifyTransmitReadyRequest> ntr_requests;
+
+    public static class NotifyTransmitReadyRequest extends 
RequestContainer.Request {
+        private final int size;
+        final public PeerIdentity target;
+        final public long priority;
+        public int smrId;
+        final public MessageTransmitter transmitter;
+        final public AbsoluteTime deadline;
+
+        public NotifyTransmitReadyRequest(int priority, int size, PeerIdentity 
target, RelativeTime timeout, MessageTransmitter transmitter) {
+            this.deadline = timeout.toAbsolute();
+            this.priority = priority;
+            this.size = size;
+            this.target = target;
+            this.transmitter = transmitter;
+        }
+
+        @Override
+        public Envelope assembleRequest() {
+            SendMessageRequest m = new SendMessageRequest();
+            m.peer = target;
+            m.smrId = smrId;
+            m.priority = priority;
+            m.size = size;
+            m.deadline = deadline.asMessage();
+            return new Envelope(m);
+        }
+
+        public void cancel() {
+            // do nothing
+        }
+    }
+
+
+    public final class CoreReceiver extends RunaboutMessageReceiver {
+        public void visit(InitReplyMessage m) {
+            PeerIdentity myIdentity = m.myIdentity;
+            connectedPeers.put(myIdentity, 1);
+
+            if (initCallback != null) {
+                initCallback.onInit(m.myIdentity);
+                initCallback = null;
+            }
+        }
+
+        public void visit(ConnectNotifyMessage m) {
+            if (connectHandler != null) {
+                connectHandler.onConnect(m.peer);
+            }
+        }
+
+        public void visit(DisconnectNotifyMessage m) {
+            if (disconnectHandler != null) {
+                disconnectHandler.onDisconnect(m.peer);
+            }
+        }
+
+        public void visit(NotifyInboundTrafficMessage m) {
+            boolean found = false;
+            if (notifyInboundHeaders != null) {
+                notifyInboundHeaders.notify(m.payloadHeader);
+            }
+            if (notifyInboundMessages != null) {
+                // todo: call corresponding notify on notifyInboundMessages
+            }
+
+            for (int i : interested) {
+                if (i == m.payloadHeader.messageType) {
+                    found = true;
+                    break;
+                }
+            }
+            if (found) {
+                Class bodyClass = 
MessageLoader.getUnionClass(GnunetMessage.Body.class, 
m.payloadHeader.messageType);
+                @SuppressWarnings("unchecked")
+                GnunetMessage.Body b = (GnunetMessage.Body) 
Construct.parseAs(m.payloadBody, bodyClass);
+                messageHandler.visitAppropriate(b);
+            }
+        }
+
+        public void visit(NotifyOutboundTrafficMessage m) {
+            if (notifyOutboundHeaders != null) {
+                notifyOutboundHeaders.notify(m.payloadHeader);
+            }
+            if (notifyOutboundMessages != null) {
+                // todo
+            }
+        }
+
+        public void visit(SendMessageReady m) {
+            RequestIdentification rid = new RequestIdentification(m.smrId, 
m.peer);
+            NotifyTransmitReadyRequest req = ntr_requests.getRequest(rid);
+
+            final SendMessage sm = new SendMessage();
+            sm.cork = 0;
+            sm.peer = req.target;
+            sm.priority = req.priority;
+            sm.deadline = req.deadline.asMessage();
+
+            req.transmitter.transmit(new Connection.MessageSink() {
+                boolean sent;
+                @Override
+                public void send(GnunetMessage.Body m) {
+                    if (sent) {
+                        throw new AssertionError("sending multiple messages 
not supported");
+                    }
+                    sm.payloadMessage =  GnunetMessage.fromBody(m);
+                    sent = true;
+                }
+            });
+
+
+            if (sm.payloadMessage == null)
+                throw new AssertionError();
+
+            client.send(sm);
+        }
+
+        @Override
+        public void visitDefault(Object o) {
+            logger.warn("received unexpected message from core: {}", 
o.getClass());
+        }
+
+        @Override
+        public void handleError() {
+            if (disconnectHandler != null) {
+                for (PeerIdentity e : connectedPeers.keySet()) {
+                    disconnectHandler.onDisconnect(e);
+                }
+            }
+            connectedPeers.clear();
+        }
+    }
+
+    public Core(Configuration cfg) {
+        client = new Client("core", cfg);
+        client.installReceiver(new CoreReceiver());
+        ntr_requests = new MatchingRequestContainer<RequestIdentification, 
NotifyTransmitReadyRequest>(client);
+    }
+
+    /**
+     * Send to the service which messages are we interested in.
+     *
+     * @param initCallback called after the init message has been sent
+     */
+    public void init(InitCallback initCallback) {
+        this.initCallback = initCallback;
+        InitMessage initMessage = new InitMessage();
+
+        initMessage.interested = interested;
+        initMessage.options = 0;
+
+        for (int i : interested) {
+            logger.debug("we are interested in " + i);
+        }
+        client.sendPrefered(initMessage);
+    }
+
+    /**
+     * Ask the core to call "notify" once it is ready to transmit the
+     * given number of bytes to the specified "target".    Must only be
+     * called after a connection to the respective peer has been
+     * established (and the client has been informed about this).
+     *
+     * @param priority    how important is the message?
+     * @param maxdelay    how long can the message wait?
+     * @param target      the identity of the receiver
+     * @param size        the size of the message we want to transmit
+     * @param transmitter called once the core service is ready to send message
+     * @return a handle to cancel the notification
+     */
+    public Cancelable notifyTransmitReady(int priority, RelativeTime maxdelay,
+                                          PeerIdentity target, int size, final 
MessageTransmitter transmitter) {
+        if (!connectedPeers.containsKey(target)) {
+            throw new AssertionError("notifyTransmitReady called for 
unconnected peer");
+        }
+        int id = connectedPeers.get(target);
+        connectedPeers.put(target, id+1);
+        NotifyTransmitReadyRequest notifyRequest = new 
NotifyTransmitReadyRequest(priority, size, target, maxdelay, transmitter);
+        notifyRequest.smrId = id;
+        RequestIdentification rid = new 
RequestIdentification(notifyRequest.smrId, target);
+        return ntr_requests.addRequest(rid, notifyRequest);
+    }
+
+    /**
+     * Observe outgoing message headers from core.
+     * @param h callback
+     */
+    public void observeOutboundHeaders(HeaderNotify h) {
+        this.notifyOutboundHeaders = h;
+    }
+
+    public void observeInboundHeaders(HeaderNotify h) {
+        this.notifyInboundHeaders = h;
+    }
+
+    public void observeInboundMessages(MessageNotify h) {
+        this.notifyInboundMessages = h;
+    }
+
+    public void observeOutboundMessages(MessageNotify h) {
+        this.notifyOutboundMessages = h;
+    }
+
+    public void observeConnect(ConnectHandler connectHandler) {
+        this.connectHandler = connectHandler;
+    }
+
+    public void observeDisconnect(DisconnectHandler disconnectHandler) {
+        this.disconnectHandler = disconnectHandler;
+    }
+
+    /**
+     * Handle all incoming messages with the specified runabout.
+     * Has to be called before init, as the service has to know which messages 
we
+     * are interested in.
+     */
+    public void setMessageHandler(Runabout runabout) {
+        if (messageHandler != null) {
+            throw new AssertionError("Core can have only on message handler");
+        }
+        if (client.isConnected()) {
+            // todo: shouldn't we just reconnect?
+            throw new AssertionError("can set message handler only if not yet 
connected");
+        }
+        messageHandler = runabout;
+        interested = RunaboutUtil.getRunaboutMessageTypes(runabout);
+    }
+
+    /**
+     * Disconnect from the core service. This function can only
+     * be called *after* all pending notifyTransmitReady
+     * requests have been explicitly cancelled.
+     */
+    public void disconnect() {
+        client.disconnect();
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/core/DisconnectHandler.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/core/DisconnectHandler.java            
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/core/DisconnectHandler.java    
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,30 @@
+/*
+ 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.core;
+
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * Called when a peer disconnects from the core.
+ */
+public interface DisconnectHandler {
+    void onDisconnect(PeerIdentity peerIdentity);
+}

Added: gnunet-java/src/main/java/org/gnunet/core/DisconnectNotifyMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/core/DisconnectNotifyMessage.java      
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/core/DisconnectNotifyMessage.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,46 @@
+/*
+ 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.core;
+
+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 sent by the service to clients to notify them
+ * about a peer disconnecting.
+ */
address@hidden(68)
+public class DisconnectNotifyMessage implements GnunetMessage.Body {
+    /**
+     * Always zero.
+     */
+    @UInt32
+    public int reserved;
+
+    /**
+     * Identity of the connecting peer.
+     */
+    @NestedMessage
+    public PeerIdentity peer;
+}

Added: gnunet-java/src/main/java/org/gnunet/core/HeaderNotify.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/core/HeaderNotify.java                 
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/core/HeaderNotify.java 2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,30 @@
+/*
+ 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.core;
+
+import org.gnunet.util.GnunetMessage;
+
+/**
+ *
+ */
+public interface HeaderNotify {
+    void notify(GnunetMessage.Header header);
+}

Added: gnunet-java/src/main/java/org/gnunet/core/InitCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/core/InitCallback.java                 
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/core/InitCallback.java 2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,30 @@
+/*
+ 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.core;
+
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * Called once the handshake with core was successful.
+ */
+public interface InitCallback {
+    void onInit(PeerIdentity myIdentity);
+}

Added: gnunet-java/src/main/java/org/gnunet/core/InitMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/core/InitMessage.java                  
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/core/InitMessage.java  2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,45 @@
+/*
+ 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.core;
+
+import org.gnunet.construct.IntegerFill;
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
+
address@hidden(64)
+public class InitMessage implements GnunetMessage.Body {
+    /*
+    * Options used to tell core what kind of traffic notify messages we are 
interested in.
+    */
+    private final static int
+            OPTION_FULL_INBOUND = 8,
+            OPTION_HDR_INBOUND = 16,
+            OPTION_FULL_OUTBOUND = 32,
+            OPTION_HDR_OUTBOUND = 64;
+
+    @UInt32
+    public long options;
+
+    @IntegerFill(signed = false, bitSize = 16)
+    public int[] interested;
+}

Added: gnunet-java/src/main/java/org/gnunet/core/InitReplyMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/core/InitReplyMessage.java             
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/core/InitReplyMessage.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,39 @@
+/*
+ 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.core;
+
+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;
+
+
address@hidden(65)
+public class InitReplyMessage implements GnunetMessage.Body {
+    @UInt32
+    public int reserved = 0;
+    /**
+     * pubkey of the local peer
+     */
+    @NestedMessage
+    public PeerIdentity myIdentity;
+}

Added: gnunet-java/src/main/java/org/gnunet/core/MessageNotify.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/core/MessageNotify.java                
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/core/MessageNotify.java        
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,28 @@
+/*
+ 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.core;
+
+import org.gnunet.util.GnunetMessage;
+
+
+public interface MessageNotify {
+    void notify(GnunetMessage messageBody);
+}

Added: 
gnunet-java/src/main/java/org/gnunet/core/NotifyInboundTrafficMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/core/NotifyInboundTrafficMessage.java  
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/core/NotifyInboundTrafficMessage.java  
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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.core;
+
+import org.gnunet.construct.*;
+import org.gnunet.util.ATSInformation;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.PeerIdentity;
+
+
address@hidden(70)
+public class NotifyInboundTrafficMessage implements GnunetMessage.Body {
+    /**
+     * Identity of the receiver or sender.
+     */
+    @NestedMessage
+    public PeerIdentity peer;
+
+    @NestedMessage(newFrame = true)
+    public GnunetMessage.Header payloadHeader;
+
+    /**
+     * The (optional) message body corresponding to payloadHeader.
+     * Not typed as GnunetMessage.Body because the message type may not be 
known by this
+     * peer.
+     */
+    @FillWith @UInt8
+    public byte[] payloadBody;
+}

Added: 
gnunet-java/src/main/java/org/gnunet/core/NotifyOutboundTrafficMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/core/NotifyOutboundTrafficMessage.java 
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/core/NotifyOutboundTrafficMessage.java 
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,58 @@
+/*
+ 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.core;
+
+import org.gnunet.construct.*;
+import org.gnunet.util.ATSInformation;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.PeerIdentity;
+
+
address@hidden(71)
+public class NotifyOutboundTrafficMessage implements GnunetMessage.Body {
+    /**
+     * Number of ATS key-value pairs that follow this struct
+     * (excluding the 0-terminator).
+     */
+    @UInt32
+    public long ats_count;
+
+    /**
+     * Identity of the receiver or sender.
+     */
+    @NestedMessage
+    public PeerIdentity peer;
+
+    @VariableSizeArray(lengthField = "ats_count")
+    public ATSInformation[] atsRest;
+
+    @NestedMessage(newFrame = true)
+    public GnunetMessage.Header payloadHeader;
+
+    /**
+     * The (optional) message body corresponding to payloadHeader.
+     * Not typed as GnunetMessage.Body because the message type may not be 
known by this
+     * peer.
+     */
+    @FillWith @UInt8
+    public byte[] payloadBody;
+
+}

Added: gnunet-java/src/main/java/org/gnunet/core/RequestIdentification.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/core/RequestIdentification.java        
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/core/RequestIdentification.java        
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,35 @@
+package org.gnunet.core;
+
+import org.gnunet.peerinfo.PeerInfo;
+import org.gnunet.util.PeerIdentity;
+
+
+final class RequestIdentification {
+    public final int requestIdentifier;
+    public final PeerIdentity peerIdentity;
+
+    public RequestIdentification(int requestIdentifier, PeerIdentity 
peerIdentity) {
+        this.requestIdentifier = requestIdentifier;
+        this.peerIdentity = peerIdentity;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        RequestIdentification that = (RequestIdentification) o;
+
+        if (requestIdentifier != that.requestIdentifier) return false;
+        if (!peerIdentity.equals(that.peerIdentity)) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = requestIdentifier;
+        result = 31 * result + peerIdentity.hashCode();
+        return result;
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/core/SendMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/core/SendMessage.java                  
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/core/SendMessage.java  2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,71 @@
+/*
+ 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.core;
+
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UInt64;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.AbsoluteTimeMessage;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * Client asking core to transmit a particular message to a particular
+ * target (response to GNUNET_MESSAGE_TYPE_CORE_SEND_READY).
+ */
address@hidden(76)
+public class SendMessage implements GnunetMessage.Body {
+    /**
+     * How important is this message?
+     */
+    @UInt32
+    public long priority;
+
+    /**
+     * By what time would the sender really like to see this
+     * message transmitted?
+     */
+    @NestedMessage
+    public AbsoluteTimeMessage deadline;
+
+    /**
+     * Identity of the intended receiver.
+     */
+    @NestedMessage
+    public PeerIdentity peer;
+
+    /**
+     * GNUNET_YES if corking is allowed, GNUNET_NO if not.
+     */
+    @UInt32
+    public int cork;
+
+    /**
+     * Always 0.
+     */
+    @UInt64
+    public int reserved;
+
+    @NestedMessage(newFrame = true)
+    public GnunetMessage payloadMessage;
+
+}

Added: gnunet-java/src/main/java/org/gnunet/core/SendMessageReady.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/core/SendMessageReady.java             
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/core/SendMessageReady.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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.core;
+
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UInt16;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * Core notifying client that it is allowed to now
+ * transmit a message to the given target
+ * (response to GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST).
+ */
address@hidden(75)
+public class SendMessageReady implements GnunetMessage.Body {
+    /**
+     * How many bytes are allowed for transmission?
+     * Guaranteed to be at least as big as the requested size,
+     * or ZERO if the request is rejected (will timeout,
+     * peer disconnected, queue full, etc.).
+     */
+    @UInt16
+    public int size;
+
+    /**
+     * smrId from the request.
+     */
+    @UInt16
+    public int smrId;
+
+    /**
+     * Identity of the intended target.
+     */
+    @NestedMessage
+    public PeerIdentity peer;
+}

Added: gnunet-java/src/main/java/org/gnunet/core/SendMessageRequest.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/core/SendMessageRequest.java           
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/core/SendMessageRequest.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,73 @@
+/*
+ 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.core;
+
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UInt16;
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.AbsoluteTimeMessage;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * Client notifying core about the maximum-priority
+ * message it has in the queue for a particular target.
+ */
address@hidden(74)
+public class SendMessageRequest implements GnunetMessage.Body {
+    /**
+     * How important is this message?
+     */
+    @UInt32
+    public long priority;
+
+    /**
+     * By what time would the sender really like to see this
+     * message transmitted?
+     */
+    @NestedMessage
+    public AbsoluteTimeMessage deadline;
+
+    /**
+     * Identity of the intended target.
+     */
+    @NestedMessage
+    public PeerIdentity peer;
+
+    /**
+     * How large is the client's message queue for this peer?
+     */
+    @UInt32
+    public byte reserved;
+
+    /**
+     * How large is the message?
+     */
+    @UInt16
+    public int size;
+
+    /**
+     * Counter for this peer to match SMRs to replies.
+     */
+    @UInt16
+    public int smrId;
+}

Added: gnunet-java/src/main/java/org/gnunet/core/package-info.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/core/package-info.java                 
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/core/package-info.java 2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,24 @@
+/*
+ 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.
+ */
+
+/**
+ * API for the gnunet core service.
+ */
+package org.gnunet.core;

Added: gnunet-java/src/main/java/org/gnunet/dht/BlockType.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/dht/BlockType.java                     
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/dht/BlockType.java     2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,81 @@
+/*
+ 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.dht;
+
+/**
+ * Information on how to interpret a block of data.
+ */
+public enum BlockType {
+    /**
+     * Any type of block, used as a wildcard when searching.  Should
+     * never be attached to a specific block.
+     */
+    ANY(0),
+    /**
+     * Data block (leaf) in the CHK tree.
+     */
+    DBLOCK(1),
+    /**
+     * Inner block in the CHK tree.
+     */
+    IBLOCK(2),
+    /**
+     * Type of a block representing a keyword search result.  Note that
+     * the values for KBLOCK, SBLOCK and NBLOCK must be consecutive.
+     */
+    KBLOCK(3),
+    /**
+     * Type of a block that is used to advertise content in a namespace.
+     */
+    SBLOCK(4),
+    /**
+     * Type of a block that is used to advertise a namespace.
+     */
+    NBLOCK(5),
+    /**
+     * Type of a block representing a block to be encoded on demand from disk.
+     * Should never appear on the network directly.
+     */
+    FS_ONDEMAND(6),
+    /**
+     * Type of a block that contains a HELLO for a peer (for
+     * DHT find-peer operations).
+     */
+    DHT_HELLO(7),
+    /**
+     * Block for testing.
+     */
+    TEST(8),
+    /**
+     * Block for storing .gnunet-domains
+     */
+    DNS(10),
+    /**
+     * Block for storing record data
+     */
+    NAMERECORD(11);
+
+    public final int val;
+
+    BlockType(int val) {
+        this.val = val;
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/dht/ClientGetMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/dht/ClientGetMessage.java              
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/dht/ClientGetMessage.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,51 @@
+/*
+ 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.dht;
+
+import org.gnunet.construct.*;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.HashCode;
+
+/**
+* Created with IntelliJ IDEA.
+* User: dold
+* Date: 5/2/12
+* Time: 7:05 PM
+* To change this template use File | Settings | File Templates.
+*/
address@hidden(143)
+public class ClientGetMessage implements GnunetMessage.Body {
+    /**
+     * Combination of RouteOption.*
+     */
+    @UInt32
+    public int options;
+    @UInt32
+    public int desiredReplicationLevel;
+    @UInt32
+    public int type;
+    @NestedMessage
+    public HashCode key;
+    @UInt64
+    public long uniqueId;
+    @FillWith @UInt8
+    public byte[] xquery;
+}

Added: gnunet-java/src/main/java/org/gnunet/dht/ClientGetStopMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/dht/ClientGetStopMessage.java          
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/dht/ClientGetStopMessage.java  
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,45 @@
+/*
+ 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.dht;
+
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UInt64;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.HashCode;
+
+/**
+* Created with IntelliJ IDEA.
+* User: dold
+* Date: 5/2/12
+* Time: 7:05 PM
+* To change this template use File | Settings | File Templates.
+*/
address@hidden(144)
+public class ClientGetStopMessage implements GnunetMessage.Body {
+    @UInt32
+    public int reserved = 0;
+    @UInt64
+    public long unique_id;
+    @NestedMessage
+    public HashCode key;
+}

Added: 
gnunet-java/src/main/java/org/gnunet/dht/ClientPutConfirmationMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/dht/ClientPutConfirmationMessage.java  
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/dht/ClientPutConfirmationMessage.java  
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,38 @@
+/*
+ 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.dht;
+
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UInt64;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
+
address@hidden(155)
+public class ClientPutConfirmationMessage implements GnunetMessage.Body {
+    @UInt32
+    public int reserved;
+    /**
+     * UID used to identify request with the response
+     */
+    @UInt64
+    public long uid;
+}

Added: gnunet-java/src/main/java/org/gnunet/dht/ClientPutMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/dht/ClientPutMessage.java              
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/dht/ClientPutMessage.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,54 @@
+/*
+ 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.dht;
+
+import org.gnunet.construct.*;
+import org.gnunet.util.AbsoluteTimeMessage;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.HashCode;
+
+
address@hidden(142)
+public class ClientPutMessage implements GnunetMessage.Body {
+    /**
+     * Type of data to insert, one of BlockType.*
+     */
+    @UInt32
+    public int type;
+    /**
+     * Combination of RouteOption.*
+     */
+    @UInt32
+    public int options;
+    @UInt32
+    public int desiredReplicationLevel;
+    /**
+     * UID used to identify request with the response
+     */
+    @UInt64
+    public long uid;
+    @NestedMessage
+    public AbsoluteTimeMessage expiration;
+    @NestedMessage
+    public HashCode hash;
+    @FillWith @UInt8
+    public byte[] data;
+}

Added: gnunet-java/src/main/java/org/gnunet/dht/ClientResultMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/dht/ClientResultMessage.java           
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/dht/ClientResultMessage.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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.dht;
+
+import org.gnunet.construct.*;
+import org.gnunet.util.AbsoluteTimeMessage;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.HashCode;
+import org.gnunet.util.PeerIdentity;
+
+/**
+* Created with IntelliJ IDEA.
+* User: dold
+* Date: 5/2/12
+* Time: 7:06 PM
+* To change this template use File | Settings | File Templates.
+*/
address@hidden(145)
+public class ClientResultMessage implements GnunetMessage.Body {
+    @UInt32
+    public int type;
+    @UInt32
+    public int putPathLength;
+    @UInt32
+    public int getPathLength;
+    @UInt64
+    public long uid;
+    @NestedMessage
+    public AbsoluteTimeMessage expiration;
+    @NestedMessage
+    public HashCode key;
+    @VariableSizeArray(lengthField = "putPathLength")
+    public PeerIdentity[] putPath;
+    @VariableSizeArray(lengthField = "getPathLength")
+    public PeerIdentity[] getPath;
+    @FillWith @UInt8
+    public byte[] data;
+}

Added: gnunet-java/src/main/java/org/gnunet/dht/DistributedHashTable.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/dht/DistributedHashTable.java          
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/dht/DistributedHashTable.java  
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,449 @@
+/*
+ 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.dht;
+
+import com.google.common.base.Charsets;
+import org.gnunet.mq.Envelope;
+import org.gnunet.requests.MatchingRequestContainer;
+import org.gnunet.requests.RequestContainer;
+import org.gnunet.requests.SequentialRequestContainer;
+import org.gnunet.util.*;
+import org.gnunet.util.getopt.Argument;
+import org.gnunet.util.getopt.ArgumentAction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+/**
+ * API for the gnunet dht service.
+ * <p/>
+ * Stores data under a key, distributed across the network.
+ * <p/>
+ */
+public class DistributedHashTable {
+    private static final Logger logger = LoggerFactory
+            .getLogger(DistributedHashTable.class);
+
+    private Client client;
+
+    /**
+     * next UID used on get/monitor requests, incremented after each use.
+     */
+    private long nextUID = 1;
+
+    private MatchingRequestContainer<Long, PutRequest> putRequests;
+    private MatchingRequestContainer<Long, GetRequest> getRequests;
+    private SequentialRequestContainer<MonitorRequest> monitorRequests;
+
+    private class PutRequest extends RequestContainer.Request {
+        public byte[] data;
+        public HashCode key;
+        public int replicationLevel;
+        public AbsoluteTime expiration;
+        public int type;
+        public Continuation cont;
+        public long uid;
+
+        public PutRequest() {
+            this.uid = nextUID++;
+        }
+
+        @Override
+        public Envelope assembleRequest() {
+            final ClientPutMessage cpm = new ClientPutMessage();
+            cpm.data = data;
+            cpm.hash = key;
+            cpm.desiredReplicationLevel = replicationLevel;
+            cpm.expiration = expiration.asMessage();
+            cpm.type = type;
+            cpm.uid = uid;
+            return new Envelope(cpm);
+        }
+
+        public void cancel() {
+        }
+    }
+
+
+    private class GetRequest extends RequestContainer.Request {
+        public long uid;
+        public HashCode key;
+        public ResultCallback cb;
+        public int type;
+        public int replication;
+        public byte[] xquery;
+
+        public GetRequest() {
+            uid = DistributedHashTable.this.nextUID++;
+        }
+
+        @Override
+        public Envelope assembleRequest() {
+            ClientGetMessage gm = new ClientGetMessage();
+            gm.desiredReplicationLevel = replication;
+            gm.type = type;
+            gm.xquery = xquery == null ? new byte[0] : xquery;
+            gm.key = key;
+            gm.uniqueId = uid;
+            return new Envelope(gm);
+        }
+        public void cancel() {
+
+        }
+    }
+
+    private class MonitorRequest extends RequestContainer.Request {
+        public int blockType;
+        public HashCode key;
+        public MonitorGetHandler getHandler;
+        public MonitorGetResponseHandler getResponseHandler;
+        public MonitorPutHandler putHandler;
+
+        @Override
+        public Envelope assembleRequest() {
+            MonitorStartStop mss = new MonitorStartStop();
+            if (key != null) {
+                mss.filter_key = 1;
+                mss.key = key;
+            } else {
+                mss.key = new HashCode();
+            }
+            if (getHandler != null) {
+                mss.get = 1;
+            }
+            if (getResponseHandler != null) {
+                mss.getResp = 1;
+            }
+            if (putHandler != null) {
+                mss.put = 1;
+            }
+            mss.type = blockType;
+            return new Envelope(mss);
+        }
+
+        public void cancel() {
+            // todo: use priority requests
+            MonitorRequest cancelRequest = new MonitorRequest();
+            cancelRequest.getHandler = null;
+            cancelRequest.getResponseHandler = null;
+            cancelRequest.putHandler = null;
+            monitorRequests.addRequest(cancelRequest);
+
+            monitorRequests.addRequest(cancelRequest);
+        }
+    }
+
+    private class DHTMessageReceiver extends RunaboutMessageReceiver {
+        public void visit(ClientPutConfirmationMessage pcm) {
+            PutRequest thePutRequest = putRequests.getRequest(pcm.uid);
+            if (thePutRequest == null) {
+                logger.warn("request UID not found");
+                return;
+            }
+            if (thePutRequest.cont != null) {
+                thePutRequest.cont.cont(true);
+            }
+        }
+
+        public void visit(ClientResultMessage rm) {
+            GetRequest theGetRequest = getRequests.getRequest(rm.uid);
+            if (theGetRequest == null) {
+                logger.warn("request UID not found");
+                return;
+            }
+            
theGetRequest.cb.handleResult(AbsoluteTime.fromNetwork(rm.expiration), rm.key, 
null, null,
+                    BlockType.TEST,
+                    rm.data);
+        }
+
+        public void visit(MonitorGetMessage monitorGetMessage) {
+            for (MonitorRequest monitorRequest : monitorRequests.iter()) {
+                boolean type_ok = (monitorGetMessage.type == BlockType.ANY.val)
+                        || (monitorGetMessage.type == 
monitorRequest.blockType);
+                boolean key_ok = monitorGetMessage.key.isAllZero()
+                        || monitorGetMessage.key.equals(monitorRequest.key);
+
+                if (key_ok && type_ok && monitorRequest.getHandler != null) {
+                    monitorRequest.getHandler.onGet(monitorGetMessage.options, 
monitorGetMessage.type,
+                            monitorGetMessage.hop_count, 
monitorGetMessage.desired_replication_level, monitorGetMessage.getPath,
+                            monitorGetMessage.key);
+                }
+            }
+        }
+
+        public void visit(MonitorGetRespMessage monitorGetRespMessage) {
+            for (MonitorRequest monitorRequest : monitorRequests.iter()) {
+                boolean type_ok = (monitorGetRespMessage.type == 
BlockType.ANY.val)
+                        || (monitorGetRespMessage.type == 
monitorRequest.blockType);
+                boolean key_ok = monitorGetRespMessage.key.isAllZero()
+                        || 
monitorGetRespMessage.key.equals(monitorRequest.key);
+
+                if (key_ok && type_ok && monitorRequest.getResponseHandler != 
null) {
+                    monitorRequest.getResponseHandler.onGetResponse(
+                            monitorGetRespMessage.type,
+                            monitorGetRespMessage.getPath,
+                            monitorGetRespMessage.putPath,
+                            monitorGetRespMessage.expiration,
+                            monitorGetRespMessage.key,
+                            monitorGetRespMessage.data);
+                }
+            }
+
+        }
+
+        public void visit(MonitorPutMessage monitorPutMessage) {
+            for (MonitorRequest monitorRequest : monitorRequests.iter()) {
+                boolean type_ok = (monitorPutMessage.type == BlockType.ANY.val)
+                        || (monitorPutMessage.type == 
monitorRequest.blockType);
+                boolean key_ok = monitorPutMessage.key.isAllZero()
+                        || monitorPutMessage.key.equals(monitorRequest.key);
+
+                if (key_ok && type_ok && monitorRequest.putHandler != null) {
+                    monitorRequest.putHandler.onPut(monitorPutMessage.options, 
monitorPutMessage.type,
+                            monitorPutMessage.hop_count, 
monitorPutMessage.expirationTime,
+                            monitorPutMessage.putPath, monitorPutMessage.key, 
monitorPutMessage.data);
+                }
+            }
+        }
+
+        @Override
+        public void handleError() {
+        }
+    }
+
+
+    /**
+     * Create a connection with the DHT service.
+     *
+     * @param cfg the configuration to use
+     */
+    public DistributedHashTable(Configuration cfg) {
+        client = new Client("dht", cfg);
+        client.installReceiver(new DHTMessageReceiver());
+        putRequests = new MatchingRequestContainer<Long, PutRequest>(client);
+        getRequests = new MatchingRequestContainer<Long, GetRequest>(client);
+        monitorRequests = new 
SequentialRequestContainer<MonitorRequest>(client);
+    }
+
+    /**
+     * Put data into the dht.
+     *
+     * @param key              key key to store the data under
+     * @param data             data data to store
+     * @param replicationLevel how many peers should store this value
+     * @param routeOptions     additional options
+     * @param type             type of the data to store
+     * @param expiration       how long should the value be stored? TODO: what 
is the maximum?
+     * @param timeout          how long after we give up on storing the value?
+     * @param cont             called after the put operation failed or 
succeeded
+     */
+    public void put(HashCode key, byte[] data, int replicationLevel, 
Set<RouteOption> routeOptions,
+                    int type, AbsoluteTime expiration,
+                    RelativeTime timeout, final Continuation cont) {
+        PutRequest pr = new PutRequest();
+        pr.key = key;
+        pr.data = data;
+        pr.replicationLevel = replicationLevel;
+        pr.expiration = expiration;
+        pr.type = type;
+        pr.cont = cont;
+
+        putRequests.addRequest(pr.uid, pr);
+    }
+
+
+    /**
+     * Request results from the DHT.
+     *
+     * @param timeout      timeout for the request
+     * @param type         which type of data do we want to query for? (the 
DHT does not support TYPE_ANY)
+     * @param key          the key we want to query
+     * @param replication  how many peers do we want to ask?
+     * @param routeOptions extra routing options, null for default
+     * @param xquery       extra query parameters, defaults to null
+     * @param cb           the callback object for results or failure 
indication
+     * @return a handle to cancel the request
+     */
+    public Cancelable startGet(RelativeTime timeout, int type, HashCode key,
+                               int replication, EnumSet<RouteOption> 
routeOptions,
+                               byte[] xquery, ResultCallback cb) {
+
+        final GetRequest getRequest = new GetRequest();
+        getRequest.key = key;
+        getRequest.cb = cb;
+        getRequest.type = type;
+        getRequest.replication = type;
+        getRequest.xquery = xquery;
+
+        return getRequests.addRequest(getRequest.uid, getRequest);
+    }
+
+    public Cancelable startMonitor(int blockType, HashCode key, 
MonitorGetHandler getHandler,
+                                   MonitorGetResponseHandler 
getResponseHandler,
+                                   MonitorPutHandler putHandler) {
+        MonitorRequest monitorRequest = new MonitorRequest();
+        monitorRequest.blockType = blockType;
+        monitorRequest.key = key;
+        monitorRequest.getHandler = getHandler;
+        monitorRequest.getResponseHandler = getResponseHandler;
+        monitorRequest.putHandler = putHandler;
+
+        return monitorRequests.addRequest(monitorRequest);
+    }
+
+
+    /**
+     * Destroy the connection to the service.
+     */
+    public void destroy() {
+        // there's nothing to sync, just destroy!
+        client.disconnect();
+    }
+
+    public static void main(String[] args) {
+        new Program(args) {
+            @Argument(action = ArgumentAction.SET,
+                    shortname = "p",
+                    longname = "put",
+                    description = "set a value in the DHT; default is get")
+            boolean modePut = false;
+
+            @Argument(action = ArgumentAction.SET,
+                    shortname = "m",
+                    longname = "monitor",
+                    description = "monitor requests going to the local DHT")
+            boolean monitor = false;
+
+
+            @Argument(action = ArgumentAction.STORE_STRING,
+                    shortname = "d",
+                    longname = "data",
+                    description = "data (only used with --put)")
+            String data = null;
+
+            @Argument(action = ArgumentAction.STORE_STRING,
+                    shortname = "k",
+                    longname = "key",
+                    description = "key used for the operation")
+            String key = null;
+
+
+            // todo: implement the following options
+            /*
+            @Argument(action = ArgumentAction.STORE_STRING,
+                    shortname = "t",
+                    longname = "type",
+                    description = "type of data used in this operation")
+            String type = null;
+
+            @Argument(action = ArgumentAction.STORE_STRING,
+                    shortname = "e",
+                    longname = "expire",
+                    description = "expiration (ony use with --put)")
+            String expiration = null;
+            */
+
+
+            @Argument(action = ArgumentAction.STORE_NUMBER,
+                    shortname = "r",
+                    longname = "replication",
+                    description = "desired replication (only used with --put)")
+            int replication = 5;
+
+
+            public void run() {
+                if (modePut) {
+
+                    if (key == null) {
+                        System.out.println("key required");
+                        return;
+                    }
+
+                    if (data == null) {
+                        System.out.println("data required on put");
+                        return;
+                    }
+                    final DistributedHashTable dht = new 
DistributedHashTable(cfg);
+
+                    dht.put(new HashCode(key), data.getBytes(), replication, 
EnumSet.of(RouteOption.NONE),
+                            BlockType.TEST.val, 
AbsoluteTime.now().add(RelativeTime.HOUR),
+                            RelativeTime.SECOND, new Continuation() {
+                        @Override
+                        public void cont(boolean success) {
+                            if (success) {
+                                System.out.println("put request sent");
+                            } else {
+                                System.out.println("error");
+                            }
+                            dht.destroy();
+                        }
+                    });
+                } else if (monitor) {
+                    final DistributedHashTable dht = new 
DistributedHashTable(cfg);
+                    dht.startMonitor(BlockType.TEST.val, null,
+                            new MonitorGetHandler() {
+                                @Override
+                                public void onGet(int options, int type, int 
hop_count,
+                                                  int 
desired_replication_level, PeerIdentity[] getPath, HashCode key) {
+                                    System.out.println("get monitored");
+                                }
+                            },
+                            new MonitorGetResponseHandler() {
+                                @Override
+                                public void onGetResponse(int type, 
PeerIdentity[] getPath, PeerIdentity[] putPath,
+                                                          AbsoluteTimeMessage 
expiration, HashCode key, byte[] data) {
+                                    System.out.println("get response 
monitored");
+                                }
+                            },
+                            new MonitorPutHandler() {
+                                @Override
+                                public void onPut(int options, int type, int 
hop_count, AbsoluteTimeMessage
+                                        expirationTime, PeerIdentity[] 
putPath, HashCode key, byte[] data) {
+                                    System.out.println("put monitored");
+                                }
+                            });
+                } else { // get
+                    if (key == null) {
+                        System.out.println("key required");
+                        return;
+                    }
+                    if (data != null) {
+                        System.out.println("get does not take data as an 
option");
+                        return;
+                    }
+
+                    final DistributedHashTable dht = new 
DistributedHashTable(cfg);
+
+                    dht.startGet(RelativeTime.SECOND, BlockType.TEST.val, new 
HashCode(key), replication, null,
+                            new byte[0], new ResultCallback() {
+                        @Override
+                        public void handleResult(AbsoluteTime expiration, 
HashCode key, List<PeerIdentity>
+                                getPath, List<PeerIdentity> putPath, BlockType 
type, byte[] data) {
+                            System.out.println("got result:");
+                            System.out.println(new String(data, 
Charsets.UTF_8));
+                        }
+                    });
+                }
+            }
+        }.start();
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/dht/MonitorGetHandler.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/dht/MonitorGetHandler.java             
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/dht/MonitorGetHandler.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,30 @@
+/*
+ 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.dht;
+
+import org.gnunet.util.HashCode;
+import org.gnunet.util.PeerIdentity;
+
+
+public interface MonitorGetHandler {
+    void onGet(int options, int type, int hop_count, int 
desired_replication_level, PeerIdentity[] getPath,
+               HashCode key);
+}

Added: gnunet-java/src/main/java/org/gnunet/dht/MonitorGetMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/dht/MonitorGetMessage.java             
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/dht/MonitorGetMessage.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,75 @@
+/*
+ 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.dht;
+
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.construct.VariableSizeArray;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.HashCode;
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * Message to monitor get requests going through peer, DHT service -> clients.
+ */
address@hidden(149)
+public class MonitorGetMessage implements GnunetMessage.Body {
+    /**
+     * Message options, actually an 'enum GNUNET_DHT_RouteOption' value.
+     */
+    @UInt32
+    public int options;
+
+    /**
+     * The type of data in the request.
+     */
+    @UInt32
+    public int type;
+
+    /**
+     * Hop count
+     */
+    @UInt32
+    public int hop_count;
+
+    /**
+     * Replication level for this message
+     */
+    @UInt32
+    public int desired_replication_level;
+
+    /**
+     * Number of peers recorded in the outgoing path from source to the
+     * storage location of this message.
+     */
+    @UInt32
+    public int get_path_length;
+
+    /**
+     * The key to store the value under.
+     */
+    @NestedMessage
+    public HashCode key;
+
+    @VariableSizeArray(lengthField = "get_path_length")
+    public PeerIdentity[] getPath;
+}

Added: gnunet-java/src/main/java/org/gnunet/dht/MonitorGetRespMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/dht/MonitorGetRespMessage.java         
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/dht/MonitorGetRespMessage.java 
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,72 @@
+/*
+ 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.dht;
+
+import org.gnunet.construct.*;
+import org.gnunet.util.AbsoluteTimeMessage;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.HashCode;
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * Message to monitor get results going through peer, DHT service --> clients.
+ */
address@hidden(150)
+public class MonitorGetRespMessage implements GnunetMessage.Body {
+    /**
+     * Content type.
+     */
+    @UInt32
+    int type;
+
+    /**
+     * Length of the PUT path that follows (if tracked).
+     */
+    @UInt32
+    int put_path_length;
+
+    /**
+     * Length of the GET path that follows (if tracked).
+     */
+    @UInt32
+    int get_path_length;
+
+    /**
+     * When does the content expire?
+     */
+    @NestedMessage
+    public AbsoluteTimeMessage expiration;
+
+    /**
+     * The key of the corresponding GET request.
+     */
+    @NestedMessage
+    public HashCode key;
+
+    @VariableSizeArray(lengthField = "put_path_length")
+    public PeerIdentity[] putPath;
+
+    @VariableSizeArray(lengthField = "get_path_length")
+    public PeerIdentity[] getPath;
+
+    @FillWith @UInt8
+    public byte[] data;
+}

Added: gnunet-java/src/main/java/org/gnunet/dht/MonitorGetResponseHandler.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/dht/MonitorGetResponseHandler.java     
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/dht/MonitorGetResponseHandler.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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.dht;
+
+import org.gnunet.util.AbsoluteTimeMessage;
+import org.gnunet.util.HashCode;
+import org.gnunet.util.PeerIdentity;
+
+
+public interface MonitorGetResponseHandler {
+    void onGetResponse(int type, PeerIdentity[] getPath, PeerIdentity[] 
putPath, AbsoluteTimeMessage expiration,
+                       HashCode key, byte[] data);
+}

Added: gnunet-java/src/main/java/org/gnunet/dht/MonitorPutHandler.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/dht/MonitorPutHandler.java             
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/dht/MonitorPutHandler.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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.dht;
+
+import org.gnunet.util.AbsoluteTimeMessage;
+import org.gnunet.util.HashCode;
+import org.gnunet.util.PeerIdentity;
+
+
+public interface MonitorPutHandler {
+    void onPut(int options, int type, int hop_count, AbsoluteTimeMessage 
expirationTime, PeerIdentity[] putPath,
+               HashCode key, byte[] data);
+}

Added: gnunet-java/src/main/java/org/gnunet/dht/MonitorPutMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/dht/MonitorPutMessage.java             
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/dht/MonitorPutMessage.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,82 @@
+/*
+ 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.dht;
+
+import org.gnunet.construct.*;
+import org.gnunet.util.AbsoluteTimeMessage;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.HashCode;
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * Message to monitor put requests going through peer, DHT service --> clients.
+ */
address@hidden(151)
+public class MonitorPutMessage implements GnunetMessage.Body {
+    /**
+     * Message options, actually an 'enum GNUNET_DHT_RouteOption' value.
+     */
+    @UInt32
+    public int options;
+
+    /**
+     * The type of data in the request.
+     */
+    @UInt32
+    public int type;
+
+    /**
+     * Hop count so far.
+     */
+    @UInt32
+    public int hop_count;
+
+    /**
+     * Replication level for this message
+     */
+    @UInt32
+    public int desired_replication_level;
+
+    /**
+     * Number of peers recorded in the outgoing path from source to the
+     * storage location of this message.
+     */
+    @UInt32
+    public int put_path_length;
+
+    /**
+     * How long should this data persist?
+     */
+    @NestedMessage
+    public AbsoluteTimeMessage expirationTime;
+
+    /**
+     * The key to store the value under.
+     */
+    @NestedMessage
+    public HashCode key;
+
+    @VariableSizeArray(lengthField = "put_path_length")
+    public PeerIdentity[] putPath;
+
+    @FillWith @UInt8
+    public byte[] data;
+}

Added: gnunet-java/src/main/java/org/gnunet/dht/MonitorStartStop.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/dht/MonitorStartStop.java              
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/dht/MonitorStartStop.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,70 @@
+/*
+ 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.dht;
+
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UInt16;
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.HashCode;
+
+/**
+ * Message to request monitoring messages, clients --> DHT service.
+ */
address@hidden(153)
+public class MonitorStartStop implements GnunetMessage.Body {
+    /**
+     * The type of data desired, GNUNET_BLOCK_TYPE_ANY for all.
+     */
+    @UInt32
+    public int type;
+
+    /**
+     * Flag whether to notify about GET messages.
+     */
+    @UInt16
+    public int get;
+
+    /**
+     * Flag whether to notify about GET_REPONSE messages.
+     */
+    @UInt16
+    public int getResp;
+
+    /**
+     * Flag whether to notify about PUT messages.
+     */
+    @UInt16
+    public int put;
+
+    /**
+     * Flag whether to use the provided key to filter messages.
+     */
+    @UInt16
+    public int filter_key;
+
+    /*
+    The key to filter messages by.
+     */
+    @NestedMessage
+    public HashCode key;
+}

Added: gnunet-java/src/main/java/org/gnunet/dht/ResultCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/dht/ResultCallback.java                
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/dht/ResultCallback.java        
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,46 @@
+/*
+ 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.dht;
+
+import org.gnunet.util.AbsoluteTime;
+import org.gnunet.util.HashCode;
+import org.gnunet.util.PeerIdentity;
+
+import java.util.List;
+
+/**
+ * Callback object for requests to the dht
+ */
+public interface ResultCallback {
+    /**
+     * Called when the dht returns a result
+     *
+     * @param expiration expiration of the returned entry
+     * @param key        key of the returned entry
+     * @param getPath    put path of the returned entry
+     * @param putPath    put path of the returned entry
+     * @param type       type of data in the entry
+     * @param data       data of the returned entry
+     */
+    public void handleResult(AbsoluteTime expiration, HashCode key,
+                             List<PeerIdentity> getPath, List<PeerIdentity> 
putPath,
+                             BlockType type, byte[] data);
+}

Added: gnunet-java/src/main/java/org/gnunet/dht/RouteOption.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/dht/RouteOption.java                   
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/dht/RouteOption.java   2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,55 @@
+/*
+ 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.dht;
+
+/**
+ * Options passed to the dht service for routing requests.
+ */
+enum RouteOption {
+    /**
+     * Default.  Do nothing special.
+     */
+    NONE(0),
+    /**
+     * Each peer along the way should look at 'enc' (otherwise
+     * only the k-peers closest to the key should look at it).
+     */
+    DEMULTIPLEX_EVERYWHERE(1),
+    /**
+     * We should keep track of the route that the message
+     * took in the P2P network.
+     */
+    RECORD_ROUTE(2),
+    /**
+     * This is a 'FIND-PEER' request, so approximate results are fine.
+     */
+    FIND_PEER(4),
+    /**
+     * Possible message option for query key randomization.
+     */
+    BART(8);
+
+    private int val;
+
+    RouteOption(int val) {
+        this.val = val;
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/dht/package-info.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/dht/package-info.java                  
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/dht/package-info.java  2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,24 @@
+/*
+ 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.
+ */
+
+/**
+ * API for the gnunet dht service.
+ */
+package org.gnunet.dht;

Added: gnunet-java/src/main/java/org/gnunet/hello/HelloMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/hello/HelloMessage.java                
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/hello/HelloMessage.java        
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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;
+}

Added: gnunet-java/src/main/java/org/gnunet/hello/package-info.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/hello/package-info.java                
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/hello/package-info.java        
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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;

Added: gnunet-java/src/main/java/org/gnunet/mesh/ClientConnectMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mesh/ClientConnectMessage.java         
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/mesh/ClientConnectMessage.java 
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,17 @@
+package org.gnunet.mesh;
+
+import org.gnunet.construct.*;
+import org.gnunet.util.GnunetMessage;
+
+/**
+ * Allows a client to register with the service.
+ *
+ * Direction: client -> service
+ *
+ * @author Florian Dold
+ */
address@hidden(272)
+public class ClientConnectMessage implements GnunetMessage.Body {
+    @IntegerFill(signed = false, bitSize = 32)
+    public int[] apps_list;
+}

Added: gnunet-java/src/main/java/org/gnunet/mesh/ConnectHandler.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mesh/ConnectHandler.java               
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/mesh/ConnectHandler.java       
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,13 @@
+package org.gnunet.mesh;
+
+import org.gnunet.peerinfo.PeerInfo;
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public interface ConnectHandler {
+    public void onConnect(Mesh.Tunnel tunnel, PeerIdentity peer);
+}

Added: gnunet-java/src/main/java/org/gnunet/mesh/DataMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mesh/DataMessage.java                  
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/mesh/DataMessage.java  2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,19 @@
+package org.gnunet.mesh;
+
+import org.gnunet.construct.*;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
address@hidden(260)
+public class DataMessage implements GnunetMessage.Body {
+    @UInt32
+    public int tid;
+    @FillWith
+    @UInt8
+    public byte[] payload;
+}

Added: gnunet-java/src/main/java/org/gnunet/mesh/DisconnectHandler.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mesh/DisconnectHandler.java            
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/mesh/DisconnectHandler.java    
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,12 @@
+package org.gnunet.mesh;
+
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public interface DisconnectHandler {
+    void onDisconnect(PeerIdentity peer);
+}

Added: gnunet-java/src/main/java/org/gnunet/mesh/InboundTunnelHandler.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mesh/InboundTunnelHandler.java         
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/mesh/InboundTunnelHandler.java 
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,12 @@
+package org.gnunet.mesh;
+
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public interface InboundTunnelHandler {
+    void onInboundTunnel(Mesh.Tunnel tunnel, PeerIdentity initiator);
+}

Added: gnunet-java/src/main/java/org/gnunet/mesh/LocalAckMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mesh/LocalAckMessage.java              
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/mesh/LocalAckMessage.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,18 @@
+package org.gnunet.mesh;
+
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
address@hidden(286)
+public class LocalAckMessage implements GnunetMessage.Body {
+    @UInt32
+    public int tid;
+    @UInt32
+    public int maxPid;
+}

Added: gnunet-java/src/main/java/org/gnunet/mesh/Mesh.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mesh/Mesh.java                         
(rev 0)
+++ gnunet-java/src/main/java/org/gnunet/mesh/Mesh.java 2013-08-27 17:16:18 UTC 
(rev 28880)
@@ -0,0 +1,309 @@
+/*
+ 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.mesh;
+
+import org.gnunet.construct.Construct;
+import org.gnunet.mq.Envelope;
+import org.gnunet.mq.MessageQueue;
+import org.gnunet.mq.NotifySentHandler;
+import org.gnunet.util.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Mesh API
+ *
+ * @author Florian Dold
+ */
+public class Mesh {
+    /**
+     * Class logger.
+     */
+    private static final Logger logger = LoggerFactory
+            .getLogger(Mesh.class);
+
+    /**
+     * For tunnels created by the client, the bit in this
+     * mask is always set.
+     */
+    private static final int TUNNEL_ID_CLI = 0x80000000;
+
+    /**
+     * For tunnels created by the server, the bit in this
+     * mask is always set.
+     */
+    private static final int TUNNEL_ID_SERV = 0xB0000000;
+
+    /**
+     * Disable buffering on intermediate nodes (for minimum latency).
+     * Yes/No.
+     */
+    private static final int OPTION_NOBUFFER = 1;
+
+    /**
+     * Enable tunnel reliability, lost messages will be retransmitted.
+     * Yes/No.
+     */
+    private static final int OPTION_RELIABLE = 2;
+
+    /**
+     * Client connected to the mesh service
+     */
+    private final Client client;
+
+    /**
+     * Called whenever a tunnel was destroyed.
+     */
+    private TunnelEndHandler tunnelEndHandler;
+
+    /**
+     * Message handler for messages received through
+     * a tunnel.
+     */
+    private MeshRunabout messageReceiver;
+
+    /**
+     * Ports that we listen on.
+     */
+    private int[] ports;
+
+    /**
+     * Handler for inbound tunnels.
+     */
+    private InboundTunnelHandler inboundTunnelHandler;
+
+    /**
+     * Mapping from the tunnel's ID to the tunnel object.
+     */
+    private Map<Integer,Tunnel> tunnelMap = new HashMap<Integer,Tunnel>();
+
+    /**
+     * Counter for generating fresh tunnel ID's
+     * when creating new tunnels.
+     */
+    private int next_tid = 1;
+
+    /**
+     * A tunnel to a remote peer.
+     * @param <T> type of context data for the tunnel
+     */
+    public class Tunnel<T> extends MessageQueue {
+        private T context;
+        private final int opt;
+        public final PeerIdentity peer;
+        public final int port;
+        protected int tunnelId;
+        private boolean receive_done_expected = false;
+        int ack_count = 1;
+
+        /**
+         * Canceler for the currently submitted envelope.
+         */
+        public Cancelable envelopeCanceler;
+
+        /**
+         * Create a new tunnel (we're initiator and will be allowed to 
add/remove peers
+         * and to broadcast).
+         *
+         * @param context tunnel context
+         * @param peer peer identity the tunnel should go to
+         * @param port Port number.
+         * @param nobuffer Flag for disabling buffering on relay nodes.
+         * @param reliable Flag for end-to-end reliability.
+         */
+        public Tunnel(PeerIdentity peer, int port, boolean nobuffer, boolean 
reliable, T context)
+        {
+            this(peer, 0, port, nobuffer, reliable);
+            TunnelCreateMessage tcm = new TunnelCreateMessage();
+            tcm.otherEnd = peer;
+            tcm.opt = opt;
+            tcm.port = port;
+            tcm.tunnel_id = tunnelId;
+            client.send(tcm);
+        }
+
+        /**
+         * Private tunnel constructor, for creating tunnel objects for
+         * incoming tunnels.
+         *
+         * @param peer
+         * @param tunnelId
+         * @param port
+         * @param nobuffer
+         * @param reliable
+         */
+        public Tunnel(PeerIdentity peer, int tunnelId, int port, boolean 
nobuffer, boolean reliable) {
+            int my_opt = 0;
+            if (reliable)
+                my_opt |= OPTION_RELIABLE;
+            if (nobuffer)
+                my_opt |= OPTION_NOBUFFER;
+            if (0 == tunnelId)
+                this.tunnelId = ((next_tid++) | TUNNEL_ID_CLI) & 
~TUNNEL_ID_SERV;
+            else
+                this.tunnelId = tunnelId;
+            this.peer = peer;
+            this.port = port;
+            this.opt = my_opt;
+        }
+
+        public void receiveDone() {
+            if (!receive_done_expected)
+                throw new AssertionError("unexpected call to receiveDone");
+            LocalAckMessage am = new LocalAckMessage();
+            am.tid = tunnelId;
+            client.send(am);
+            receive_done_expected = false;
+        }
+
+        public void destroy() {
+            TunnelDestroyMessage m = new TunnelDestroyMessage();
+            m.tunnel_id = tunnelId;
+            client.send(m);
+        }
+
+        @Override
+        protected void submit(Envelope ev) {
+            if (ack_count <= 0)
+                throw new AssertionError();
+            DataMessage m = new DataMessage();
+            m.payload = Construct.toBinary(GnunetMessage.fromBody(ev.message));
+            Envelope mesh_ev = new Envelope(m);
+            mesh_ev.notifySent(new NotifySentHandler() {
+                @Override
+                public void onSent() {
+                    envelopeCanceler = null;
+                }
+            });
+            client.send(mesh_ev);
+            envelopeCanceler = mesh_ev;
+            ack_count -= 1;
+        }
+
+        @Override
+        protected void retract() {
+            if (envelopeCanceler == null)
+                throw new AssertionError();
+            envelopeCanceler.cancel();
+            envelopeCanceler = null;
+        }
+
+        public T getContext() {
+            return context;
+        }
+
+        public void setContext(T newContext) {
+            context = newContext;
+        }
+
+    }
+
+    private class MeshMessageReceiver extends RunaboutMessageReceiver {
+        public void visit(TunnelCreateMessage m) {
+            Tunnel t = new Tunnel(m.otherEnd, m.tunnel_id, m.port,
+                    (m.opt & OPTION_NOBUFFER) != 0, (m.opt & OPTION_NOBUFFER) 
!= 0);
+            if (inboundTunnelHandler != null) {
+                inboundTunnelHandler.onInboundTunnel(t, m.otherEnd);
+            }
+        }
+
+        public void visit(DataMessage m) {
+            Tunnel t = tunnelMap.get(m.tid);
+            if (t != null)
+            {
+                if (t.receive_done_expected)
+                    logger.warn("got unexpected message from service");
+                t.receive_done_expected = true;
+                messageReceiver.visitAppropriate(Construct.parseAs(m.payload, 
GnunetMessage.class).body);
+            }
+        }
+
+        public void visit(LocalAckMessage m) {
+            Tunnel t = tunnelMap.get(m.tid);
+            if (t != null)
+                t.ack_count += 1;
+        }
+
+        public void visit(TunnelDestroyMessage m) {
+            Tunnel t = tunnelMap.get(m.tunnel_id);
+            if (t == null) {
+                logger.warn("server got confused with tunnel IDs on destroy, 
ignoring message");
+                return;
+            }
+            t.destroy();
+            tunnelEndHandler.onTunnelEnd(t);
+        }
+
+        @Override
+        public void handleError() {
+            if (tunnelEndHandler != null) {
+                for (Tunnel t : tunnelMap.values()) {
+                    tunnelEndHandler.onTunnelEnd(t);
+                }
+            }
+            tunnelMap.clear();
+            client.reconnect();
+            ClientConnectMessage ccm = new ClientConnectMessage();
+            ccm.apps_list = ports;
+            client.send(ccm);
+        }
+    }
+
+    /**
+     * Connect to the mesh service.
+     *
+     * @param cfg                  configuration to use
+     * @param inboundTunnelHandler function called when an *inbound* tunnel is 
created
+     * @param tunnelEndHandler     function called when an *inbound* tunnel is 
destroyed by the
+     *                             remote peer, it is *not* called if 
Tunnel.destroy
+     *                             is called on the tunnel
+     */
+    public Mesh(Configuration cfg, InboundTunnelHandler inboundTunnelHandler,
+                TunnelEndHandler tunnelEndHandler, MeshRunabout 
messageReceiver, int... ports) {
+        this.tunnelEndHandler = tunnelEndHandler;
+        this.messageReceiver = messageReceiver;
+        this.ports = ports;
+        this.inboundTunnelHandler = inboundTunnelHandler;
+
+        client = new Client("mesh", cfg);
+        client.installReceiver(new MeshMessageReceiver());
+        ClientConnectMessage ccm = new ClientConnectMessage();
+        ccm.apps_list = ports;
+        client.send(ccm);
+    }
+
+    public <T> Tunnel<T> createTunnel(PeerIdentity peer, int port, boolean 
nobuffer, boolean reliable, T initialContext) {
+        return new Tunnel<T>(peer, port, nobuffer, reliable, initialContext);
+    }
+
+    /**
+     * Disconnect from the mesh service.
+     * All tunnels will be destroyed.
+     * All tunnel disconnect callbacks will be called on any still connected 
peers, notifying
+     * about their disconnection.
+     */
+    public void destroy() {
+        client.disconnect();
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/mesh/MeshRunabout.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mesh/MeshRunabout.java                 
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/mesh/MeshRunabout.java 2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,19 @@
+package org.gnunet.mesh;
+
+import org.gnunet.util.PeerIdentity;
+import org.grothoff.Runabout;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public class MeshRunabout extends Runabout {
+    private PeerIdentity sender;
+    /* package private */ void setSender(PeerIdentity sender) {
+        this.sender = sender;
+    }
+    public PeerIdentity getSender() {
+        return sender;
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/mesh/TunnelCreateMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mesh/TunnelCreateMessage.java          
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/mesh/TunnelCreateMessage.java  
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,27 @@
+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;
+
+/**
+ * FIXME
+ *
+ * @author Florian Dold
+ */
address@hidden(273)
+public class TunnelCreateMessage implements GnunetMessage.Body {
+    @UInt32
+    public int tunnel_id;
+
+    @NestedMessage(optional = false)
+    public PeerIdentity otherEnd;
+
+    @UInt32
+    public int port;
+
+    @UInt32
+    public int opt;
+}

Added: gnunet-java/src/main/java/org/gnunet/mesh/TunnelDestroyMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mesh/TunnelDestroyMessage.java         
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/mesh/TunnelDestroyMessage.java 
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,16 @@
+package org.gnunet.mesh;
+
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
address@hidden(274)
+public class TunnelDestroyMessage implements GnunetMessage.Body {
+    @UInt32
+    public int tunnel_id;
+}

Added: gnunet-java/src/main/java/org/gnunet/mesh/TunnelEndHandler.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mesh/TunnelEndHandler.java             
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/mesh/TunnelEndHandler.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,10 @@
+package org.gnunet.mesh;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public interface TunnelEndHandler {
+    void onTunnelEnd(Mesh.Tunnel tunnel);
+}

Added: gnunet-java/src/main/java/org/gnunet/mesh/TunnelNotificationMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mesh/TunnelNotificationMessage.java    
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/mesh/TunnelNotificationMessage.java    
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,22 @@
+package org.gnunet.mesh;
+
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UInt32;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public class TunnelNotificationMessage implements GnunetMessage.Body {
+    @UInt32
+    public int tunnel_id;
+    /**
+     * Peer at the other end, if any
+     * TODO: ask bart what 'if any' means here
+     */
+    @NestedMessage
+    public PeerIdentity peer;
+}

Added: gnunet-java/src/main/java/org/gnunet/mesh/package-info.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mesh/package-info.java                 
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/mesh/package-info.java 2013-08-27 
17:16:18 UTC (rev 28880)
@@ -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;

Added: gnunet-java/src/main/java/org/gnunet/mq/Envelope.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mq/Envelope.java                       
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/mq/Envelope.java       2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,36 @@
+package org.gnunet.mq;
+
+import org.gnunet.util.Cancelable;
+import org.gnunet.util.GnunetMessage;
+
+/**
+ * Container for a message to be sent by a message queue.
+ */
+public class Envelope implements Cancelable {
+    public final GnunetMessage.Body message;
+    private MessageQueue parent_queue;
+    private NotifySentHandler notify_sent_handler;
+
+    public Envelope(GnunetMessage.Body message) {
+        this.message = message;
+    }
+
+    public void notifySent(NotifySentHandler h) {
+        this.notify_sent_handler = h;
+    }
+
+    public void injectSent() {
+        if (notify_sent_handler != null)
+            notify_sent_handler.onSent();
+    }
+
+    public void cancel() {
+        if (parent_queue == null)
+            throw new AssertionError("can not cancel an unqueued message");
+    }
+
+    /* pkg-private */ void invokeSentNotification() {
+        if (null != notify_sent_handler)
+            notify_sent_handler.onSent();
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/mq/MessageQueue.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mq/MessageQueue.java                   
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/mq/MessageQueue.java   2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,84 @@
+package org.gnunet.mq;
+
+
+import org.gnunet.util.GnunetMessage;
+
+import java.util.LinkedList;
+
+/**
+ * General-purpose message queue
+ */
+public abstract class MessageQueue {
+    private LinkedList<Envelope> queued_envelopes = new LinkedList<Envelope>();
+    private LinkedList<Envelope> prefered_queued_envelopes = new 
LinkedList<Envelope>();
+    protected Envelope current_envelope;
+
+    protected abstract void submit(Envelope ev);
+
+    protected abstract void retract();
+
+    public void send(GnunetMessage.Body body) {
+        send(new Envelope(body));
+    }
+
+    public void sendPrefered(GnunetMessage.Body body) {
+        sendPrefered(new Envelope(body));
+    }
+
+    private Envelope pollNextEnvelope() {
+        if (!prefered_queued_envelopes.isEmpty())
+            return prefered_queued_envelopes.removeFirst();
+        if (!queued_envelopes.isEmpty())
+            return queued_envelopes.removeFirst();
+        return null;
+    }
+
+    public void send(Envelope ev) {
+        if (null == current_envelope) {
+            current_envelope = ev;
+            submit(current_envelope);
+        } else {
+            queued_envelopes.addLast(ev);
+        }
+    }
+
+    public void sendPrefered(Envelope ev) {
+        if (null == current_envelope) {
+            current_envelope = ev;
+            submit(current_envelope);
+        } else {
+            prefered_queued_envelopes.addLast(ev);
+        }
+    }
+
+    protected void reportMessageSent() {
+        if (null == current_envelope)
+            throw new AssertionError();
+        current_envelope.invokeSentNotification();
+        next();
+    }
+
+    private void next() {
+        current_envelope = pollNextEnvelope();
+        if (current_envelope == null)
+            return;
+        submit(current_envelope);
+    }
+
+    /**
+     * Cancel sending an envelope. The envelope must be queued in this message 
queue.
+     *
+     * @param ev the envelope to cancel
+     */
+    /* pkg-private */ void cancelEnvelope(Envelope ev) {
+        if (null == current_envelope)
+            throw new AssertionError();
+        if (ev == current_envelope) {
+            retract();
+            next();
+        } else {
+            queued_envelopes.remove(ev);
+            prefered_queued_envelopes.remove(ev);
+        }
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/mq/NotifySentHandler.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mq/NotifySentHandler.java              
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/mq/NotifySentHandler.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,6 @@
+package org.gnunet.mq;
+
+
+public interface NotifySentHandler {
+    void onSent();
+}

Added: gnunet-java/src/main/java/org/gnunet/nse/NetworkSizeEstimation.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/nse/NetworkSizeEstimation.java         
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/nse/NetworkSizeEstimation.java 
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,167 @@
+/*
+ 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.util.*;
+import org.gnunet.util.getopt.Argument;
+import org.gnunet.util.getopt.ArgumentAction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+
+/**
+ * An API for the network size estimation service.
+ *
+ * @author Florian Dold
+ */
+public class NetworkSizeEstimation {
+    private static final Logger logger = LoggerFactory
+            .getLogger(NetworkSizeEstimation.class);
+
+    private Collection<Subscriber> subscribers = new HashSet<Subscriber>(1);
+    private boolean disconnected = false;
+
+    private Client client;
+
+    private class NSE_Receiver extends RunaboutMessageReceiver {
+        public void visit(UpdateMessage uMsg) {
+            for (Subscriber s : subscribers) {
+                s.update(AbsoluteTime.fromNetwork(uMsg.timestamp), 
uMsg.sizeEstimate, uMsg.stdDeviation);
+            }
+
+            if (!disconnected) {
+                client.receiveOne(RelativeTime.FOREVER, this);
+            }
+        }
+
+        @Override
+        public void handleError() {
+            logger.warn("NSE connection lost - trying to reconnect");
+            client.reconnect();
+            requestUpdate();
+        }
+    }
+
+    private class NSE_Transmitter implements MessageTransmitter {
+        @Override
+        public void transmit(Connection.MessageSink sink) {
+            StartMessage m = new StartMessage();
+            sink.send(m);
+            client.receiveOne(RelativeTime.FOREVER, new NSE_Receiver());
+        }
+
+        @Override
+        public void handleError() {
+            logger.warn("NSE connection lost - trying to reconnect");
+            client.reconnect();
+            requestUpdate();
+
+        }
+    }
+
+
+    /**
+     * A handle for a subscription to the network size estimation service, may 
be used to cancel the
+     * subscription.
+     */
+    public class Subscription implements Cancelable {
+        private Subscriber sub;
+
+        private Subscription(Subscriber sub) {
+            this.sub = sub;
+        }
+
+        /**
+         * Cancel the subscription.
+         */
+        public void cancel() {
+            subscribers.remove(sub);
+        }
+    }
+
+    /**
+     * A NSE_Subscriber receives updates from the service.
+     */
+    public interface Subscriber {
+        public void update(AbsoluteTime timestamp, double estimate, double 
deviation);
+    }
+
+    /**
+     * Subscribe for updates from the service.
+     *
+     * @param s callback for updates
+     * @return a subscription handle that may be used to cancel the 
subscription
+     */
+    public Cancelable subscribe(Subscriber s) {
+        subscribers.add(s);
+        requestUpdate();
+        return new Subscription(s);
+    }
+
+    /**
+     * Create a connection to the network size estimation service.
+     *
+     * @param cfg the configuration to use for connecting with the service
+     */
+    public NetworkSizeEstimation(Configuration cfg) {
+        client = new Client("nse", cfg);
+    }
+
+    private void requestUpdate() {
+        client.notifyTransmitReady(RelativeTime.FOREVER, true, 0, new 
NSE_Transmitter());
+    }
+
+    /**
+     * Cancel all subscriptions and disconnect from the service.
+     */
+    public void disconnect() {
+        disconnected = true;
+    }
+
+    public static void main(String[] args) {
+        new Program(args) {
+            @Argument(action = ArgumentAction.SET,
+                    shortname = "w",
+                    longname = "watch",
+                    description = "wait and watch for more NSE updates")
+            boolean cont = false;
+
+            public void run() {
+                final NetworkSizeEstimation svc = new 
NetworkSizeEstimation(cfg);
+
+                Subscriber subscriber = new Subscriber() {
+                    @Override
+                    public void update(AbsoluteTime timestamp, double 
estimate, double deviation) {
+                        System.out.println("est: " + estimate + " dev: " + 
deviation + " t: " + timestamp.toDate());
+                        if (!cont) {
+                            svc.disconnect();
+                        }
+                    }
+                };
+                svc.subscribe(subscriber);
+            }
+        }.start();
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/nse/StartMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/nse/StartMessage.java                  
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/nse/StartMessage.java  2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,12 @@
+package org.gnunet.nse;
+
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
+/**
+* ...
+*
+* @author Florian Dold
+*/
address@hidden(321)
+public class StartMessage implements GnunetMessage.Body {}

Added: gnunet-java/src/main/java/org/gnunet/nse/UpdateMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/nse/UpdateMessage.java                 
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/nse/UpdateMessage.java 2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,26 @@
+package org.gnunet.nse;
+
+import org.gnunet.construct.*;
+import org.gnunet.construct.DoubleValue;
+import org.gnunet.util.AbsoluteTimeMessage;
+import org.gnunet.util.GnunetMessage;
+
+/**
+* ...
+*
+* @author Florian Dold
+*/
address@hidden(323)
+public class UpdateMessage implements GnunetMessage.Body {
+    @UInt32
+    public int reserved;
+
+    @NestedMessage
+    public AbsoluteTimeMessage timestamp;
+
+    @DoubleValue
+    public double sizeEstimate;
+
+    @DoubleValue
+    public double stdDeviation;
+}

Added: gnunet-java/src/main/java/org/gnunet/nse/package-info.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/nse/package-info.java                  
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/nse/package-info.java  2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,24 @@
+/*
+ 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.
+ */
+
+/**
+ * API for the gnunet nse service.
+ */
+package org.gnunet.nse;

Added: gnunet-java/src/main/java/org/gnunet/peerinfo/InfoEnd.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/peerinfo/InfoEnd.java                  
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/peerinfo/InfoEnd.java  2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,14 @@
+package org.gnunet.peerinfo;
+
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
address@hidden(333)
+public class InfoEnd implements GnunetMessage.Body {
+
+}

Added: gnunet-java/src/main/java/org/gnunet/peerinfo/InfoMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/peerinfo/InfoMessage.java              
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/peerinfo/InfoMessage.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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.peerinfo;
+
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.hello.HelloMessage;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * @author Florian Dold
+ */
address@hidden(332)
+public class InfoMessage implements GnunetMessage.Body {
+    /**
+     * Always zero.
+     */
+    @UInt32
+    public int reserved;
+    /**
+     * About which peer are we talking here?
+     */
+    @NestedMessage
+    public PeerIdentity peerIdentity;
+    /**
+     * HELLO of the peer, null if no HELLO present.
+     */
+    @NestedMessage(optional = true)
+    public HelloMessage hello;
+}

Added: gnunet-java/src/main/java/org/gnunet/peerinfo/ListAllPeersMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/peerinfo/ListAllPeersMessage.java      
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/peerinfo/ListAllPeersMessage.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,39 @@
+/*
+ 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.peerinfo;
+
+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 requesting a listing of all known peers,
+ * possibly restricted to the specified peer identity.
+ *
+ * @author Florian Dold
+ */
address@hidden(331)
+public class ListAllPeersMessage implements GnunetMessage.Body {
+    @UInt32
+    public int include_friend_only;
+}

Added: gnunet-java/src/main/java/org/gnunet/peerinfo/ListPeerMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/peerinfo/ListPeerMessage.java          
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/peerinfo/ListPeerMessage.java  
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,39 @@
+/*
+ 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.peerinfo;
+
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UInt32;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * Message requesting a listing of all known peers,
+ * possibly restricted to the specified peer identity.
+ *
+ * @author Florian Dold
+ */
+public class ListPeerMessage implements GnunetMessage.Body {
+    @UInt32
+    public int reserved;
+    @NestedMessage
+    public PeerIdentity peer;
+}

Added: gnunet-java/src/main/java/org/gnunet/peerinfo/PeerInfo.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/peerinfo/PeerInfo.java                 
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/peerinfo/PeerInfo.java 2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,159 @@
+/*
+ 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.peerinfo;
+
+import org.gnunet.hello.HelloMessage;
+import org.gnunet.mq.Envelope;
+import org.gnunet.mq.MessageQueue;
+import org.gnunet.requests.RequestContainer;
+import org.gnunet.requests.SequentialRequestContainer;
+import org.gnunet.util.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Interface to the service that maintains all known hosts.
+ *
+ * @author Florian Dold
+ */
+public class PeerInfo {
+    private static final Logger logger = LoggerFactory
+            .getLogger(AbsoluteTime.class);
+
+    /**
+     * Client that connects to the peerinfo service.
+     */
+    private final Client client;
+
+    /**
+     * All currently active iterate requests.
+     */
+    private SequentialRequestContainer<PeerIterateRequest> iterate_requests;
+
+    private class PeerIterateRequest extends RequestContainer.Request {
+        public PeerIdentity peer;
+        public PeerProcessor peerProcessor;
+        public boolean friend_only;
+        public boolean canceled;
+
+        public PeerIterateRequest(PeerIdentity peer, boolean friend_only, 
PeerProcessor peerProcessor) {
+            this.peer = peer;
+            this.peerProcessor = peerProcessor;
+            this.friend_only = friend_only;
+        }
+
+        @Override
+        public Envelope assembleRequest() {
+            if (peer == null) {
+                ListAllPeersMessage m = new ListAllPeersMessage();
+                m.include_friend_only = friend_only ? 1 : 0;
+                return new Envelope(m);
+            } else {
+                ListPeerMessage m = new ListPeerMessage();
+                m.peer = peer;
+                return new Envelope(m);
+            }
+        }
+
+        public void cancel() {
+            canceled = true;
+        }
+    }
+
+    private class PeerInfoMessageReceiver extends RunaboutMessageReceiver {
+        public void visit(InfoEnd infoEnd) {
+            PeerIterateRequest r = iterate_requests.getRequest();
+            if (!r.canceled)
+                r.peerProcessor.onEnd();
+            iterate_requests.next();
+        }
+        public void visit(InfoMessage infoMessage) {
+            PeerIterateRequest r = iterate_requests.getRequest();
+            if (!r.canceled)
+                r.peerProcessor.onPeer(infoMessage.peerIdentity, 
infoMessage.hello);
+        }
+
+        @Override
+        public void handleError() {
+            client.reconnect();
+            iterate_requests.restart();
+        }
+    }
+
+
+    public PeerInfo(Configuration cfg) {
+        client = new Client("peerinfo", cfg);
+        client.installReceiver(new PeerInfoMessageReceiver());
+        iterate_requests = new 
SequentialRequestContainer<PeerIterateRequest>(client);
+        // Make sure that new requests are only sent once the old request has 
finished.
+        // Otherwise, the peerinfo service would send the answers interleaved.
+        iterate_requests.setOverlap(false);
+    }
+
+    /**
+     * Iterates over the HELLOs of all peers.
+     *
+     * @param timeout
+     * @param processor
+     * @return
+     */
+    public Cancelable iterate(RelativeTime timeout, boolean friend_only, 
PeerProcessor processor) {
+        return iterate(timeout, null, friend_only, processor);
+    }
+
+    /**
+     * Iterates over the HELLOs of the given peer.
+     * Can be called with peer=null to iterate over all peers.
+     * @param timeout
+     * @param peer
+     * @param processor
+     * @return
+     */
+    public Cancelable iterate(RelativeTime timeout, PeerIdentity peer, boolean 
friend_only, PeerProcessor processor) {
+        PeerIterateRequest r = new PeerIterateRequest(peer, friend_only, 
processor);
+        return iterate_requests.addRequest(r);
+    }
+
+    public void disconnect() {
+        client.disconnect();
+    }
+
+    public static void main(String... args) {
+        new Program(args) {
+            @Override
+            public void run() {
+                final PeerInfo peerInfo = new PeerInfo(getConfiguration());
+                peerInfo.iterate(RelativeTime.FOREVER, false, new 
PeerProcessor() {
+                    @Override
+                    public void onPeer(PeerIdentity peerIdentity, HelloMessage 
hello) {
+                        System.out.println("peer " + peerIdentity.toString());
+                    }
+
+                    @Override
+                    public void onEnd() {
+                        System.out.println("got peer end");
+                        peerInfo.disconnect();
+                    }
+                });
+            }
+        }.start();
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/peerinfo/PeerProcessor.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/peerinfo/PeerProcessor.java            
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/peerinfo/PeerProcessor.java    
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,12 @@
+package org.gnunet.peerinfo;
+
+import org.gnunet.hello.HelloMessage;
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * Callback class to receive known peers and their HELLOs.
+ */
+public interface PeerProcessor {
+    public void onPeer(PeerIdentity peerIdentity, HelloMessage hello);
+    public void onEnd();
+}

Added: 
gnunet-java/src/main/java/org/gnunet/peerinfo/RsaPublicKeyBinaryEncoded.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/peerinfo/RsaPublicKeyBinaryEncoded.java    
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/peerinfo/RsaPublicKeyBinaryEncoded.java    
    2013-08-27 17:16:18 UTC (rev 28880)
@@ -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.peerinfo;
+
+import org.gnunet.construct.FixedSizeIntegerArray;
+import org.gnunet.construct.Message;
+import org.gnunet.construct.UInt16;
+import org.gnunet.construct.UInt8;
+
+/**
+ * @author Florian Dold
+ */
+public class RsaPublicKeyBinaryEncoded implements Message {
+    public static final int GNUNET_CRYPTO_RSA_KEY_LENGTH = 258;
+
+    /**
+     * In big-endian, must be GNUNET_CRYPTO_RSA_KEY_LENGTH+4
+     */
+    @UInt16
+    public int len;
+    /**
+     * Size of n in key; in big-endian!
+     */
+    @UInt16
+    public int sizen;
+
+    /**
+     * The key itself, contains n followed by e.
+     */
+    @FixedSizeIntegerArray(length = 
RsaPublicKeyBinaryEncoded.GNUNET_CRYPTO_RSA_KEY_LENGTH, signed = false, bitSize 
= 8)
+    public byte[] key;
+
+    /**
+     * Padding.
+     */
+    @UInt8
+    public byte reserved;
+}

Added: gnunet-java/src/main/java/org/gnunet/peerinfo/package-info.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/peerinfo/package-info.java             
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/peerinfo/package-info.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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;

Added: 
gnunet-java/src/main/java/org/gnunet/requests/MatchingRequestContainer.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/requests/MatchingRequestContainer.java 
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/requests/MatchingRequestContainer.java 
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,51 @@
+package org.gnunet.requests;
+
+import com.google.common.collect.Maps;
+import org.gnunet.mq.MessageQueue;
+import org.gnunet.util.Cancelable;
+
+import java.util.Map;
+
+
+public class MatchingRequestContainer<K, T extends RequestContainer.Request> 
extends RequestContainer {
+    private Map<K,T> requests = Maps.newHashMap();
+    private final MessageQueue mq;
+
+    public MatchingRequestContainer(MessageQueue mq) {
+        this.mq = mq;
+    }
+
+    public Cancelable addRequest(K key, final T request) {
+        if (requests.containsKey(key))
+            throw new AssertionError("key already present in request 
container");
+        requests.put(key, request);
+        mq.send(request.assembleRequest());
+        return new Cancelable() {
+            @Override
+            public void cancel() {
+                setRequestCancelled(request);
+                if (isRequestTransmitting(request)) {
+                    cancelRequestTransmission(request);
+                } else {
+                    request.cancel();
+                }
+            }
+        };
+    }
+
+    @Override
+    public void restart() {
+        Map<K,T> requestsOld = requests;
+        requests = Maps.newHashMap();
+        for (Map.Entry<K,T> e : requestsOld.entrySet()) {
+            if (!isRequestCancelled(e.getValue())) {
+                setRequestTransmitting(e.getValue(), false);
+                addRequest(e.getKey(), e.getValue());
+            }
+        }
+    }
+
+    public T getRequest(K key) {
+        return requests.get(key);
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/requests/RequestContainer.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/requests/RequestContainer.java         
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/requests/RequestContainer.java 
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,59 @@
+package org.gnunet.requests;
+
+
+import org.gnunet.mq.Envelope;
+import org.gnunet.util.Cancelable;
+
+public abstract class RequestContainer {
+    protected boolean overlap = true;
+
+    public abstract static class Request {
+        private boolean transmitting;
+        private boolean canceled;
+        private Cancelable cancelRequest;
+        public abstract Envelope assembleRequest();
+        public void cancel() {
+            throw new AssertionError("request of type " + this.getClass() + " 
can not be canceled (not implemented)");
+        }
+    }
+
+    /**
+     * Re-send all requests in the queue that have not been canceled.
+     */
+    public abstract void restart();
+
+    /**
+     * Allow or disallow requests to be send while other requests in the queue 
have not been completed.
+     *
+     * @param overlap true to allow overlapped requests, false to disallow them
+     */
+    public void setOverlap(boolean overlap) {
+        this.overlap = overlap;
+    }
+
+    protected boolean isRequestTransmitting(Request r) {
+        return r.transmitting;
+    }
+
+    protected void setRequestTransmitting(Request r, boolean transmitting) {
+        r.transmitting = transmitting;
+    }
+
+    protected void setRequestTransmissionCancel(Request request, Cancelable 
cancel) {
+        request.cancelRequest = cancel;
+    }
+
+    protected void cancelRequestTransmission(Request r) {
+        r.cancelRequest.cancel();
+        r.cancelRequest = null;
+    }
+
+    protected void setRequestCancelled(Request r) {
+        r.canceled = true;
+    }
+
+    protected boolean isRequestCancelled(Request r) {
+        return r.canceled;
+    }
+
+}

Added: 
gnunet-java/src/main/java/org/gnunet/requests/SequentialRequestContainer.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/requests/SequentialRequestContainer.java   
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/requests/SequentialRequestContainer.java   
    2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,86 @@
+package org.gnunet.requests;
+
+import org.gnunet.mq.Envelope;
+import org.gnunet.mq.MessageQueue;
+import org.gnunet.mq.NotifySentHandler;
+import org.gnunet.util.Cancelable;
+
+import java.util.LinkedList;
+
+/**
+ * Container for requests that are responded to in sequential order.
+ */
+public class SequentialRequestContainer<T extends RequestContainer.Request> 
extends RequestContainer {
+    private LinkedList<T> requests = new LinkedList<T>();
+    private MessageQueue mq;
+
+    int requestsActive = 0;
+
+    public SequentialRequestContainer(MessageQueue mq) {
+        this.mq = mq;
+    }
+
+    public T getRequest() {
+        return requests.getFirst();
+    }
+
+    public void next() {
+        if (requestsActive == 0 || requests.isEmpty())
+            throw new AssertionError();
+        requestsActive--;
+        requests.removeFirst();
+        if (requestsActive == 0 && !requests.isEmpty()) {
+            Request r = requests.getFirst();
+            setRequestTransmitting(r, true);
+            Envelope ev = r.assembleRequest();
+            setRequestTransmissionCancel(r, ev);
+            mq.send(r.assembleRequest());
+            requestsActive++;
+        }
+    }
+
+    public Cancelable addRequest(final T request) {
+        requests.addLast(request);
+        if (overlap || requestsActive == 0) {
+            requestsActive++;
+            setRequestTransmitting(request, true);
+            Envelope ev = request.assembleRequest();
+            ev.notifySent(new NotifySentHandler() {
+                @Override
+                public void onSent() {
+                    setRequestTransmitting(request, false);
+                }
+            });
+            setRequestTransmissionCancel(request, ev);
+            mq.send(request.assembleRequest());
+        }
+        return new Cancelable() {
+            @Override
+            public void cancel() {
+                setRequestCancelled(request);
+                if (isRequestTransmitting(request)) {
+                    cancelRequestTransmission(request);
+                } else {
+                    request.cancel();
+                }
+            }
+        };
+    }
+
+    @Override
+    public void restart() {
+        LinkedList<T> requestsOld = requests;
+        requests = new LinkedList<T>();
+        for (T r : requestsOld) {
+            if (!isRequestCancelled(r)) {
+                setRequestTransmitting(r, false);
+                addRequest(r);
+            }
+        }
+    }
+
+
+    public Iterable<T> iter() {
+        return requests;
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/requests/package-info.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/requests/package-info.java             
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/requests/package-info.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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;

Added: gnunet-java/src/main/java/org/gnunet/statistics/GetMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/statistics/GetMessage.java             
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/statistics/GetMessage.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,43 @@
+/*
+ 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.statistics;
+
+import org.gnunet.construct.UnionCase;
+import org.gnunet.construct.ZeroTerminatedString;
+import org.gnunet.util.GnunetMessage;
+
+/**
+ * Client --> Service
+ *
+ */
address@hidden(169)
+public class GetMessage implements GnunetMessage.Body {
+    /**
+     * Subsystem of interest, empty string for all subsystems.
+     */
+    @ZeroTerminatedString
+    public String subsystemName;
+    /**
+     * Statistics value name of interest, empty string for all values.
+     */
+    @ZeroTerminatedString
+    public String statisticsName;
+}

Added: gnunet-java/src/main/java/org/gnunet/statistics/GetRequest.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/statistics/GetRequest.java             
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/statistics/GetRequest.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,31 @@
+package org.gnunet.statistics;
+
+import org.gnunet.mq.Envelope;
+import org.gnunet.mq.MessageQueue;
+import org.gnunet.requests.RequestContainer;
+
+/**
+ */
+public class GetRequest extends RequestContainer.Request {
+    private final String subsystem;
+    private final String name;
+    public final StatisticsReceiver receiver;
+    private RequestContainer parent;
+
+    public GetRequest(String subsystem, String name, StatisticsReceiver 
receiver) {
+        this.subsystem = subsystem;
+        this.name = name;
+        this.receiver = receiver;
+    }
+
+    @Override
+    public Envelope assembleRequest() {
+        GetMessage m = new GetMessage();
+        m.subsystemName = subsystem;
+        m.statisticsName = name;
+        return new Envelope(m);
+    }
+
+    public void cancel() {
+    }
+}

Added: 
gnunet-java/src/main/java/org/gnunet/statistics/GetResponseEndMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/statistics/GetResponseEndMessage.java  
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/statistics/GetResponseEndMessage.java  
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,32 @@
+/*
+ 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.statistics;
+
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
+/**
+ * ...
+*/
address@hidden(171)
+public class GetResponseEndMessage implements GnunetMessage.Body {
+    // empty
+}

Added: gnunet-java/src/main/java/org/gnunet/statistics/GetResponseMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/statistics/GetResponseMessage.java     
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/statistics/GetResponseMessage.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,49 @@
+/*
+ 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.statistics;
+
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UInt64;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.construct.ZeroTerminatedString;
+import org.gnunet.util.GnunetMessage;
+
+/**
+ * service --> client
+ *
+ *
+ */
address@hidden(170)
+public class GetResponseMessage implements GnunetMessage.Body {
+    /**
+     * Unique numerical identifier for the value (will
+     * not change during the same client-session).  Highest
+     * bit will be set for persistent values.
+     */
+    @UInt32
+    public long uid;
+    @UInt64
+    public long value;
+    @ZeroTerminatedString
+    public String subsystemName;
+    @ZeroTerminatedString
+    public String statisticName;
+}

Added: gnunet-java/src/main/java/org/gnunet/statistics/SetMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/statistics/SetMessage.java             
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/statistics/SetMessage.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,43 @@
+/*
+ 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.statistics;
+
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UInt64;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.construct.ZeroTerminatedString;
+import org.gnunet.util.GnunetMessage;
+
+
+/**
+ * Sent to the service by the client to set a statistics value.
+ */
address@hidden(168)
+public class SetMessage implements GnunetMessage.Body {
+    @UInt32
+    public int flags;
+    @UInt64
+    public long value;
+    @ZeroTerminatedString
+    public String subsystemName;
+    @ZeroTerminatedString
+    public String statisticName;
+}

Added: gnunet-java/src/main/java/org/gnunet/statistics/SetRequest.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/statistics/SetRequest.java             
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/statistics/SetRequest.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,48 @@
+package org.gnunet.statistics;
+
+import org.gnunet.mq.Envelope;
+import org.gnunet.mq.MessageQueue;
+import org.gnunet.requests.RequestContainer;
+import org.gnunet.util.RelativeTime;
+
+
+public class SetRequest extends RequestContainer.Request {
+    /**
+     * Time after we give up on setting values in statistics
+     */
+    private static final RelativeTime SET_TIMEOUT = 
RelativeTime.SECOND.multiply(10);
+
+    private final static int SETFLAG_RELATIVE = 1;
+    private final static int SETFLAG_PERSIST = 2;
+    private final String subsystem;
+    private final String name;
+    private final boolean persist;
+    private final long value;
+    private final boolean relative;
+
+    public SetRequest(String subsystem, String name, long value, boolean 
relative, boolean persist) {
+        this.subsystem = subsystem;
+        this.name = name;
+        this.persist = persist;
+        this.value = value;
+        this.relative = relative;
+
+    }
+
+    @Override
+    public Envelope assembleRequest() {
+        SetMessage m = new SetMessage();
+        m.statisticName = name;
+        m.subsystemName = subsystem;
+        m.value = value;
+        if (relative)
+            m.flags |= SETFLAG_RELATIVE;
+        if (persist)
+            m.flags |= SETFLAG_PERSIST;
+        return new Envelope(m);
+    }
+
+    public void cancel() {
+        //To change body of implemented methods use File | Settings | File 
Templates.
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/statistics/Statistics.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/statistics/Statistics.java             
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/statistics/Statistics.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,310 @@
+/*
+ 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 stuff below does nothing whatsoever, first milestone of
+ * this project is to implement the StatisticsService api
+ * 
+ */
+
+package org.gnunet.statistics;
+
+import org.gnunet.requests.MatchingRequestContainer;
+import org.gnunet.requests.SequentialRequestContainer;
+import org.gnunet.util.*;
+import org.gnunet.util.getopt.Argument;
+import org.gnunet.util.getopt.ArgumentAction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * API for the GNUnet statistics service.
+ * <p/>
+ * Set, get and monitor statistics values, represented as unsigned 64bit 
integer.
+ * Note that address@hidden long}, java's largest primitive type, can only 
store signed 64bit integers.
+ * With absolute operation, its negative values are interpreted as large 
numbers by the statistics api.
+ */
+public class  Statistics {
+    private static final Logger logger = LoggerFactory
+            .getLogger(Statistics.class);
+
+    /**
+     * Client connecting us to the statistics service.
+     */
+    private final Client client;
+
+    SequentialRequestContainer<SetRequest> set_requests;
+    SequentialRequestContainer<GetRequest> get_requests;
+    MatchingRequestContainer<Long,WatchRequest> watch_requests;
+
+    private boolean destroy_requested;
+
+    private long wid = 0;
+
+    public class StatisticsMessageReceiver extends RunaboutMessageReceiver {
+        public void visit(GetResponseMessage m) {
+            GetRequest r = get_requests.getRequest();
+            if (r != null)
+                   r.receiver.onReceive(m.subsystemName, m.statisticName, 
m.value);
+        }
+
+        public void visit(GetResponseEndMessage m) {
+            GetRequest r = get_requests.getRequest();
+            if (r != null)
+                r.receiver.onDone();
+            //noinspection ConstantConditions
+            if (get_requests != null)
+                get_requests.next();
+        }
+
+        public void visit(TestMessage m) {
+            System.out.println("got back TEST message");
+            client.disconnect();
+        }
+
+        public void visit(WatchResponseMessage wrm) {
+            WatchRequest r = watch_requests.getRequest((long) wrm.wid);
+            if (r != null) {
+                r.watcher.onReceive(r.subsystem, r.name, wrm.value);
+            }
+        }
+
+        @Override
+        public void handleError() {
+            if (!destroy_requested) {
+                client.reconnect();
+                get_requests.restart();
+                set_requests.restart();
+                watch_requests.restart();
+            }
+        }
+    }
+
+    public Statistics(Configuration cfg) {
+        client = new Client("statistics", cfg);
+        client.installReceiver(new StatisticsMessageReceiver());
+        get_requests = new SequentialRequestContainer<GetRequest>(client);
+        set_requests = new SequentialRequestContainer<SetRequest>(client);
+        watch_requests = new MatchingRequestContainer<Long, 
WatchRequest>(client);
+    }
+
+    /**
+     * Retrieve values from statistics.
+     * Only one instance of this request may be active simultaneously.
+     * Upon cancellation
+     *
+     * @param timeout      time after we give up and call receiver.onTimeout
+     * @param subsystem    the subsystem of interest
+     * @param name         name of the statistics value belongs to
+     * @param receiver     callback
+     * @return handle to cancel the request
+     */
+    public Cancelable get(RelativeTime timeout, final String subsystem, final 
String name,
+                          final StatisticsReceiver receiver) {
+        if (destroy_requested)
+            throw new AssertionError("already destroyed");
+        return get_requests.addRequest(new GetRequest(subsystem, name, 
receiver));
+    }
+
+    /**
+     * Sets a statistics value asynchronously.
+     *
+     * @param name    name of the entry
+     * @param value   desired value
+     * @param persist keep value even if the statistics service restarts
+     * @return a handle to cancel the request
+     */
+    public Cancelable set(final String subsystem, final String name, final 
long value, boolean persist) {
+        if (destroy_requested)
+            throw new AssertionError("already destroyed");
+        return set_requests.addRequest(new SetRequest(subsystem, name, value, 
false, persist));
+    }
+
+    /**
+     * Changes a statistics value asynchronously.
+     *
+     * @param name    name of the entry
+     * @param delta   relative difference to the old value
+     * @param persist keep value even if the statistics service restarts
+     * @return a handle to cancel the request
+     */
+    public Cancelable update(final String subsystem, final String name, final 
long delta, boolean persist) {
+        if (destroy_requested)
+            throw new AssertionError("already destroyed");
+        return set_requests.addRequest(new SetRequest(subsystem, name, delta, 
true, persist));
+    }
+
+    /**
+     * Receive updates about changing statistics values.
+     *
+     * @param subsystem the subsystem to watch
+     * @param name the value to watch
+     * @param watcher the object that receives the updates
+     * @return a handle to cancel the request
+     */
+    public Cancelable watch(final String subsystem, final String name, 
StatisticsWatcher watcher) {
+        if (destroy_requested)
+            throw new AssertionError("already destroyed");
+        WatchRequest r = new WatchRequest(subsystem, name, watcher);
+        return watch_requests.addRequest(wid++, r);
+    }
+
+    /**
+     * Destroy handle to the statistics service. Always finishes writing 
pending values.
+     */
+    public void destroy() {
+        destroy_requested = true;
+        client.send(new TestMessage());
+        // wait until the service responds
+        // TODO: or timeout
+        System.out.println("destroying statistics");
+    }
+
+
+    /**
+     * Statistics command line utility entry point
+     *
+     * @param args command line arguments
+     */
+    public static void main(String[] args) {
+        new Program(args) {
+            @Argument(
+                    shortname = "x",
+                    longname = "set",
+                    action = ArgumentAction.SET,
+                    description = "watch a value")
+            boolean set;
+            @Argument(
+                    shortname = "w",
+                    longname = "watch",
+                    action = ArgumentAction.SET,
+                    description = "set a value")
+            boolean watch;
+            @Argument(
+                    shortname = "n",
+                    longname = "name",
+                    action = ArgumentAction.STORE_STRING,
+                    argumentName = "NAME",
+                    description = "statistics name")
+            String statisticsName = "";
+            @Argument(
+                    shortname = "s",
+                    longname = "subsystem",
+                    argumentName = "SUBSYS",
+                    action = ArgumentAction.STORE_STRING,
+                    description = "subsystem name")
+            String subsystemName = "";
+            @Argument(
+                    shortname = "p",
+                    longname = "persistent",
+                    action = ArgumentAction.SET,
+                    description = "set value persistently (used with -x)")
+            boolean persistent = false;
+            @Argument(
+                    shortname = "r",
+                    longname = "relative",
+                    action = ArgumentAction.SET,
+                    description = "set value relative to old value (used with 
-x)")
+            boolean relative = false;
+
+            @Override
+            protected String makeHelpText() {
+                return "Get, set and watch GNUnet's statistics.";
+            }
+
+            public void run() {
+                final Statistics statistics = new Statistics(cfg);
+
+                if (set && watch) {
+                    System.err.println("--watch/-w and --set/-s cannot be used 
together");
+                    return;
+                }
+
+                if (set) {
+                    if (subsystemName.isEmpty() || statisticsName.isEmpty()) {
+                        System.err.println("both subsystem and name must be 
given for --set/-x");
+                        return;
+                    }
+                    if (unprocessedArgs.length != 1) {
+                        System.err.println("must specify exactly one value to 
set");
+                        return;
+                    }
+                    long value;
+                    try {
+                        value = Long.parseLong(unprocessedArgs[0]);
+                    } catch (NumberFormatException e) {
+                        System.err.println("invalid value (not a long)");
+                        return;
+                    }
+                    if (relative)
+                        statistics.update(subsystemName, statisticsName, 
value, persistent);
+                     else
+                        statistics.set(subsystemName, statisticsName, value, 
persistent);
+                    statistics.destroy();
+                    return;
+                }
+
+                if (unprocessedArgs.length != 0) {
+                    System.err.println("dumping statistics does not take any 
positional parameters");
+                    return;
+                }
+
+                if (watch) {
+                    if (subsystemName.isEmpty() || statisticsName.isEmpty()) {
+                        System.err.println("both subsystem and name must be 
given for --watch/-w");
+                        return;
+                    }
+                    statistics.watch(subsystemName, statisticsName,
+                            new StatisticsWatcher() {
+                                @Override
+                                public void onReceive(String subsystem, String 
name, long value) {
+                                    System.out.println(subsystem + "(" + name 
+ ") = " + value);
+                                }
+
+                                @Override
+                                public void onTimeout() {
+                                   System.err.println("timeout");
+                                }
+                            }
+                    );
+                } else {
+                    statistics.get(RelativeTime.SECOND, subsystemName, 
statisticsName,
+                            new StatisticsReceiver() {
+                                @Override
+                                public void onReceive(String subsystem, String 
name, long value) {
+                                    System.out.println(subsystem + "(" + name 
+ ") = " + value);
+                                }
+
+                                @Override
+                                public void onTimeout() {
+                                    System.err.println("timeout");
+                                }
+
+                                @Override
+                                public void onDone() {
+                                    statistics.destroy();
+                                }
+                            }
+                    );
+                }
+            }
+        }.start();
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/statistics/StatisticsReceiver.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/statistics/StatisticsReceiver.java     
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/statistics/StatisticsReceiver.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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.statistics;
+
+
+
+public interface StatisticsReceiver {
+    public void onReceive(String subsystem, String name, long value);
+    public void onTimeout();
+    public void onDone();
+}

Added: gnunet-java/src/main/java/org/gnunet/statistics/StatisticsWatcher.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/statistics/StatisticsWatcher.java      
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/statistics/StatisticsWatcher.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,13 @@
+package org.gnunet.statistics;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: dold
+ * Date: 8/24/13
+ * Time: 5:56 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public interface StatisticsWatcher {
+    public void onReceive(String subsystem, String name, long value);
+    public void onTimeout();
+}

Added: gnunet-java/src/main/java/org/gnunet/statistics/WatchMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/statistics/WatchMessage.java           
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/statistics/WatchMessage.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,40 @@
+/*
+ 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.statistics;
+
+
+import org.gnunet.construct.UnionCase;
+import org.gnunet.construct.ZeroTerminatedString;
+import org.gnunet.util.GnunetMessage;
+
address@hidden(172)
+public class WatchMessage implements GnunetMessage.Body {
+    /**
+     * Subsystem of interest, may not be empty.
+     */
+    @ZeroTerminatedString
+    public String subsystemName;
+    /**
+     * Statistics value name of interest, may not be empty.
+     */
+    @ZeroTerminatedString
+    public String statisticsName;
+}

Added: gnunet-java/src/main/java/org/gnunet/statistics/WatchRequest.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/statistics/WatchRequest.java           
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/statistics/WatchRequest.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,31 @@
+package org.gnunet.statistics;
+
+import org.gnunet.mq.Envelope;
+import org.gnunet.mq.MessageQueue;
+import org.gnunet.requests.RequestContainer;
+
+/**
+ */
+public class WatchRequest extends RequestContainer.Request {
+    public String subsystem;
+    public String name;
+    public StatisticsWatcher watcher;
+
+    public WatchRequest(String subsystem, String name, StatisticsWatcher 
watcher) {
+        this.subsystem = subsystem;
+        this.name = name;
+        this.watcher = watcher;
+    }
+
+    @Override
+    public Envelope assembleRequest() {
+        WatchMessage m = new WatchMessage();
+        m.statisticsName = name;
+        m.subsystemName = subsystem;
+        return new Envelope(m);
+    }
+
+    public void cancel() {
+
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/statistics/WatchResponseMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/statistics/WatchResponseMessage.java   
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/statistics/WatchResponseMessage.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,39 @@
+/*
+ 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.statistics;
+
+
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UInt64;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
address@hidden(173)
+public class WatchResponseMessage implements GnunetMessage.Body {
+    @UInt32
+    public int flags;
+    @UInt32
+    public int wid;
+    @UInt32
+    public int reserved ;
+    @UInt64
+    public long value;
+}

Added: gnunet-java/src/main/java/org/gnunet/statistics/package-info.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/statistics/package-info.java           
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/statistics/package-info.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,24 @@
+/*
+ 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.
+ */
+
+/**
+ * API for the gnunet statistics service.
+ */
+package org.gnunet.statistics;

Added: gnunet-java/src/main/java/org/gnunet/testbed/Controller.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/Controller.java                
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/testbed/Controller.java        
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,84 @@
+package org.gnunet.testbed;
+
+import org.gnunet.util.Client;
+import org.gnunet.util.Configuration;
+
+/**
+ * Handle to interact with a GNUnet testbed controller.  Each
+ * controller has at least one master handle which is created when the
+ * controller is created; this master handle interacts with the
+ * controller process, destroying it destroys the controller (by
+ * closing stdin of the controller process).  Additionally,
+ * controllers can interact with each other (in a P2P fashion); those
+ * links are established via TCP/IP on the controller's service port.
+ */
+public class Controller {
+
+    /**
+     * Client connecting to the testbed service.
+     */
+    Client client;
+
+    /**
+     * Connect to a controller process.  The configuration to use for the 
connection
+     * is retreived from the given host where a controller is started using
+     * GNUNET_TESTBED_controller_start().
+     *
+     * @param host host to run the controller on; This should be the same host 
if
+     *          the controller was previously started with
+     *          GNUNET_TESTBED_controller_start()
+     * @param event_mask bit mask with set of events to call 'cc' for;
+     *                   or-ed values of "1LL" shifted by the
+     *                   respective 'enum GNUNET_TESTBED_EventType'
+     *                   (i.e.  "(1LL << GNUNET_TESTBED_ET_CONNECT) | ...")
+     * @param cb controller callback to invoke on events
+     */
+    public Controller(Host host, long event_mask, ControllerEventCallback cb) {
+        client = new Client("testbed", host.cfg);
+    }
+
+
+    /**
+     * Create the given peer at the specified host using the given
+     * controller.  If the given controller is not running on the target
+     * host, it should find or create a controller at the target host and
+     * delegate creating the peer.  Explicit delegation paths can be setup
+     * using 'GNUNET_TESTBED_controller_link'.  If no explicit delegation
+     * path exists, a direct link with a subordinate controller is setup
+     * for the first delegated peer to a particular host; the subordinate
+     * controller is then destroyed once the last peer that was delegated
+     * to the remote host is stopped.
+     *
+     * Creating the peer only creates the handle to manipulate and further
+     * configure the peer; use "GNUNET_TESTBED_peer_start" and
+     * "GNUNET_TESTBED_peer_stop" to actually start/stop the peer's
+     * processes.
+     *
+     * Note that the given configuration will be adjusted by the
+     * controller to avoid port/path conflicts with other peers.
+     * The "final" configuration can be obtained using
+     * 'GNUNET_TESTBED_peer_get_information'.
+     *
+     * @param host host to run the peer on; cannot be NULL
+     * @param cfg Template configuration to use for the peer. Should exist 
until
+     *          operation is cancelled or GNUNET_TESTBED_operation_done() is 
called
+     * @param cb the callback to call when the peer has been created
+     * @return the operation handle
+     */
+
+    public Operation createPeer(Host host, Configuration cfg, 
PeerCreateCallback cb) {
+        return null;
+    }
+
+
+    /**
+     * Stop the given controller (also will terminate all peers and
+     * controllers dependent on this controller).  This function
+     * blocks until the testbed has been fully terminated (!).
+     */
+    public void disconnect () {
+
+    }
+
+
+}

Added: gnunet-java/src/main/java/org/gnunet/testbed/ControllerEventCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/ControllerEventCallback.java   
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/testbed/ControllerEventCallback.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,21 @@
+package org.gnunet.testbed;
+
+
+
+public abstract class ControllerEventCallback {
+    void onPeerStart() {
+        throw new AssertionError("event not handled");
+    }
+    void onPeerStop() {
+        throw new AssertionError("event not handled");
+    }
+    void onPeerConnect() {
+        throw new AssertionError("event not handled");
+    }
+    void onPeerDisconnect() {
+        throw new AssertionError("event not handled");
+    }
+    void onOperationFinished() {
+        throw new AssertionError("event not handled");
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/testbed/ControllerInitMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/ControllerInitMessage.java     
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/testbed/ControllerInitMessage.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,30 @@
+package org.gnunet.testbed;
+
+
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UInt64;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.construct.ZeroTerminatedString;
+import org.gnunet.util.GnunetMessage;
+
address@hidden(460)
+public class ControllerInitMessage implements GnunetMessage.Body {
+    /**
+     * Host ID that the controller is either given (if this is the
+     * dominating client) or assumed to have (for peer-connections
+     * between controllers).  A controller must check that all
+     * connections make consistent claims...
+     */
+    @UInt32
+    public int host_id;
+
+    /**
+     * Event mask that specifies which events this client is interested in.
+     */
+    @UInt64
+    public long event_mask;
+
+    @ZeroTerminatedString
+    public String controler_hostname;
+
+}

Added: gnunet-java/src/main/java/org/gnunet/testbed/ControllerProc.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/ControllerProc.java            
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/testbed/ControllerProc.java    
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,89 @@
+package org.gnunet.testbed;
+
+import com.google.common.base.Charsets;
+import org.gnunet.util.Helper;
+import org.gnunet.util.RunaboutMessageReceiver;
+
+import java.io.ByteArrayOutputStream;
+import java.util.zip.Deflater;
+
+/**
+ * A controller process.
+ * The controller process is either a local helper process, or an ssh process 
that starts and controls
+ * the testbed helper on a remote machine.
+ */
+public class ControllerProc {
+    final Helper helper;
+
+
+    public class ControllerProcReceiver extends RunaboutMessageReceiver {
+        public void visit(HelperReplyMessage m) {
+
+        }
+        @Override
+        public void handleError() {
+
+        }
+    }
+
+    /**
+     * Starts a controller process at the given host.  The given host's 
configration
+     * is used as a Template configuration to use for the remote controller; 
the
+     * remote controller will be started with a slightly modified configuration
+     * (port numbers, unix domain sockets and service home values are changed 
as per
+     * TESTING library on the remote host).  The modified configuration 
replaces the
+     * host's existing configuration before signalling success through the
+     * GNUNET_TESTBED_ControllerStatusCallback()
+     *
+     * @param trustedIP the ip address of the controller which will be set as 
TRUSTED
+     *          HOST(all connections form this ip are permitted by the 
testbed) when
+     *          starting testbed controller at host. This can either be a 
single ip
+     *          address or a network address in CIDR notation.
+     * @param host the host where the controller has to be started.  CANNOT be 
NULL.
+     * @param cb function called when the controller is successfully started or
+     *          dies unexpectedly; GNUNET_TESTBED_controller_stop shouldn't be
+     *          called if cb is called with GNUNET_SYSERR as status. Will 
never be
+     *          called in the same task as 'GNUNET_TESTBED_controller_start'
+     *          (synchronous errors will be signalled by returning NULL). This
+     *          parameter cannot be NULL.
+     */
+    public ControllerProc(String trustedIP, Host host, 
ControllerStatusCallback cb) {
+        if (host.isLocal()) {
+            helper = new Helper(false, "gnunet-testbed-helper", null, new 
ControllerProcReceiver());
+        } else {
+            throw new AssertionError("not implemented yet");
+        }
+    }
+
+
+    private HelperInitMessage makeHelperInitMessage(String trustedIP, Host 
host) {
+        HelperInitMessage m = new HelperInitMessage();
+        if (host.hostname == null) {
+            m.hostname = null;
+            m.hostname_size = 0;
+        } else {
+            m.hostname_size =  host.hostname.length();
+            m.hostname = host.hostname.getBytes(Charsets.UTF_8);
+        }
+        m.trusted_ip_size = trustedIP.length();
+        m.trusted_ip = trustedIP;
+
+        byte[] serialized_config = host.cfg.serialize().getBytes();
+
+        Deflater compresser = new Deflater();
+        compresser.setInput(serialized_config);
+        compresser.finish();
+
+        ByteArrayOutputStream s = new ByteArrayOutputStream();
+        byte[] buf = new byte[1024];
+        while (!compresser.finished()) {
+            int n = compresser.deflate(buf);
+            s.write(buf, 0, n);
+        }
+
+        m.compressed_config = s.toByteArray();
+        m.config_size = serialized_config.length;
+
+        return m;
+    }
+}

Added: 
gnunet-java/src/main/java/org/gnunet/testbed/ControllerStatusCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/ControllerStatusCallback.java  
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/testbed/ControllerStatusCallback.java  
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,9 @@
+package org.gnunet.testbed;
+
+
+import org.gnunet.util.Configuration;
+
+public interface ControllerStatusCallback {
+    void onStartupSuccess(Configuration cfg);
+    void onStartupFailure();
+}

Added: gnunet-java/src/main/java/org/gnunet/testbed/HelperInitMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/HelperInitMessage.java         
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/testbed/HelperInitMessage.java 
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,37 @@
+package org.gnunet.testbed;
+
+import org.gnunet.construct.*;
+import org.gnunet.util.GnunetMessage;
+
+/**
+ * Initialization message for gnunet-helper-testbed to start testbed service
+ */
address@hidden(495)
+public class HelperInitMessage implements GnunetMessage.Body {
+    /**
+     * The controller hostname size excluding the NULL termination character -
+     * strlen (hostname); cannot be zero
+     */
+    @UInt16
+    int trusted_ip_size;
+    /**
+     * The hostname size excluding the NULL termination character - strlen
+     * (hostname); cannot be zero
+     */
+    @UInt16
+    int hostname_size;
+    /**
+     * The size of the uncompressed configuration
+     */
+    @UInt16
+    public int config_size;
+
+    @ZeroTerminatedString(optional = true)
+    public String trusted_ip;
+
+    @VariableSizeIntegerArray(signed = true, bitSize = 8, lengthField = 
"hostname_size")
+    public byte[] hostname;
+
+    @FillWith @UInt8
+    public byte[] compressed_config;
+}

Added: gnunet-java/src/main/java/org/gnunet/testbed/HelperReplyMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/HelperReplyMessage.java        
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/testbed/HelperReplyMessage.java        
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,17 @@
+package org.gnunet.testbed;
+
+
+import org.gnunet.construct.FillWith;
+import org.gnunet.construct.UInt16;
+import org.gnunet.construct.UInt8;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
address@hidden(496)
+public class HelperReplyMessage implements GnunetMessage.Body {
+    @UInt16
+    int uncompressed_config_size;
+
+    @FillWith @UInt8
+    byte[] compressed_config;
+}

Added: gnunet-java/src/main/java/org/gnunet/testbed/Host.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/Host.java                      
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/testbed/Host.java      2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,51 @@
+package org.gnunet.testbed;
+
+import org.gnunet.util.Configuration;
+
+/**
+ * Opaque handle to a host running experiments managed by the testing 
framework.
+ * The master process must be able to SSH to this host without password (via
+ * ssh-agent).
+ */
+public class Host {
+    static int nextUID = 1;
+    final Configuration cfg;
+    final String hostname;
+    final String username;
+    final int port;
+
+    private boolean controllerStarted;
+
+    /**
+     * Create a host to run peers and controllers on.
+     *
+     * @param hostname name of the host, use "NULL" for localhost
+     * @param username username to use for the login; may be NULL
+     * @param cfg the configuration to use as a template while starting a 
controller
+     *          on this host.  Operation queue sizes specific to a host are 
also
+     *          read from this configuration handle
+     * @param port port number to use for ssh; use 0 to let ssh decide
+     */
+    public Host(String hostname, String username, Configuration cfg, int port) 
{
+        this.port = (port == 0) ? 22 : port;
+        this.hostname = hostname;
+        this.username = username;
+        this.cfg = cfg;
+    }
+
+    /**
+     * Create a host handle for the local machine.
+     *
+     * @param cfg the configuration to use as a template while starting a 
controller
+     *          on this host.  Operation queue sizes specific to a host are 
also
+     *          read from this configuration handle
+     * @param port port number to use for ssh; use 0 to let ssh decide
+     */
+    public Host(Configuration cfg, int port) {
+        this(null, null, cfg, port);
+    }
+
+    public boolean isLocal() {
+        return hostname == null;
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/testbed/Operation.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/Operation.java                 
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/testbed/Operation.java 2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,11 @@
+package org.gnunet.testbed;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: dold
+ * Date: 8/25/13
+ * Time: 1:26 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public interface Operation {
+}

Added: 
gnunet-java/src/main/java/org/gnunet/testbed/OperationCompletionCallback.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/testbed/OperationCompletionCallback.java   
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/testbed/OperationCompletionCallback.java   
    2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,11 @@
+package org.gnunet.testbed;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: dold
+ * Date: 8/25/13
+ * Time: 1:35 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public interface OperationCompletionCallback {
+}

Added: gnunet-java/src/main/java/org/gnunet/testbed/Peer.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/Peer.java                      
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/testbed/Peer.java      2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,57 @@
+package org.gnunet.testbed;
+
+import org.gnunet.util.Configuration;
+
+/**
+ * Opaque handle to a peer controlled by the testbed framework.  A peer runs
+ * at a particular host.
+ */
+public class Peer {
+    public Operation start(PeerChurnCallback peerChurnCallback) {
+        return null;
+    }
+
+    public Operation stop(PeerChurnCallback peerChurnCallback) {
+        return null;
+    }
+
+    public Operation getInformation() {
+        return null;
+    }
+
+    /*
+     * Change peer configuration.  Must only be called while the
+     * peer is stopped.  Ports and paths cannot be changed this
+     * way.
+     */
+    public Operation updateConfiguration(Configuration cfg) {
+        return null;
+    }
+
+    /*
+     * Change peer configuration.  Must only be called while the
+     * peer is stopped.  Ports and paths cannot be changed this
+     * way.
+     */
+    public Operation destroy() {
+        return null;
+    }
+
+    public Operation manageService(String serviceName, boolean start) {
+        return null;
+    }
+
+    /**
+     * Both peers must have been started before calling this function.
+     * This function then obtains a HELLO from this peer, gives it to 
'otherPeer'
+     * and asks 'otherPeer' to connect to this peer..
+     */
+    public Operation connectOverlay(OperationCompletionCallback cb, Peer 
otherPeer) {
+        return null;
+    }
+
+    public Operation getServiceConnection(String serviceName /*,... */) {
+        return null;
+    }
+
+}

Added: gnunet-java/src/main/java/org/gnunet/testbed/PeerChurnCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/PeerChurnCallback.java         
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/testbed/PeerChurnCallback.java 
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,7 @@
+package org.gnunet.testbed;
+
+
+public interface PeerChurnCallback {
+    void onChurnSuccess();
+    void onChurnError(String emsg);
+}

Added: gnunet-java/src/main/java/org/gnunet/testbed/PeerCreateCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testbed/PeerCreateCallback.java        
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/testbed/PeerCreateCallback.java        
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,7 @@
+package org.gnunet.testbed;
+
+
+public interface PeerCreateCallback {
+    void onPeerCreated(Peer peer);
+    void onError(String errorMessage);
+}

Added: gnunet-java/src/main/java/org/gnunet/testing/TestingFixture.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testing/TestingFixture.java            
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/testing/TestingFixture.java    
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,21 @@
+package org.gnunet.testing;
+
+import org.gnunet.util.Scheduler;
+import org.junit.After;
+import org.junit.Before;
+
+/**
+ * Default JUnit4 fixture methods for gnunet-java tests.
+ * Resets the scheduler properly.
+ */
+public class TestingFixture {
+    @Before
+    public void beginGNJTest() {
+        Scheduler.forceReset();
+    }
+
+    @After
+    public void endGNJTest() {
+        Scheduler.forceReset();
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/testing/TestingServer.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testing/TestingServer.java             
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/testing/TestingServer.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,65 @@
+package org.gnunet.testing;
+
+import org.gnunet.util.Client;
+import org.gnunet.util.RelativeTime;
+import org.gnunet.util.Server;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.nio.channels.ServerSocketChannel;
+
+/**
+ * Server with an ephemeral port.
+ * Can spawn clients connected to the server for testing.
+ *
+ * @author Florian Dold
+ */
+public class TestingServer {
+    public final Server server;
+    private final ServerSocketChannel srvChan;
+
+    public TestingServer() {
+        this(RelativeTime.FOREVER, true);
+    }
+
+    public TestingServer(RelativeTime idleTimeout, boolean requireFound) {
+        try {
+            srvChan = ServerSocketChannel.open();
+            srvChan.configureBlocking(false);
+
+            // bind to ephemeral port
+            srvChan.socket().bind(null);
+        } catch (IOException e) {
+            throw new RuntimeException("TestingServer creation failed");
+        }
+
+        server = new Server(idleTimeout, requireFound);
+        server.addAcceptSocket(srvChan);
+
+    }
+
+    /**
+     * Create a client connected to this server.
+     *
+     * @return a client connected to this server
+     */
+    public Client createClient() {
+        SocketAddress socketAddress = srvChan.socket().getLocalSocketAddress();
+
+        if (!(socketAddress instanceof InetSocketAddress)) {
+            throw new RuntimeException("unknown type of socket address");
+        }
+        InetSocketAddress saddr = (InetSocketAddress) socketAddress;
+
+        String hostname = saddr.getHostName();
+
+        if (hostname == null) {
+            throw new RuntimeException("localhost SocketAddress has no 
hostname");
+        }
+
+        return new Client(hostname, srvChan.socket().getLocalPort());
+    }
+
+}

Added: gnunet-java/src/main/java/org/gnunet/testing/TestingSetup.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testing/TestingSetup.java              
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/testing/TestingSetup.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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.testing;
+
+import org.gnunet.util.Program;
+import org.gnunet.util.RelativeTime;
+
+/**
+ * A testing setup is responsible for configuring the loggers during testing, 
and can
+ * start gnunet subsystems (like statistics, core, etc.).
+ *
+ * @author Florian Dold
+ */
+public final class TestingSetup {
+
+    private TestingSetup() {
+
+    }
+
+    public static class SetupException extends RuntimeException {
+        public SetupException(Exception e) {
+            super(e);
+        }
+        public SetupException(String msg) {
+            super(msg);
+        }
+
+    }
+
+    public static void setup() {
+        String log = System.getenv("GNJ_LOGLEVEL");
+        if (log != null) {
+            Program.configureLogging(log, null);
+        } else {
+            Program.configureLogging("WARN", null);
+        }
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/testing/TestingSubsystem.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testing/TestingSubsystem.java          
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/testing/TestingSubsystem.java  
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,133 @@
+/*
+ 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.testing;
+
+import com.google.common.base.Charsets;
+import org.gnunet.util.Configuration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+
+/**
+ * Handle to a GNUnet subsystem that has been started for testing purposes.
+ *
+ * @author Florian Dold
+*/
+public class TestingSubsystem {
+    private static final Logger logger = LoggerFactory
+            .getLogger(TestingSubsystem.class);
+
+
+    private Process p;
+    private BufferedReader reader;
+    private OutputStreamWriter writer;
+    private Configuration cfg;
+
+    public Configuration getConfiguration() {
+        return cfg;
+    }
+
+    public TestingSubsystem(String service) {
+        try {
+            p = Runtime.getRuntime().exec(new String[]{"gnunet-testing", "-r", 
service});
+        } catch (IOException e) {
+            throw new TestingSetup.SetupException(e);
+        }
+
+        reader = new BufferedReader(new InputStreamReader(p.getInputStream(), 
Charsets.UTF_8));
+
+        writer = new OutputStreamWriter(p.getOutputStream(), Charsets.UTF_8);
+
+        String started;
+        try {
+            started = reader.readLine();
+        } catch (IOException e) {
+            throw new TestingSetup.SetupException(e);
+        }
+
+        if (started == null || !started.equals("ok")) {
+            throw new TestingSetup.SetupException("could not start service ('" 
+ started + "')");
+        }
+
+
+        String cfgFileName;
+        try {
+            cfgFileName = reader.readLine();
+        } catch (IOException e) {
+            throw new TestingSetup.SetupException(e);
+        }
+
+        if (cfgFileName == null) {
+            throw new TestingSetup.SetupException("could not start subsystem 
for testing: no config file received from helper");
+        }
+
+        cfg = new Configuration();
+        cfg.parse(cfgFileName);
+
+        try {
+            if (p.getErrorStream().available() != 0) {
+                throw new TestingSetup.SetupException("error starting 
service");
+            }
+        } catch (IOException e) {
+            throw new TestingSetup.SetupException(e);
+        }
+    }
+    public void destroy() {
+        try {
+            writer.write("q\n");
+            writer.flush();
+        } catch (IOException e) {
+            throw new TestingSetup.SetupException(e);
+        }
+        try {
+            p.waitFor();
+        } catch (InterruptedException e) {
+            throw new TestingSetup.SetupException(e);
+        }
+        if (p.exitValue() != 0) {
+            throw new TestingSetup.SetupException("gnunet-testing exit value 
unsuccessful");
+        }
+    }
+
+    public void restart() {
+        try {
+            writer.write("r\n");
+            writer.flush();
+        } catch (IOException e) {
+            throw new TestingSetup.SetupException(e);
+        }
+        String response;
+        logger.debug("waiting for gnunet-testing to respond to restart");
+        try {
+            response = reader.readLine();
+        } catch (IOException e) {
+            throw new TestingSetup.SetupException(e);
+        }
+        if (response == null || !response.equals("restarted")) {
+            throw new TestingSetup.SetupException("wrapper did not cooperate");
+        }
+        logger.debug("restart successful");
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/testing/package-info.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/testing/package-info.java              
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/testing/package-info.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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;

Added: gnunet-java/src/main/java/org/gnunet/transport/AddressIterateMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/AddressIterateMessage.java   
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/transport/AddressIterateMessage.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,34 @@
+package org.gnunet.transport;
+
+
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.AbsoluteTime;
+import org.gnunet.util.PeerIdentity;
+
+
+/**
+ * Message from the client to the transport service
+ * asking for binary addresses known for a peer.
+ */
address@hidden(380)
+public class AddressIterateMessage {
+    /**
+     * One shot call or continous replies?
+     */
+    @UInt32
+    public int one_shot;
+
+    /**
+     * FIXME: This field seems to be deprecated in the C API?
+     */
+    @NestedMessage
+    public AbsoluteTime timeout;
+
+    /**
+     * The identity of the peer to look up.
+     */
+    @NestedMessage
+    public PeerIdentity peer;
+}

Added: gnunet-java/src/main/java/org/gnunet/transport/BlacklistCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/BlacklistCallback.java       
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/transport/BlacklistCallback.java       
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,12 @@
+package org.gnunet.transport;
+
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public interface BlacklistCallback {
+    boolean isAllowed(PeerIdentity peerIdentity);
+}

Added: gnunet-java/src/main/java/org/gnunet/transport/BlacklistInitMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/BlacklistInitMessage.java    
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/transport/BlacklistInitMessage.java    
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,9 @@
+package org.gnunet.transport;
+
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
address@hidden(369)
+public class BlacklistInitMessage implements GnunetMessage.Body {
+    // message body is empty
+}

Added: gnunet-java/src/main/java/org/gnunet/transport/HelloUpdateCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/HelloUpdateCallback.java     
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/transport/HelloUpdateCallback.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,9 @@
+package org.gnunet.transport;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public class HelloUpdateCallback {
+}

Added: gnunet-java/src/main/java/org/gnunet/transport/PeerIterateCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/PeerIterateCallback.java     
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/transport/PeerIterateCallback.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,12 @@
+package org.gnunet.transport;
+
+import org.gnunet.util.PeerIdentity;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public interface PeerIterateCallback {
+    void processPeerAddress(PeerIdentity peer, Object hello);
+}

Added: gnunet-java/src/main/java/org/gnunet/transport/RequestConnectMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/RequestConnectMessage.java   
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/transport/RequestConnectMessage.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,24 @@
+package org.gnunet.transport;
+
+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;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
address@hidden(374)
+public class RequestConnectMessage implements GnunetMessage.Body {
+    @UInt32
+    public int reserved;
+
+    /**
+     * Identity of the peer we would like to connect to.
+     */
+    @NestedMessage
+    public PeerIdentity peer;
+}

Added: gnunet-java/src/main/java/org/gnunet/transport/StartMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/StartMessage.java            
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/transport/StartMessage.java    
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,32 @@
+package org.gnunet.transport;
+
+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;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
address@hidden(360)
+public class StartMessage implements GnunetMessage.Body {
+    /**
+     * 0: no options
+     * 1: The 'self' field should be checked
+     * 2: this client is interested in payload traffic
+     */
+    @UInt32
+    public int options;
+
+    /**
+     * Identity we think we have.  If it does not match, the
+     * receiver should print out an error message and disconnect.
+     */
+    @NestedMessage
+    public PeerIdentity self;
+
+
+}

Added: gnunet-java/src/main/java/org/gnunet/transport/Transport.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/Transport.java               
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/transport/Transport.java       
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,156 @@
+package org.gnunet.transport;
+
+import org.gnunet.hello.HelloMessage;
+import org.gnunet.mq.Envelope;
+import org.gnunet.mq.NotifySentHandler;
+import org.gnunet.util.*;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public class Transport {
+
+    /**
+     * Client that connects to the transport service,
+     */
+    private final Client client;
+
+    boolean init_requested;
+
+    /**
+     * Blacklist callback, null if there is no active blacklist
+     * for this handle.
+     */
+    BlacklistCallback blacklistCallback;
+
+    private final class TransportReceiver extends RunaboutMessageReceiver {
+        @Override
+        public void handleError() {
+            client.reconnect();
+            // FIXME: complete
+        }
+    }
+
+    /**
+     * Create a handle to the transport service.
+     *
+     * @param cfg configuration to use for connecting
+     */
+    public Transport(Configuration cfg) {
+        client = new Client("transport", cfg);
+        client.installReceiver(new TransportReceiver());
+    }
+
+    /**
+     * Ask the transport service to establish a connection to
+     * the given peer.
+     *
+     * @param target who we should try to connect to
+     * @param cb     callback to be called when request was transmitted to 
transport
+     *               service
+     * @return a handle to cancel the operation
+     */
+    Cancelable tryConnect(PeerIdentity target, final TryConnectCallback cb) {
+        RequestConnectMessage m = new RequestConnectMessage();
+        m.peer = target;
+        m.reserved = 0;
+        final Envelope ev = new Envelope(m);
+        ev.notifySent(new NotifySentHandler() {
+            @Override
+            public void onSent() {
+                cb.onDone();
+            }
+        });
+        client.send(ev);
+
+        return new Cancelable() {
+            @Override
+            public void cancel() {
+                ev.cancel();
+            }
+        };
+    }
+
+    /**
+     * ... (discuss first)
+     */
+    public void init(Object receiveCallback, Object notifyConnect, Object 
notifyDisconnect) {
+
+    }
+
+
+    /**
+     * Obtain the HELLO message for this peer.
+     *
+     * @param rec function to call with the HELLO, sender will be our peer
+     *            identity; message and sender will be NULL on timeout
+     *            (handshake with transport service pending/failed).
+     *            cost estimate will be 0.
+     * @return handle to cancel the operation
+     */
+    Cancelable getHello(HelloUpdateCallback rec) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Offer the transport service the HELLO of another peer.  Note that
+     * the transport service may just ignore this message if the HELLO is
+     * malformed or useless due to our local configuration.
+     *
+     * @param hello the hello message
+     * @param cont  continuation to call when HELLO has been sent,
+     *              tc reason GNUNET_SCHEDULER_REASON_TIMEOUT for fail
+     *              tc reasong GNUNET_SCHEDULER_REASON_READ_READY for success
+     * @return a GNUNET_TRANSPORT_OfferHelloHandle handle or NULL on failure,
+     *         in case of failure cont will not be called
+     */
+
+    Cancelable offerHello(HelloMessage hello,
+                          Scheduler.Task cont) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Install a blacklist callback.  The service will be queried for all
+     * existing connections as well as any fresh connections to check if
+     * they are permitted.
+     * The blacklist is active until the Transport handle is destroyed.
+     * When the transport handle that installed the blacklist is destroyed,
+     * all hosts that were denied in the past will automatically be
+     * whitelisted again.  This is the only way to re-enable
+     * connections from peers that were previously blacklisted.
+     *
+     * @param blacklistCallback  callback to invoke to check if connections 
are allowed
+     */
+    public void blacklist(BlacklistCallback blacklistCallback) {
+        if (this.blacklistCallback != null)
+            throw new AssertionError("there is already a blacklist");
+        if (blacklistCallback == null)
+            throw new AssertionError("blacklist callback may not be null");
+        this.blacklistCallback = blacklistCallback;
+        client.send(new BlacklistInitMessage());
+    }
+
+    /**
+     * Return all the known addresses for a specific peer or all peers.
+     * Returns continuously all address if one_shot is set to false
+     * <p/>
+     * Returns the address(es) that we are currently using for this
+     * peer.  Upon completion, the 'AddressLookUpCallback' is called one more
+     * time with 'NULL' for the address and the peer.  After this, the 
operation must no
+     * longer be explicitly canceled.
+     *
+     * @param peer                      peer identity to look up the addresses 
of, CHANGE: allow NULL for all (connected) peers
+     * @param one_shot                  GNUNET_YES to return the current state 
and then end (with NULL+NULL),
+     *                                  GNUNET_NO to monitor the set of 
addresses used (continuously, must be explicitly canceled)
+     * @param timeout                   how long is the lookup allowed to take 
at most (irrelevant if one_shot is set to GNUNET_NO)
+     * @param peer_address_callback     function to call with the results
+     */
+    Cancelable getActiveAddresses(PeerIdentity peer, int one_shot,
+                              RelativeTime timeout, PeerIterateCallback 
peer_address_callback) {
+        throw new UnsupportedOperationException();
+    }
+}
+

Added: gnunet-java/src/main/java/org/gnunet/transport/TryConnectCallback.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/transport/TryConnectCallback.java      
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/transport/TryConnectCallback.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,10 @@
+package org.gnunet.transport;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public interface TryConnectCallback {
+    void onDone();
+}

Added: gnunet-java/src/main/java/org/gnunet/util/ATSInformation.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/ATSInformation.java               
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/ATSInformation.java       
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,37 @@
+/*
+ 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.Message;
+import org.gnunet.construct.UInt32;
+
+
+/**
+ * Information related to Automatic Transport Selection.
+ */
+public class ATSInformation implements Message {
+    @UInt32
+    public long type;
+
+    @UInt32
+    public long value;
+}

Added: gnunet-java/src/main/java/org/gnunet/util/AbsoluteTime.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/AbsoluteTime.java                 
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/AbsoluteTime.java 2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,269 @@
+/*
+     This file is part of GNUnet.
+     (C) 2009 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 2, 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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Date;
+
+/**
+ * A specific point in time.
+ * 
+ * @author Florian Dold
+ */
+public class AbsoluteTime implements Comparable<AbsoluteTime> {
+    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);
+
+    /**
+     * Absolute time value in microseconds.
+     */
+    private final long abs_value_us;
+
+    /**
+     * Gets the current time.
+     * 
+     * @return the current time
+     */
+    public static AbsoluteTime now() {
+        return new AbsoluteTime(System.currentTimeMillis() * 1000);
+    }
+
+    public AbsoluteTime(final long abs_value_us) {
+        this.abs_value_us = abs_value_us;
+    }
+
+    /**
+     * Adds a relative time value to an absolute time.
+     * 
+     * @param duration duration to add to address@hidden this}
+     * @return address@hidden this + duration}
+     */
+    public AbsoluteTime add(RelativeTime duration) {
+        if (abs_value_us == Long.MAX_VALUE
+                || duration.isForever()) {
+            return AbsoluteTime.FOREVER;
+        }
+        if (abs_value_us + duration.getMicroseconds() < abs_value_us) {
+            return AbsoluteTime.FOREVER;
+        }
+        return new AbsoluteTime(abs_value_us + duration.getMicroseconds());
+    }
+
+    /**
+     * Calculates the estimate time of arrival/completion for an operation.
+     * 
+     * @param start
+     *            when did the operation start?
+     * @param finished
+     *            how much has been done?
+     * @param total
+     *            how much must be done overall (same unit as for "finished")
+     * @return remaining duration for the operation, assuming it continues at
+     *         the same speed
+     */
+    public static RelativeTime calculateETA(final AbsoluteTime start,
+            final long finished, final long total) {
+        if (finished >= total) {
+            return RelativeTime.ZERO;
+        }
+        if (finished == 0) {
+            return RelativeTime.FOREVER;
+        }
+        final RelativeTime dur = start.getDuration();
+        final double exp = dur.getMicroseconds() * total
+                / (double) finished;
+        return new RelativeTime((long) exp);
+    }
+
+
+    /**
+     * address@hidden
+     */
+    @Override
+    public boolean equals(Object other) {
+        return other instanceof AbsoluteTime && compareTo((AbsoluteTime) 
other) == 0;
+    }
+
+    /**
+     * address@hidden
+     */
+    @Override
+    public int hashCode() {
+        return (int) this.abs_value_us;
+    }
+
+    /**
+     * address@hidden
+     */
+    @Override
+    public int compareTo(AbsoluteTime other) {
+        if (this.abs_value_us < other.abs_value_us) {
+            return -1;
+        }
+        if (this.abs_value_us > other.abs_value_us) {
+            return 1;
+        }
+        return 0;
+    }
+
+    /**
+     * address@hidden
+     */
+    @Override
+    public String toString() {
+        if (this.isForever()) {
+            return "AbsoluteTime(FOREVER)";
+        }
+        return "AbsoluteTime("+this.abs_value_us +")";
+    }
+
+
+    /**
+     * Check if a deadline is due.
+     * @return true if NOW is greater than the given time, false otherwise
+     */
+    public boolean isDue() {
+        return this.abs_value_us < now().abs_value_us;
+    }
+
+    /**
+     * Does this AbsoluteTime value represent forever?
+     *
+     * @return this==FOREVER
+     */
+    public boolean isForever() {
+        return this.abs_value_us == Long.MAX_VALUE;
+    }
+
+    /**
+     * Calculates the difference between two absolute times.
+     * 
+     * @param other ...
+     * @return this - other
+     */
+    public RelativeTime getDifference(final AbsoluteTime other) {
+        if (other.abs_value_us == Long.MAX_VALUE) {
+            return RelativeTime.FOREVER;
+        }
+        return new RelativeTime(abs_value_us - other.abs_value_us);
+    }
+
+    /**
+     * Gets the duration of an operation as the difference of the current time
+     * and address@hidden this}.
+     *
+     * @return this - now
+     */
+    public RelativeTime getDuration() {
+        assert abs_value_us != Long.MAX_VALUE;
+        return getDifference(AbsoluteTime.now());
+    }
+
+    /**
+     * Returns the milliseconds since some fixed point of reference.
+     * 
+     * @return the absolute time in milliseconds
+     */
+    public long getMicroseconds() {
+        return abs_value_us;
+    }
+
+    /**
+     * Calculates the remaining time relative to now.
+     * 
+     * @return this - now
+     */
+    public RelativeTime getRemaining() {
+        if (abs_value_us == Long.MAX_VALUE) {
+            return RelativeTime.FOREVER;
+        }
+        return getDifference(AbsoluteTime.now());
+    }
+
+    /**
+     * Returns the maximum of two time values.
+     * 
+     * @param other ...
+     * @return max(this,other)
+     */
+    public AbsoluteTime max(final AbsoluteTime other) {
+        return abs_value_us >= other.abs_value_us ? this : other;
+
+    }
+
+    /**
+     * Returns the minimum of two time values.
+     * 
+     * @param other ...
+     * @return min(this,other)
+     */
+    public AbsoluteTime min(final AbsoluteTime other) {
+        return abs_value_us <= other.abs_value_us ? this : other;
+    }
+
+    /**
+     * Subtracts a relative time value to an absolute time
+     * 
+     * @param duration ...
+     * @return this - duration
+     */
+    public AbsoluteTime subtract(final RelativeTime duration) {
+        if (abs_value_us <= duration.getMicroseconds()) {
+            return AbsoluteTime.ZERO;
+        }
+        if (abs_value_us == Long.MAX_VALUE) {
+            return this;
+        }
+        return new AbsoluteTime(abs_value_us - duration.getMicroseconds());
+    }
+
+    /**
+     * 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__);
+    }
+
+    public Date toDate() {
+        return new Date(abs_value_us / 1000);
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/util/AbsoluteTimeMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/AbsoluteTimeMessage.java          
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/AbsoluteTimeMessage.java  
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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;
+
+import org.gnunet.construct.Message;
+import org.gnunet.construct.UInt64;
+
+
+
+/**
+ * Representation of an AbsoluteTime object, to be sent over the network.
+ */
+public class AbsoluteTimeMessage implements Message {
+    @UInt64
+    public long value__;
+
+    public AbsoluteTimeMessage() {
+
+    }
+
+
+    public AbsoluteTimeMessage(final AbsoluteTime t) {
+        if (t.equals(AbsoluteTime.FOREVER)) {
+            this.value__ = -1;
+        } else {
+            this.value__ = t.getMicroseconds();
+        }
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/util/Cancelable.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/Cancelable.java                   
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/Cancelable.java   2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,28 @@
+/*
+ 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;
+
+/**
+ * Any asynchronous operation that can be canceled should implement this 
interface.
+ */
+public interface Cancelable {
+    public void cancel();
+}

Added: gnunet-java/src/main/java/org/gnunet/util/Client.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/Client.java                       
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/Client.java       2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,329 @@
+/*
+     This file is part of GNUnet.
+     (C) 2009 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 2, 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 com.google.common.base.Optional;
+import org.gnunet.mq.Envelope;
+import org.gnunet.mq.MessageQueue;
+import org.gnunet.statistics.Statistics;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A connection to a GNUnet service.
+ *
+ * Wraps a Connection, and is responsible for waiting until the underlying 
connection has been made
+ * and allows reconnects.
+ */
+public class Client extends MessageQueue {
+    private static final Logger logger = LoggerFactory
+            .getLogger(Client.class);
+
+    /**
+     * Underlying connection to the service.
+     * May be NULL if the client is currently not connected.
+     */
+    private Connection connection;
+
+    /**
+     * Host this client should be connected to.
+     */
+    private final String hostname;
+
+    /**
+     * Port of the host the client should connect to.
+     */
+    private final int port;
+
+    /**
+     * Initial value for connectBackoff.
+     */
+    private static final RelativeTime INITAL_BACKOFF = 
RelativeTime.MILLISECOND.multiply(5);
+
+    /**
+     * Maximum value for connectBackoff.
+     */
+    private static final RelativeTime MAX_BACKOFF = 
RelativeTime.SECOND.multiply(5);
+
+    /**
+     * The time to wait after an error occured while connecting.
+     * Every time an error occurs while connecting, this value is doubled 
until its maximum
+     * value (MAX_BACKOFF) has been reached. This strategy is called 
exponential backoff.
+     */
+    private RelativeTime connectBackoff = INITAL_BACKOFF;
+
+    /**
+     * True if we are waiting for the client to connect before we can ask it 
to do
+     * notifyTransmitReady.
+     */
+    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;
+
+    /**
+     * Currently installed persistent receiver.
+     * Will receive all messages sent to the client.
+     */
+    private RunaboutMessageReceiver receiver;
+
+    private boolean receiver_active;
+
+    /**
+     * Handle to cancel the message currently submitted in the queue,
+     */
+    private Cancelable currentSubmit;
+
+    /**
+     * Create a connection to a service.
+     *
+     * @param serviceName name of the service
+     * @param cfg         configuration to use
+     */
+    public Client(String serviceName, Configuration cfg) {
+        if (cfg == null) {
+            throw new AssertionError("Configuration may not be null");
+        }
+        if (!cfg.haveValue(serviceName, "PORT")) {
+            throw new Configuration.ConfigurationException(String.format("PORT 
of service '%s' not specified", serviceName));
+        }
+        if (!cfg.haveValue(serviceName, "HOSTNAME")) {
+            throw new 
Configuration.ConfigurationException(String.format("HOSTNAME of service '%s' 
not specified", serviceName));
+        }
+
+        // get port of this service from the configuration
+        Optional<Long> portOption = cfg.getValueNumber(serviceName, "PORT");
+        port = portOption.get().intValue();
+        // get the hostname from the configuration
+        hostname = cfg.getValueString(serviceName, "HOSTNAME").get();
+        if (hostname == null || hostname.isEmpty()) {
+            throw new 
Configuration.ConfigurationException(String.format("hostname of service '%s' 
empty", serviceName));
+        }
+        reconnect();
+    }
+
+    /**
+     * Create a connection to a service with the specified hostname and port.
+     *
+     * @param hostname hostname of the service
+     * @param port port of the service
+     */
+    public Client(String hostname, int port) {
+        this.hostname = hostname;
+        this.port = port;
+        reconnect();
+    }
+
+
+    /**
+     * Receive one message from the service. Can only be called after sending 
a message to the server.
+     *
+     * @param timeout  deadline after which MessageReceiver.deadline will be 
called
+     * @param receiver MessageReceiver that is responsible for the received 
message
+     */
+    public Cancelable receiveOne(RelativeTime timeout, MessageReceiver 
receiver) {
+        return connection.receive(timeout, receiver);
+    }
+
+    /**
+     * Ask the client to call us once it is able to send a message.
+     *
+     *
+     * @param timeout     after how long should we give up (and call 
transmitter.transmit(null))
+     * @param autoRetry   if the connection to the service dies, should we
+     *                    automatically reconnect and retry (within the 
deadline period)
+     *                    or should we immediately fail in this case?  Pass 
true
+     *                    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
+     *                    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 == null) {
+            throw new AssertionError("notifyTransmitReady called on 
disconnected client");
+        }
+        if (connection.isConnected()) {
+            return connection.notifyTransmitReady(0, timeout, transmitter);
+        } else {
+            notifyTransmitReadyDelayed = true;
+            final AbsoluteTime deadline = timeout.toAbsolute();
+            delayedNotifyTransmitHandle = 
connection.notifyConnected(connectBackoff, new Continuation() {
+                @Override
+                public void cont(boolean success) {
+                    delayedNotifyTransmitHandle = null;
+                    if (success) {
+                        activateReceiver();
+                        notifyTransmitReadyDelayed = false;
+                        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("connect timed out, trying again");
+                        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 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 final void reconnect() {
+        if (connection != null) {
+            connection.disconnect();
+        }
+        connection = new Connection(hostname, port);
+    }
+
+    /**
+     * Disconnect from the service. Cancel all pending receive/transmit 
requests.
+     */
+    public void disconnect() {
+        if (notifyTransmitReadyDelayed) {
+            logger.error("disconnecting while notifyTransmitReady is pending");
+        }
+        connection.disconnect();
+        connection = null;
+    }
+
+    public boolean isConnected() {
+        return (connection != null) && connection.isConnected();
+    }
+
+    @Override
+    protected void submit(Envelope ev) {
+        currentSubmit = transmitWhenReady(RelativeTime.FOREVER, ev.message, 
new Continuation() {
+            @Override
+            public void cont(boolean success) {
+                currentSubmit = null;
+                reportMessageSent();
+            }
+        });
+    }
+
+    @Override
+    protected void retract() {
+        if (currentSubmit == null)
+            throw new AssertionError();
+        currentSubmit.cancel();
+        currentSubmit = null;
+    }
+
+    private void activateReceiver() {
+        if (receiver_active || receiver == null)
+            return;
+        final MessageReceiver proxyReceiver = new MessageReceiver() {
+            @Override
+            public void process(GnunetMessage.Body msg) {
+                Client.this.receiver.process(msg);
+                if (connection != null && connection.isConnected())
+                    connection.receive(RelativeTime.FOREVER, this);
+                else
+                    receiver_active = false;
+            }
+
+            @Override
+            public void handleError() {
+                Client.this.receiver.handleError();
+                receiver_active = false;
+            }
+        };
+        connection.receive(RelativeTime.FOREVER, proxyReceiver);
+        receiver_active = true;
+    }
+
+    public void installReceiver(RunaboutMessageReceiver receiver) {
+        this.receiver = receiver;
+        if (connection != null && connection.isConnected())
+            activateReceiver();
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/util/Configuration.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/Configuration.java                
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/Configuration.java        
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,389 @@
+/*
+     This file is part of GNUnet.
+     (C) 2009 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 2, 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 com.google.common.base.Charsets;
+import com.google.common.base.Optional;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Table;
+import com.google.common.io.Files;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Configuration management.
+ *
+ * @author Florian Dold
+ */
+public class Configuration {
+    public static class ParsingError extends RuntimeException {
+        ParsingError(String msg) {
+            super(msg);
+        }
+
+        ParsingError(String msg, final Throwable t) {
+            super(msg, t);
+        }
+    }
+
+    private static final Logger logger = LoggerFactory
+            .getLogger(Configuration.class);
+
+    private static Pattern section = Pattern.compile("\\[(.*?)\\]");
+    private static Pattern tag = Pattern.compile("\\s*(\\S+?)\\s*=(.*?)");
+    private static Pattern whitspace = Pattern.compile("\\s*");
+
+    // rows are sections, colums are options
+    private final Table<String, String, String> sections = 
HashBasedTable.create();
+
+    private final Map<String, Set<String>> sectionSources = new 
HashMap<String, Set<String>>(20);
+
+    /**
+     * Start with an empty configuration.
+     */
+    public Configuration() {
+    }
+
+
+    /**
+     * Expand an expression of the form "$FOO/BAR" to "DIRECTORY/BAR" where
+     * either in the "PATHS" section or the environment "FOO" is set to
+     * "DIRECTORY".
+     *
+     * @param orig string to $-expand
+     * @return $-expanded string
+     */
+    public String expandDollar(String orig) {
+        Map<String, String> env = System.getenv();
+        for (final Map.Entry<String, String> e : env.entrySet()) {
+            orig = orig.replace("$" + e.getKey(), e.getValue());
+        }
+
+        for (final Map.Entry<String, String> e : 
sections.row("PATHS").entrySet()) {
+            orig = orig.replace("$" + e.getKey(), e.getValue());
+        }
+        return orig;
+    }
+
+    /**
+     * Returns all configuration options in a section.
+     *
+     * @param s the section of interest
+     * @return an unmodifiable view of the section.
+     */
+    public Map<String, String> getSection(String s) {
+        Map<String, String> m = sections.row(s);
+        return Collections.unmodifiableMap(m);
+    }
+
+    /**
+     * Returns the names of all non-empty sections
+     *
+     * @return set of non-empty section names
+     */
+    public Set<String> getSections() {
+        return sections.rowKeySet();
+    }
+
+    /**
+     * Get a configuration value that should be in a set of predefined strings
+     *
+     * @param section section of interest
+     * @param option  option of interest
+     * @param choices list of legal values
+     * @return matching value from choices
+     */
+    public Optional<String> getValueChoice(String section, String option,
+                                 Iterable<String> choices) {
+        Optional<String> value = getValueString(section, option);
+        if (!value.isPresent()) {
+            return value;
+        }
+        for (String c : choices) {
+            if (c.equals(value.get())) {
+                return value;
+            }
+        }
+        logger.error("Failure in configuration section {}: invalid value", 
section);
+        return Optional.absent();
+    }
+
+
+    /**
+     * Get a configuration value that should be a number
+     *
+     * @param section section of interest
+     * @param option  option of interest
+     * @return null if value not in configuration, the option's value otherwise
+     */
+    public Optional<Long> getValueNumber(String section, String option) {
+        Optional<String> num_str = getValueString(section, option);
+        if (!num_str.isPresent()) {
+            logSectionSources(section);
+            return Optional.absent();
+        }
+        try {
+            return Optional.of(Long.parseLong(num_str.get()));
+        } catch (NumberFormatException e) {
+            logger.error("Failure in configuration section "
+                    + section + " option " + option + ": " + e.getMessage(), 
e);
+            return Optional.absent();
+        }
+    }
+
+    private void logSectionSources(String section) {
+        Set<String> sources = sectionSources.get(section);
+        if (sources == null) {
+            logger.info("No sources for section '{}'", section);
+        } else {
+            logger.info("Sources for section '{}': {}", section, sources);
+        }
+    }
+
+    /**
+     * Set an option to a string value in a section.
+     *
+     * @param section section of interest
+     * @param option  option of interest
+     * @return value
+     */
+    public Optional<String> getValueString(String section, String option) {
+        if (haveValue(section, option)) {
+            return Optional.of(sections.get(section, option));
+        }
+        return Optional.absent();
+    }
+
+    /**
+     * Gets a configuration value that should be in a set of {"YES","NO"}.
+     *
+     * @param section section of interest
+     * @param option  option of interest
+     * @return true, false, null
+     */
+    public Optional<Boolean> getValueYesNo(String section, String option) {
+        final Optional<String> v = getValueChoice(section, option,
+                Arrays.asList("YES", "NO"));
+        if (!v.isPresent()) {
+            Set<String> sources = sectionSources.get(section);
+            if (sources == null) {
+                logger.info("No sources for section '{}'", section);
+            } else {
+                logger.info("Sources for section '{}': {}", section, sources);
+            }
+            logger.error(String.format(
+                    "Failure in configuration section '%s': option '%s' not 
found",
+                    section, option));
+            return Optional.absent();
+        }
+        if (v.get().equalsIgnoreCase("YES")) {
+            return Optional.of(true);
+        }
+        if (v.get().equalsIgnoreCase("NO")) {
+            return Optional.of(false);
+        }
+
+        logger.error(String.format("Configuration error: section '%s', option 
'%s' not recognized as YES or NO", section, option));
+
+        return Optional.absent();
+    }
+
+    /**
+     * Tests if we have a value for a particular option.
+     *
+     * @param section section of interest
+     * @param option  option of interest
+     * @return true if so, false of not
+     */
+    public boolean haveValue(String section, String option) {
+        return sections.contains(section, option);
+    }
+
+    /**
+     * Parse a configuration file, add all of the options in the file to the
+     * configuration environment.
+     *
+     * @param filename name of the configuration file
+     */
+    public void parse(String filename) {
+        filename = replaceHome(filename);
+
+        String current_section = "";
+
+        Iterator<String> it;
+
+        try {
+            List<String> lines = Files.readLines(new File(filename), 
Charset.defaultCharset());
+            it = lines.iterator();
+        } catch (IOException e) {
+            throw new ParsingError("Cannot read configuration file '" + 
filename + "'");
+        }
+
+        int lineNumer = 1;
+
+        while (it.hasNext()) {
+            String line = it.next();
+            String[] split_line = line.split("#");
+            if (split_line.length == 0)
+                continue;
+
+            // strip comment
+            line = split_line[0];
+            Matcher m;
+
+            if ((m = tag.matcher(line)).matches()) {
+                String option = m.group(1).trim();
+                String value = m.group(2).trim();
+
+                if (value.length() != 0 && value.charAt(0) == '"') {
+                    int pos = value.indexOf('"', 1);
+                    if (pos == -1) {
+                        logger.warn("incorrecly quoted config value");
+                        continue;
+                    }
+                    value = value.substring(1, pos);
+                }
+                setValueString(current_section, option, value);
+            } else if ((m = section.matcher(line)).matches()) {
+                current_section = m.group(1).trim();
+                if (sectionSources.containsKey(current_section)) {
+                    sectionSources.get(current_section).add(filename);
+                } else {
+                    sectionSources.put(current_section, new 
HashSet<String>(Collections.singleton(filename)));
+                }
+            } else if (whitspace.matcher(line).matches()) {
+                // whitespace is ok
+            } else {
+                logger.warn(String.format("skipped unreadable line %s in 
configuration file '%s': '%s'", lineNumer,
+                        filename, line));
+            }
+
+            lineNumer++;
+        }
+    }
+
+    private String replaceHome(String filename) {
+        String home = System.getenv("HOME");
+        return home != null ? filename.replace("~", home) : filename;
+    }
+
+    /**
+     * Remove the given section and all options in it.
+     */
+    public void removeSection(String section) {
+        sections.row(section).clear();
+    }
+
+    /**
+     * Set an option to a string value in a section.
+     *
+     * @param section section of interest
+     * @param option  option of interest
+     * @param value   value to set
+     */
+    public void setValueNumber(String section, String option,
+                               long value) {
+        setValueString(section, option, "" + value);
+    }
+
+    /**
+     * Set an option to a string value in a section.
+     *
+     * @param section section of interest
+     * @param option  option of interest
+     * @param value   value to set
+     */
+    public void setValueString(String section, String option,
+                               String value) {
+        sections.put(section, option, value);
+    }
+
+    /**
+     * Write configuration file.
+     *
+     * @param filename where to write the configuration
+     */
+    public void write(String filename) throws IOException {
+        BufferedWriter w = Files.newWriter(new File(filename), Charsets.UTF_8);
+        try {
+            for (String section : sections.rowKeySet()) {
+                w.write("["+section+"]");
+                w.newLine();
+                for (Map.Entry<String,String> e : 
sections.row(section).entrySet()) {
+                    w.write(e.getKey() + " = " + e.getValue());
+                    w.newLine();
+                }
+            }
+        } finally {
+            w.close();
+        }
+    }
+
+    public String serialize() {
+        StringBuffer buf = new StringBuffer();
+        for (Map.Entry<String, Map<String,String>> section : 
sections.rowMap().entrySet()) {
+            buf.append("[" + section.getKey() + "]\n");
+            for (Map.Entry<String, String> option : 
section.getValue().entrySet()) {
+                buf.append(option.getKey() + " = " + option.getValue() + "\n");
+            }
+        }
+        return buf.toString();
+    }
+
+
+    public void loadDefaults() {
+        Collection<File> dirs = new ArrayList<File>(5);
+        dirs.add(new File("/usr/share/gnunet/config.d/"));
+        dirs.add(new File("/usr/local/share/gnunet/config.d/"));
+        String pfx = System.getenv("GNUNET_PREFIX");
+        if (pfx != null) {
+            dirs.add(new File(pfx, "share/gnunet/config.d/"));
+            dirs.add(new File(pfx, "config.d/"));
+            dirs.add(new File(pfx, "gnunet/config.d/"));
+        }
+        for (File dir : dirs) {
+            if (dir.exists() && dir.isDirectory()) {
+                File[] files = dir.listFiles();
+                if (files == null) {
+                    continue;
+                }
+                for (File f : files) {
+                    parse(f.getAbsolutePath());
+                }
+            }
+        }
+    }
+
+    public static class ConfigurationException extends RuntimeException {
+        public ConfigurationException(String string) {
+            super(string);
+        }
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/util/Connection.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/Connection.java                   
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/Connection.java   2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,696 @@
+/*
+ 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.Construct;
+import org.gnunet.construct.MessageLoader;
+import org.gnunet.construct.ProtocolViolationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOError;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.nio.channels.spi.SelectorProvider;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Integrates sockets with the gnunet-java message loop / the scheduler.
+ */
+public class Connection {
+    private static final Logger logger = LoggerFactory
+            .getLogger(Connection.class);
+
+    /**
+     * The underlying socket the client is using to talk with the service.
+     */
+    private SocketChannel connectionChannel = null;
+
+    /**
+     * The list of all address probes.
+     * Each address probe tries to connect via a different address.
+     */
+    private List<AddressProbe> addressProbes = null;
+
+    /**
+     * The task that is currently used by the resolve mechanism.
+     */
+    private Cancelable resolveHandle = null;
+
+    /**
+     * The task that is responsible for establishing the connection to the 
server.
+     */
+    private Cancelable connectHandle = null;
+
+    /**
+     * The ReceiveHelper responsible for receiving a whole message from the 
service
+     * and calling the respective MessageReceiver.
+     */
+    private ReceiveHelper currentReceiveHelper = null;
+
+    /**
+     * The buffer with the (partial) message received from the service.
+     * Initially, this buffer has the size of the smallest possible messages, 
but grows when
+     * receiving larger messages.
+     */
+    private ByteBuffer recvBuffer = 
ByteBuffer.allocate(GnunetMessage.Header.SIZE);
+
+    /**
+     * The handle for the current transmission. Writes data to the socket.
+     */
+    private TransmitHelper currentTransmitHelper = null;
+
+    /**
+     * The handle for the next transmission. The next transmission will become 
the current
+     * transmission once the current transmission has completed.
+     * While nextTransmitHelper is not null, no new transmit requests may be 
scheduled.
+     */
+    private TransmitHelper nextTransmitHelper = null;
+
+    /**
+     * The transmitters passed to transmitReadyNotify(...) write to this 
buffer by calling
+     * methods on the MessageSink passed to the 
Transmitter.transmit(MessageSink s) method.
+     * Initially, this buffer has the size of the smallest possible messages, 
but grows when
+     * transmitting larger messages.
+     */
+    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;
+
+
+    /**
+     * An address probe is a connection to a socket that may succeed or not.
+     * The first address probe that succeeded is used for this connection.
+     */
+    private static class AddressProbe {
+        Cancelable connectTask;
+        SocketChannel channel;
+
+        public void cancel() {
+            if (connectTask != null) {
+                connectTask.cancel();
+            }
+            if (channel != null) {
+                try {
+                    channel.close();
+                } catch (IOException e) {
+                    // nothing we can do here
+                }
+            }
+        }
+    }
+
+    /**
+     * Represents a request for transmission.
+     */
+    public interface TransmitHandle extends Cancelable {
+        /**
+         * Cancel a request for the transmit ready notification.
+         * This does *not* cancel a transmission that already has been started.
+         */
+        public void cancel();
+    }
+
+    /**
+     * An interface that allows the Transmitter.transmit method to deliver 
their messages
+     * to the client, which sends them to the service.
+     */
+    public interface MessageSink {
+        public void send(GnunetMessage.Body m);
+    }
+
+    /**
+     * The ReceiveHelper is responsible for receiving a whole
+     * GnunetMessage and call the respective MessageReceiver with the message 
on success,
+     * and null on failure or timeout.
+     */
+    private class ReceiveHelper implements Scheduler.Task {
+        private MessageReceiver receiver;
+        private RelativeTime timeout;
+        private GnunetMessage.Header msgh = null;
+        private Scheduler.TaskConfiguration recvTask = null;
+        private boolean finished = false;
+        // is this receiver actively working? if not, the connection process 
has to kick off the receiver
+        // (or select behaves badly)
+        private boolean working = false;
+
+        public ReceiveHelper(MessageReceiver receiver, RelativeTime timeout) {
+            this.receiver = receiver;
+            this.timeout = timeout;
+        }
+
+        public void dispatchMessage() {
+            assert msgh != null;
+            currentReceiveHelper = null;
+            finished = true;
+            recvBuffer.flip();
+
+            boolean found = true;
+            Class unionClass = null;
+
+            try {
+                unionClass = 
MessageLoader.getUnionClass(GnunetMessage.Body.class, msgh.messageType);
+            } catch (ProtocolViolationException e) {
+                found = false;
+            }
+
+            logger.debug("dispatching received message");
+            if (found) {
+                GnunetMessage msg;
+                try {
+                    msg = Construct.parseAs(recvBuffer, GnunetMessage.class);
+                } catch (OutOfMemoryError e) {
+                    throw new OutOfMemoryError("oom while parsing " + 
unionClass);
+                }
+                receiver.process(msg.body);
+            } else {
+                UnknownMessageBody b = new UnknownMessageBody();
+                b.id = msgh.messageType;
+
+                // may throw exception, doesn't matter as it's the last call
+                receiver.process(b);
+            }
+        }
+
+        @Override
+        public void run(Scheduler.RunContext ctx) {
+            recvTask = null;
+            if (ctx.reasons.contains(Scheduler.Reason.TIMEOUT)) {
+                currentReceiveHelper = null;
+                receiver.handleError();
+            } else if (ctx.reasons.contains(Scheduler.Reason.READ_READY)) {
+                try {
+                    int n = connectionChannel.read(recvBuffer);
+                    if (n == -1) {
+                        currentReceiveHelper = null;
+                        logger.warn("lost connection to service");
+                        connectionChannel.close();
+                        connectionChannel = null;
+                        if (Connection.this.currentTransmitHelper != null) {
+                            Connection.this.currentTransmitHelper.cancel();
+                            Connection.this.currentTransmitHelper = null;
+                        }
+                        try {
+                            receiver.handleError();
+                        } finally {
+                            return;
+                        }
+                    }
+                    logger.debug(String.format("read %s bytes from %s", n, 
connectionChannel.socket().toString()));
+                } catch (IOException e) {
+                    logger.error("read failed:", e);
+                    try {
+                        receiver.handleError();
+                    } finally {
+                        return;
+                    }
+                }
+                if (recvBuffer.remaining() == 0) {
+                    if (msgh != null) {
+                        dispatchMessage();
+                    } else {
+                        recvBuffer.rewind();
+                        msgh = Construct.parseAs(recvBuffer, 
GnunetMessage.Header.class);
+
+                        logger.debug("expecting message of size {}, type {}", 
msgh.messageSize, msgh.messageType);
+                        if (msgh.messageSize > GnunetMessage.Header.SIZE) {
+                            if (recvBuffer.capacity() < msgh.messageSize) {
+                                ByteBuffer buf = 
ByteBuffer.allocate(msgh.messageSize);
+                                recvBuffer.flip();
+                                buf.put(recvBuffer);
+                                recvBuffer = buf;
+                            }
+                            recvBuffer.limit(msgh.messageSize);
+                            schedule();
+                        } else {
+                            dispatchMessage();
+                        }
+                    }
+                } else {
+                    schedule();
+                }
+            } else if (ctx.reasons.contains(Scheduler.Reason.SHUTDOWN)) {
+                // nothing to do!
+            } else {
+                // XXX: what to do here?
+                throw new RuntimeException("receive failed");
+            }
+        }
+
+        private void schedule() {
+            working = true;
+            recvTask = Scheduler.addRead(timeout, connectionChannel, this);
+        }
+
+        public void cancel() {
+            if (finished) {
+                throw new AssertionError("canceling finished receive");
+            }
+            if (recvTask != null) {
+                recvTask.cancel();
+                recvTask = null;
+            }
+        }
+    }
+
+
+    private class TransmitHelper implements Scheduler.Task, MessageSink {
+        private final MessageTransmitter transmitter;
+
+        private Cancelable notifyTimeoutTask;
+
+        private Cancelable transmitTask = null;
+
+        public TransmitHelper(final MessageTransmitter transmitter, 
RelativeTime notifyTimeout) {
+            this.transmitter = transmitter;
+
+            Scheduler.TaskConfiguration tc = new 
Scheduler.TaskConfiguration(notifyTimeout,
+                    new Scheduler.Task() {
+                        @Override
+                        public void run(Scheduler.RunContext ctx) {
+                            transmitter.handleError();
+                        }
+                    });
+
+            notifyTimeoutTask = tc.schedule();
+        }
+
+        public void cancel() {
+            if (transmitTask != null) {
+                transmitTask.cancel();
+                transmitTask = null;
+            }
+            if (notifyTimeoutTask != null) {
+                notifyTimeoutTask.cancel();
+                notifyTimeoutTask = null;
+            }
+        }
+
+        @Override
+        public void run(Scheduler.RunContext ctx) {
+            this.transmitTask = null;
+            if (connectionChannel == null) {
+                logger.error("could not write to channel (null)");
+                return;
+            }
+            try {
+                int n = connectionChannel.write(transmitBuffer);
+                // logger.debug("connectionChannel has written " + n + " bytes 
to " + connectionChannel.socket().toString());
+            } catch (IOException e) {
+                throw new IOError(e);
+            }
+            if (transmitBuffer.remaining() == 0) {
+                //logger.debug("sent " + transmitBuffer.position() + "bytes 
complete message");
+                if (nextTransmitHelper == null) {
+                    currentTransmitHelper = null;
+                } else {
+                    currentTransmitHelper = nextTransmitHelper;
+                    // we need to to this so the transmit callback can do 
notifyTransmitReady
+                    TransmitHelper tmpTransmitHelper = nextTransmitHelper;
+                    nextTransmitHelper = null;
+                    tmpTransmitHelper.start();
+
+                }
+            } else {
+                schedule();
+            }
+        }
+
+        /**
+         * called to notify when we are ready to put new messages in the 
transmit buffer
+         */
+        public void start() {
+            notifyTimeoutTask.cancel();
+            notifyTimeoutTask = null;
+            transmitBuffer.clear();
+            transmitter.transmit(TransmitHelper.this);
+            transmitBuffer.flip();
+            schedule();
+        }
+
+        private void schedule() {
+            if (disconnected) {
+                return;
+            }
+            // timeout is forever, because there is no way to directly limit 
the transmission time
+            // of a message, only the max. wait time before transmission.
+            // cancel must be called on the transmitTask if we disconnect
+            Scheduler.TaskConfiguration tc = new 
Scheduler.TaskConfiguration(RelativeTime.FOREVER, this);
+            tc.selectWrite(connectionChannel);
+            this.transmitTask = tc.schedule();
+        }
+
+        @Override
+        public void send(final GnunetMessage.Body m) {
+            final GnunetMessage gm = new GnunetMessage();
+            gm.header = new GnunetMessage.Header();
+            gm.body = m;
+            Construct.patch(gm);
+            gm.header.messageSize = Construct.getSize(gm);
+            byte[] b = Construct.toBinary(gm);
+            if (b.length != gm.header.messageSize) {
+                throw new AssertionError(
+                        String.format("tried to send message with binary size 
%s but size in header %s",
+                                b.length, gm.header.messageSize));
+            }
+            logger.debug("sending message (size={},type={})", b.length, 
gm.header.messageType);
+            if (transmitBuffer.remaining() < b.length) {
+                ByteBuffer buf = ByteBuffer.allocate(b.length + 
transmitBuffer.capacity());
+                transmitBuffer.flip();
+                buf.put(transmitBuffer);
+                transmitBuffer = buf;
+            }
+            transmitBuffer.put(b);
+        }
+    }
+
+    /**
+     * Create a connection to the given hostname/port.
+     *
+     * @param hostname name of the host to connect to
+     * @param port     port of the host to connect to
+     */
+    public Connection(String hostname, int port) {
+        addressProbes = new LinkedList<AddressProbe>();
+        ConnectionResolveHandler addressHandler = new 
ConnectionResolveHandler(port);
+        resolveHandle = Resolver.getInstance().resolveHostname(hostname, 
RelativeTime.FOREVER, addressHandler);
+    }
+
+    public Connection(SocketChannel sock) {
+        assert sock != null;
+        this.connectionChannel = sock;
+    }
+
+
+    class ConnectionResolveHandler implements Resolver.AddressCallback {
+        private final int port;
+
+        public ConnectionResolveHandler(int port) {
+            this.port = port;
+        }
+
+        @Override
+        public void onAddress(InetAddress addr) {
+            final SocketChannel channel = createChannel();
+            try {
+                channel.connect(new InetSocketAddress(addr, port));
+            } catch (IOException e) {
+                logger.error("could not connect to host");
+                return;
+            }
+
+            final AddressProbe addressProbe = new AddressProbe();
+            addressProbe.channel = channel;
+            Scheduler.TaskConfiguration tc = new 
Scheduler.TaskConfiguration(RelativeTime.FOREVER,
+                    new Scheduler.Task() {
+                        @Override
+                        public void run(Scheduler.RunContext ctx) {
+                            addressProbe.connectTask = null;
+                            if 
(ctx.reasons.contains(Scheduler.Reason.SHUTDOWN)) {
+                                return;
+                            }
+                            Connection.this.finishConnect(addressProbe);
+                        }
+                    });
+
+            // our channel has already disconnected
+            if (!channel.isOpen()) {
+                return;
+            }
+
+            tc.selectConnect(channel);
+
+            addressProbe.connectTask = tc.schedule();
+        }
+
+        @Override
+        public void onFinished() {
+            resolveHandle = null;
+        }
+
+        @Override
+        public void onTimeout() {
+            // do nothing
+            // todo: is this correct?
+        }
+    }
+
+
+    private void finishConnect(AddressProbe probe) {
+        // can happen if the addres probe task was already scheduled
+        if (connectionChannel != null) {
+            try {
+                probe.channel.close();
+            } catch (IOException e) {
+                logger.error("could not close channel", e);
+            }
+            return;
+        }
+
+        SocketChannel channel = probe.channel;
+        boolean connected;
+        try {
+            connected = channel.finishConnect();
+        } catch (IOException e) {
+            logger.debug("finishConnect() was not successful: {}", (Object) e);
+            return;
+        }
+
+        if (!connected) {
+            logger.error("socket reported OP_CONNECT but is not connected");
+            return;
+        }
+
+        for (AddressProbe addressProbe : addressProbes) {
+            if (addressProbe != probe && addressProbe.connectTask != null) {
+                addressProbe.connectTask.cancel();
+                try {
+                    addressProbe.channel.close();
+                } catch (IOException e) {
+                    logger.error("could not close channel", e);
+                }
+            }
+        }
+
+        addressProbes.clear();
+
+        connectionChannel = channel;
+
+        if (currentTransmitHelper != null) {
+            currentTransmitHelper.start();
+        }
+        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);
+        }
+    }
+
+    /**
+     * Open a channel for this connection in non-blocking mode
+     */
+    private SocketChannel createChannel() {
+        try {
+            SocketChannel channel = 
SelectorProvider.provider().openSocketChannel();
+            channel.configureBlocking(false);
+            return channel;
+        } catch (IOException e) {
+            // this is fatal, no retry necessary
+            throw new IOError(e);
+        }
+    }
+
+    public boolean isConnected() {
+        return connectionChannel != null && connectionChannel.isConnected();
+    }
+
+
+    public interface ReceiveHandle extends Cancelable {
+    }
+
+    /**
+     * Receive one message from the network.
+     *
+     * @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, final MessageReceiver 
receiver) {
+        if (currentReceiveHelper != null) {
+            throw new AssertionError("receive must not be called while 
receiving");
+        }
+
+        if (!isConnected()) {
+            throw new AssertionError("cannot receive if not connected");
+        }
+
+        recvBuffer.clear();
+        recvBuffer.limit(GnunetMessage.Header.SIZE);
+        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();
+        }
+
+        return new ReceiveHandle() {
+            @Override
+            public void cancel() {
+                rh.cancel();
+            }
+        };
+    }
+
+    /**
+     * 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))
+     * @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, null 
if request could be satisfied immediately
+     */
+    public TransmitHandle notifyTransmitReady(int size, RelativeTime timeout, 
final MessageTransmitter transmitter) {
+        if (disconnected) {
+            throw new AssertionError("notifyTransmitReady called on a closed 
connection");
+        }
+        if (nextTransmitHelper != null) {
+            throw new AssertionError(
+                    "previous transmit request must have completed before 
calling notifyTransmitReady again");
+        }
+
+        if (timeout.getMicroseconds() <= 0) {
+            throw new AssertionError("notifyTransmitReady timeout must be 
positive");
+        }
+
+        if (!isConnected()) {
+            throw new AssertionError("notifyTransmitHandle can only be called 
once connected");
+        }
+
+        final TransmitHelper transmit = new TransmitHelper(transmitter, 
timeout);
+
+        if (currentTransmitHelper == null) {
+            currentTransmitHelper = transmit;
+            currentTransmitHelper.start();
+            return null;
+        }
+
+        nextTransmitHelper = transmit;
+
+        return new TransmitHandle() {
+            @Override
+            public void cancel() {
+                transmit.cancel();
+            }
+        };
+    }
+
+
+    /**
+     * 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.
+     */
+    public void disconnect() {
+        if (disconnected) {
+            logger.error("disconnect called twice");
+        }
+        disconnected = true;
+
+        if (currentTransmitHelper != null) {
+            currentTransmitHelper.cancel();
+            currentTransmitHelper = null;
+        }
+
+        if (nextTransmitHelper != null) {
+            nextTransmitHelper.cancel();
+            nextTransmitHelper = null;
+        }
+
+        if (currentReceiveHelper != null) {
+            currentReceiveHelper.cancel();
+            currentReceiveHelper = null;
+        }
+
+        if (resolveHandle != null) {
+            resolveHandle.cancel();
+            resolveHandle = null;
+        }
+        if (connectHandle != null) {
+            connectHandle.cancel();
+            connectHandle = null;
+        }
+        if (connectionChannel != null) {
+            try {
+                connectionChannel.close();
+            } catch (IOException e) {
+                throw new IOError(e);
+            }
+            connectionChannel = null;
+        }
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/util/Continuation.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/Continuation.java                 
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/Continuation.java 2013-08-27 
17:16:18 UTC (rev 28880)
@@ -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.
+ */
+
+package org.gnunet.util;
+
+public interface Continuation {
+    void cont(boolean success);
+}

Added: gnunet-java/src/main/java/org/gnunet/util/GnunetMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/GnunetMessage.java                
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/GnunetMessage.java        
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,81 @@
+/*
+ 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.*;
+
+
+/**
+ * Every message used to communicate between gnunet components uses this 
format.
+ * First, a header is sent, containing the size of the overall message 
(including the header), as
+ * well as the type of the message. After that the message body is sent, whose 
format is specified
+ * by the message type.
+ *
+ */
+public final class GnunetMessage implements Message {
+    public static final int MINIMAL_SIZE = Header.SIZE;
+
+
+    /**
+     * The header of every gnunet message.
+     */
+    public static final class Header implements Message {
+        public static final int SIZE = 4;
+
+        @FrameSize
+        @UInt16
+        public int messageSize;
+
+        @UInt16
+        public int messageType;
+    }
+
+    /**
+     * The common interface for every message body.
+     *
+     */
+    public static interface Body extends MessageUnion {
+    }
+
+
+    /**
+     * Create a GnunetMessage from its body only. The header is added and 
filled with the relevant information
+     * automatically.
+     *
+     * @param b the message body to convert
+     * @return a complete and valid gnunet message
+     */
+    public static GnunetMessage fromBody(Body b) {
+        GnunetMessage msg = new GnunetMessage();
+        msg.header = new Header();
+        msg.header.messageSize = Header.SIZE + Construct.getSize(b);
+        msg.header.messageType = 
MessageLoader.getUnionTag(GnunetMessage.Body.class, b.getClass());
+        msg.body = b;
+        return msg;
+    }
+
+    @NestedMessage
+    public Header header;
+
+    @Union(tag = "header.messageType")
+    public Body body;
+}

Added: gnunet-java/src/main/java/org/gnunet/util/HashCode.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/HashCode.java                     
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/HashCode.java     2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,94 @@
+/*
+ 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 com.google.common.base.Charsets;
+import org.gnunet.construct.FixedSizeIntegerArray;
+import org.gnunet.construct.Message;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+
+/**
+ * 512-bit hash code
+ */
+public class HashCode implements Message {
+
+    @FixedSizeIntegerArray(length = 64, signed = false, bitSize = 8)
+    public byte[] data; // should be immutable, final, can't be due to 
construct
+
+
+    public HashCode() {
+        // construct needs a default c'tor
+    }
+
+    public HashCode(byte[] hash) {
+        if (hash.length != 64) {
+            throw new AssertionError("HashCode has to have length 64");
+        }
+        data = Arrays.copyOf(hash, hash.length);
+    }
+
+    /**
+     * Create the HashCode of an UTF-8 String using SHA-512.
+     *
+     * @param s the string to hash
+     */
+    public HashCode(String s) {
+        MessageDigest digest;
+        try {
+            digest = MessageDigest.getInstance("SHA-512");
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("crypto algorithm required but not 
provided");
+        }
+        byte[] data = digest.digest(s.getBytes(Charsets.UTF_8));
+        if (data.length != 64) {
+            throw new RuntimeException("error in SHA512 algorithm");
+        }
+        this.data = data;
+    }
+
+    public boolean isAllZero() {
+        for (byte aData : data) {
+            if (aData != 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof HashCode)) {
+            return false;
+        }
+        HashCode hashCode = (HashCode) other;
+        return Arrays.equals(this.data, hashCode.data);
+    }
+
+    @Override
+    public int hashCode() {
+        return Arrays.hashCode(this.data);
+    }
+}
\ No newline at end of file

Added: gnunet-java/src/main/java/org/gnunet/util/Helper.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/Helper.java                       
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/Helper.java       2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,208 @@
+package org.gnunet.util;
+
+import org.gnunet.construct.Construct;
+import org.gnunet.construct.Message;
+import org.gnunet.construct.MessageLoader;
+import org.gnunet.construct.ProtocolViolationException;
+import org.gnunet.mq.Envelope;
+import org.gnunet.mq.MessageQueue;
+
+import java.io.IOError;
+import java.io.IOException;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.util.EnumSet;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Process that we can communicate with standard GNUnet messages over 
stdin/stdout.
+ */
+public class Helper extends MessageQueue {
+
+    private final ProcessBuilder processBuilder;
+    private final RunaboutMessageReceiver receiver;
+    private Process process;
+
+    private volatile GnunetMessage.Body writeMessage;
+
+    private final class WriteThread implements Runnable {
+        @Override
+        public void run() {
+            GnunetMessage.Body msg;
+            while (true) {
+                synchronized (Helper.this) {
+                    while (writeMessage == null) {
+                        try {
+                            wait();
+                        } catch (InterruptedException e) {
+                            // do nothing
+                        }
+                    }
+                    // we now have a message we can send
+                    msg = writeMessage;
+                    writeMessage = null;
+                    // somebody can set the next send message
+                }
+                byte[] data = Construct.toBinary(GnunetMessage.fromBody(msg));
+                try {
+                    process.getOutputStream().write(data);
+                } catch (IOException e) {
+                    // fixme: what now?
+                }
+                Scheduler.addContinuation(new Scheduler.Task() {
+                    @Override
+                    public void run(Scheduler.RunContext ctx) {
+                        reportMessageSent();
+                    }
+                }, EnumSet.noneOf(Scheduler.Reason.class));
+            }
+        }
+    }
+
+    private final class ReadThread implements Runnable {
+        private ByteBuffer buffer;
+        ReadableByteChannel channel;
+
+        private void fillBuffer() {
+            while (buffer.hasRemaining()) {
+                try {
+                    channel.read(buffer);
+                } catch (IOException e) {
+                    // FIXME
+                    return;
+                }
+            }
+        }
+
+        private void scheduleInvokeReceiver(final GnunetMessage.Body body) {
+            Scheduler.addContinuation(new Scheduler.Task() {
+                @Override
+                public void run(Scheduler.RunContext ctx) {
+                    receiver.process(body);
+                }
+            }, EnumSet.noneOf(Scheduler.Reason.class));
+
+        }
+
+        @Override
+        public void run() {
+            // allocate just enough for the message header
+            buffer = ByteBuffer.allocate(4);
+            channel = Channels.newChannel(process.getInputStream());
+            while (true) {
+                buffer.clear();
+                buffer.limit(4);
+                fillBuffer();
+                buffer.rewind();
+                GnunetMessage.Header msgh = Construct.parseAs(buffer, 
GnunetMessage.Header.class);
+                if (msgh.messageSize > GnunetMessage.Header.SIZE) {
+                    if (buffer.capacity() < msgh.messageSize) {
+                        ByteBuffer newBuf = 
ByteBuffer.allocate(msgh.messageSize);
+                        buffer.flip();
+                        newBuf.put(buffer);
+                        buffer = newBuf;
+                    }
+                    buffer.limit(msgh.messageSize);
+                    fillBuffer();
+                }
+                // we now have a complete message
+                // prepare for reading again
+                buffer.flip();
+
+                boolean found = true;
+                Class unionClass = null;
+
+                try {
+                    unionClass = 
MessageLoader.getUnionClass(GnunetMessage.Body.class, msgh.messageType);
+                } catch (ProtocolViolationException e) {
+                    found = false;
+                }
+                if (found) {
+                    GnunetMessage msg;
+                    msg = Construct.parseAs(buffer, GnunetMessage.class);
+                    scheduleInvokeReceiver(msg.body);
+                } else {
+                    UnknownMessageBody b = new UnknownMessageBody();
+                    b.id = msgh.messageType;
+                    scheduleInvokeReceiver(b);
+                }
+            }
+        }
+    }
+
+
+    public Helper(boolean withControlPipe, String binaryName, List<String> 
argv,
+                  RunaboutMessageReceiver receiver) {
+        this.receiver = receiver;
+        List<String> command = new LinkedList<String>();
+        if (binaryName == null) {
+            throw new AssertionError();
+        }
+        command.add(binaryName);
+        if (argv != null)
+            command.addAll(argv);
+        processBuilder = new ProcessBuilder(command);
+        try {
+            process = processBuilder.start();
+        } catch (IOException e) {
+            throw new IOError(e);
+        }
+    }
+
+    /**
+     * Sends termination signal to the helper process.  The helper process is 
not
+     * reaped; call GNUNET_HELPER_wait() for reaping the dead helper process.
+     *
+     * @param softkill if GNUNET_YES, signals termination by closing the 
helper's
+     *          stdin; GNUNET_NO to signal termination by sending SIGTERM to 
helper
+     * @return true on success, false on failure
+     */
+    public boolean kill(boolean softkill) {
+        if (softkill) {
+            try {
+                process.getInputStream().close();
+            } catch (IOException e) {
+                return false;
+            }
+            return true;
+        }
+        process.destroy();
+        return true;
+    }
+
+    /**
+     * Reap the helper process.  This call is blocking(!).  The helper process
+     * should either be sent a termination signal before or should be dead 
before
+     * calling this function
+     *
+     * @return true on success, false on failure
+     */
+    public boolean waitFor() {
+        try {
+            process.waitFor();
+        } catch (InterruptedException e) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    protected void submit(Envelope ev) {
+        synchronized (this) {
+            if (writeMessage != null)
+                throw new AssertionError("message queue not implemented 
correctly");
+            writeMessage = ev.message;
+            notifyAll();
+        }
+    }
+
+    @Override
+    protected void retract() {
+        synchronized (this) {
+            writeMessage = null;
+        }
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/util/MessageReceiver.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/MessageReceiver.java              
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/MessageReceiver.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,41 @@
+/*
+     This file is part of GNUnet.
+     (C) 2009 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 2, 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;
+
+/**
+ * Callback object for receiving messages.
+ * 
+ */
+public interface MessageReceiver {
+
+    /**
+     * Called when a message is received
+     * 
+     * @param msg   message received, null on deadline or fatal error
+     */
+    public void process(GnunetMessage.Body msg);
+
+
+    /**
+     * Called when an error (timeout, loss of connection) occured before 
receiving the message.
+     */
+    public void handleError();
+}

Added: gnunet-java/src/main/java/org/gnunet/util/MessageTransmitter.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/MessageTransmitter.java           
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/MessageTransmitter.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,43 @@
+/*
+ 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;
+
+
+/**
+ * Callback object for transmitting messages.
+ */
+public interface MessageTransmitter {
+    /**
+     * Called when the client is ready to transmit messages, or on 
timeout/error.
+     *
+     * @param sink A message sink that receives messages to be transmitted by 
the client,
+     *             or null on timeout/error.
+     */
+    public void transmit(Connection.MessageSink sink);
+
+
+    /**
+     * Called when the transmit request could not be fullfilled.
+     *
+     * After transmit has been called, handleError will not be called anymore 
(until the next transmit request)
+     */
+    void handleError();
+}

Added: gnunet-java/src/main/java/org/gnunet/util/PeerIdentity.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/PeerIdentity.java                 
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/PeerIdentity.java 2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,69 @@
+/*
+ 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.FixedSizeIntegerArray;
+import org.gnunet.construct.Message;
+
+import java.util.Arrays;
+
+
+/**
+ * Identity of a peer, stored as 512-bit public key.
+ */
+public class PeerIdentity implements Message {
+
+    @FixedSizeIntegerArray(length = 64, signed = false, bitSize = 8)
+    public byte[] data;
+
+    static final String HEXES = "0123456789ABCDEF";
+
+    /**
+     * Creates a zero-filled peer identity
+     */
+    public PeerIdentity() {
+        data = new byte[64];
+    }
+
+    public String getHex() {
+        final StringBuilder hex = new StringBuilder( 2 * data.length );
+        for (final byte b : data) {
+            hex.append(HEXES.charAt((b & 0xF0) >> 4))
+                    .append(HEXES.charAt((b & 0x0F)));
+        }
+        return hex.toString();
+    }
+
+    public String toString() {
+        return Strings.dataToString(data);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return obj != null && obj instanceof PeerIdentity && 
Arrays.equals(((PeerIdentity) obj).data, this.data);
+    }
+
+    @Override
+    public int hashCode() {
+        return Arrays.hashCode(data);
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/util/Program.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/Program.java                      
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/Program.java      2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,229 @@
+/*
+ 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.apache.log4j.*;
+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;
+
+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 {
+    private static final Logger logger = LoggerFactory
+            .getLogger(Program.class);
+
+
+    protected final Configuration cfg = new Configuration();
+
+    @Argument(shortname = "c", longname = "config",
+            description = "Path of the configuration file",
+            argumentName = "FILENAME",
+            action = ArgumentAction.STORE_STRING)
+    public String cfgFileName;
+
+    @Argument(shortname = "h", longname = "help",
+            description = "print this help message",
+            action = ArgumentAction.SET)
+    public boolean printHelp;
+
+    @Argument(shortname = "v", longname = "version",
+            description = "print version",
+            action = ArgumentAction.SET)
+    public boolean showVersion;
+
+
+    @Argument(shortname = "L", longname = "log",
+            description = "configure logging to use LOGLEVEL",
+            argumentName = "LOGLEVEL",
+            action = ArgumentAction.STORE_STRING)
+    public String logLevel;
+
+    @Argument(shortname = "l", longname = "logfile",
+            description = "configure logging to write logs to LOGFILE",
+            argumentName = "LOGFILE",
+            action = ArgumentAction.STORE_STRING)
+    public String logFile;
+
+
+    protected String[] unprocessedArgs;
+
+    private final String[] args;
+
+    private int returnValue = 0;
+
+
+    /**
+     * A program with the desired environment for a gnunet utility.
+     * 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
+     *             and configure log levels.
+     */
+    public Program(String... args) {
+        this.args = args;
+
+        /*
+         * Remember: We can't parse command line arguments here, as java's 
initialization order
+         * dictates that member variables of subclasses are initialized 
*after* the superclass constructor (here).
+         */
+    }
+
+    /**
+     * 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();
+
+        rootLogger.removeAllAppenders();
+
+        // %c{2}: category 2 levels
+        Layout layout = new PatternLayout("%d{dd MMM yyyy HH:mm:ss-SSS} %c{2} 
%p: %m%n");
+
+        if (logFile == null) {
+            rootLogger.addAppender(new ConsoleAppender(layout, 
ConsoleAppender.SYSTEM_OUT));
+        } else {
+            Appender appender = null;
+            try {
+                appender = new FileAppender(layout, logFile);
+            } catch (IOException e) {
+                logger.warn("could not open log file {}", logFile);
+            }
+            if (appender!= null) {
+                rootLogger.removeAllAppenders();
+                rootLogger.addAppender(appender);
+            }
+        }
+        if (logLevel == null) {
+            rootLogger.setLevel(Level.INFO);
+        } else if (logLevel.equalsIgnoreCase("debug")) {
+            rootLogger.setLevel(Level.DEBUG);
+        } else if (logLevel.equalsIgnoreCase("info")) {
+            rootLogger.setLevel(Level.INFO);
+        } else if (logLevel.equalsIgnoreCase("warn") || 
logLevel.equalsIgnoreCase("warning")) {
+            rootLogger.setLevel(Level.WARN);
+        } else if (logLevel.equalsIgnoreCase("error")) {
+            rootLogger.setLevel(Level.ERROR);
+        } else if (logLevel.equalsIgnoreCase("off")) {
+            rootLogger.setLevel(Level.OFF);
+        } else {
+            rootLogger.setLevel(Level.INFO);
+            logger.info("unknown log level '{}'; defaulting to INFO", 
logLevel);
+        }
+    }
+
+    public static void configureLogging(String logLevel) {
+        configureLogging(logLevel, null);
+    }
+
+    public static void configureLogging() {
+        configureLogging(null, null);
+    }
+
+
+    /**
+     * Override to display a different help text on "-h/--help"
+     *
+     * @return the help text
+     */
+    protected String makeHelpText() {
+        return "gnunet-java tool";
+    }
+
+    /**
+     * Override to display a different version description on "-h/--help"
+     *
+     * @return version description
+     */
+    protected String makeVersionDescription() {
+        return "development version of gnunet-java";
+    }
+
+    final protected void setReturnValue(int x) {
+        returnValue = x;
+    }
+
+    /**
+     * Start the Program as the initial task of the Scheduler.
+     */
+    public final void start() {
+        Parser optParser = new Parser(this);
+        unprocessedArgs = optParser.parse(args);
+
+        configureLogging(logLevel, logFile);
+
+        cfg.loadDefaults();
+
+        if (cfgFileName != null) {
+            cfg.parse(cfgFileName);
+        }
+
+        Resolver.getInstance().setConfiguration(cfg);
+
+        if (showVersion) {
+            System.out.println(makeVersionDescription());
+        } else if (printHelp) {
+            System.out.println(makeHelpText());
+            System.out.print(optParser.getHelp());
+        } else {
+            Scheduler.run(new Scheduler.Task() {
+                public void run(Scheduler.RunContext c) {
+                    Program.this.runHook();
+                }
+            });
+        }
+
+        System.exit(returnValue);
+    }
+
+    /**
+     * 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.
+     */
+    public abstract void run();
+
+    public final Configuration getConfiguration() {
+        return cfg;
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/util/RelativeTime.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/RelativeTime.java                 
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/RelativeTime.java 2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,231 @@
+/*
+ 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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Positive amount of time with no point of reference.
+ * 
+ * @author Florian Dold
+ */
+public final class RelativeTime implements Comparable<RelativeTime> {
+    private static final Logger logger = LoggerFactory
+            .getLogger(RelativeTime.class);
+
+    public static final RelativeTime MICROSECOND = new RelativeTime(1);
+    public static final RelativeTime MILLISECOND = MICROSECOND.multiply(1000);
+    public static final RelativeTime SECOND = MILLISECOND.multiply(1000);
+    public static final RelativeTime MINUTE = SECOND.multiply(60);
+    public static final RelativeTime HOUR = MINUTE.multiply(60);
+    public static final RelativeTime DAY = HOUR.multiply(24);
+    public static final RelativeTime WEEK = DAY.multiply(7);
+    public static final RelativeTime MONTH = DAY.multiply(30);
+    public static final RelativeTime YEAR = DAY.multiply(365);
+
+    public static final RelativeTime ZERO = new RelativeTime(0);
+    public static final RelativeTime FOREVER = new 
RelativeTime(Long.MAX_VALUE);
+
+    /**
+     * Time offset in microseconds.
+     */
+    private final long rel_value_us;
+
+    /**
+     * Create a new RelativeTime value, with a given time in milliseconds.
+     *
+     * @param abs_value time in milliseconds
+     */
+    public RelativeTime(final long abs_value) {
+        this.rel_value_us = abs_value;
+    }
+
+    public static RelativeTime fromMilliseconds(final long ms) {
+        return new RelativeTime(ms * 1000);
+    }
+
+    public static RelativeTime fromMicroseconds(final long us) {
+        return new RelativeTime(us);
+    }
+
+    /**
+     * Add relative times together.
+     * 
+     * @param other
+     *            the other timestamp
+     * 
+     * @return this + other
+     */
+    public RelativeTime add(final RelativeTime other) {
+        if (this.rel_value_us == Long.MAX_VALUE
+                || other.rel_value_us == Long.MAX_VALUE) {
+            return RelativeTime.FOREVER;
+        }
+        final long new_rel_value = this.rel_value_us + other.rel_value_us;
+        // check for numeric overflow
+        if (new_rel_value < this.rel_value_us) {
+            logger.warn("time overflow");
+            return RelativeTime.FOREVER;
+        }
+        return new RelativeTime(new_rel_value);
+    }
+
+    /**
+     * Divide relative time by a given factor.
+     *
+     * @param factor
+     *            integer to divide by
+     * @return FOREVER if this=FOREVER or factor=0; otherwise this/factor
+     */
+    public RelativeTime divide(final int factor) {
+        if (factor == 0 || this.rel_value_us == Long.MAX_VALUE) {
+            return RelativeTime.FOREVER;
+        }
+        return new RelativeTime(this.rel_value_us / factor);
+    }
+
+    /**
+     * Returns the amount of time in milliseconds.
+     * 
+     * @return the amount of time in milliseconds
+     */
+    public long getMicroseconds() {
+        return rel_value_us;
+    }
+
+    /**
+     * Return the maximum of two relative time values.
+     * 
+     * @return max(t1, t2)
+     */
+    public static RelativeTime max(RelativeTime t1, RelativeTime t2) {
+        return t1.rel_value_us >= t2.rel_value_us ? t1 : t2;
+    }
+
+    /**
+     * Return the minimum of two relative time values.
+     * 
+     * @return min(this, other)
+     */
+    public static RelativeTime min(RelativeTime t1, RelativeTime t2) {
+        return t1.rel_value_us <= t2.rel_value_us ? t1 : t2;
+    }
+
+    /**
+     * Multiply relative time by a given factor.
+     * 
+     * @return FOREVER if this=FOREVER or on overflow; otherwise this*factor
+     */
+    public RelativeTime multiply(final int factor) {
+        if (factor == 0) {
+            return RelativeTime.ZERO;
+        }
+        final long ret = this.rel_value_us * factor;
+        // check for numeric overflow
+        if (ret / factor != rel_value_us) {
+            logger.warn("time overflow");
+            return RelativeTime.FOREVER;
+        }
+        return new RelativeTime(ret);
+    }
+
+    /**
+     * Subtract relative timestamp from the other.
+     * 
+     * @param other
+     *            second timestamp
+     * @return ZERO if other>=this (including both FOREVER), FOREVER if
+     *         this=FOREVER, this-other otherwise
+     */
+    public RelativeTime subtract(final RelativeTime other) {
+        if (this.rel_value_us >= other.rel_value_us) {
+            return RelativeTime.ZERO;
+        } else if (this.rel_value_us == Long.MAX_VALUE) {
+            return this;
+        } else {
+            return new RelativeTime(this.rel_value_us - other.rel_value_us);
+        }
+    }
+
+    /**
+     * Converts relative time to an absolute time in the future.
+     * 
+     * @return timestamp that is in the future, or FOREVER if this=FOREVER (or
+     *         if we would overflow)
+     */
+    public AbsoluteTime toAbsolute() {
+        return AbsoluteTime.now().add(this);
+    }
+
+    public boolean isForever() {
+        return rel_value_us == FOREVER.rel_value_us;
+    }
+
+    public boolean equals(Object o) {
+        return (o instanceof RelativeTime) && ((RelativeTime) o).rel_value_us 
== rel_value_us;
+    }
+
+    @Override
+    public int hashCode() {
+        return (int) this.rel_value_us;
+    }
+
+    @Override
+    public int compareTo(RelativeTime other) {
+        if (this.rel_value_us < other.rel_value_us) {
+            return -1;
+        }
+        if (this.rel_value_us > other.rel_value_us) {
+            return 1;
+        }
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        if (this.isForever()) {
+            return "RelativeTime(FOREVER)";
+        }
+        return "RelativeTime("+this.rel_value_us +")";
+    }
+
+
+
+
+    public RelativeTimeMessage toNetwork() {
+        long rval = this.rel_value_us;
+        assert rval >= 0;
+        if (rval == FOREVER.rel_value_us) {
+            rval = -1L; /* 0xFFFFFFFFFFFFFFFF for network format! */
+        }
+        return new RelativeTimeMessage(rval);
+    }
+
+    public static RelativeTime fromNetwork(RelativeTimeMessage m) {
+        if (m.value__ < 0) {
+            return RelativeTime.FOREVER;
+        } else {
+            return new RelativeTime(m.value__);
+        }
+    }
+
+}

Added: gnunet-java/src/main/java/org/gnunet/util/RelativeTimeMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/RelativeTimeMessage.java          
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/RelativeTimeMessage.java  
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,55 @@
+/*
+ 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.Message;
+import org.gnunet.construct.UInt64;
+
+
+/**
+ * Representation of a RelativeTime object, to be sent over the network.
+ */
+public class RelativeTimeMessage implements Message {
+
+    /**
+     * Value__ still in Java-byte order, needs to be converted to Network byte
+     * order by the Construct class.
+     */
+    @UInt64
+    public long value__;
+
+    public RelativeTimeMessage(final long value) {
+        this.value__ = value;
+    }
+
+    public RelativeTimeMessage() {
+        // default constructor needed for Construct
+    }
+
+    public RelativeTimeMessage(final RelativeTime t) {
+        if (t.equals(RelativeTime.FOREVER)) {
+            this.value__ = -1;
+        } else {
+            this.value__ = t.getMicroseconds();
+        }
+    }
+
+}

Added: gnunet-java/src/main/java/org/gnunet/util/Resolver.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/Resolver.java                     
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/Resolver.java     2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,421 @@
+/*
+ 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 com.google.common.net.InetAddresses;
+import org.gnunet.construct.*;
+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.InetAddress;
+import java.net.UnknownHostException;
+import java.util.LinkedList;
+
+/**
+ * Resolve hostnames asynchronously, using the gnunet resolver service if 
necessary.
+ * <p/>
+ * TODO: implement reverse lookup (already done in the C-API)
+ */
+public class Resolver {
+    private static final Logger logger = LoggerFactory
+            .getLogger(Resolver.class);
+
+    private static Resolver singletonInstance;
+
+    private Configuration cfg;
+
+    private Client client;
+
+    public static InetAddress getInetAddressFromString(String ipString) {
+        try {
+            return InetAddresses.forString(ipString);
+        } catch (IllegalArgumentException e) {
+            return null;
+        }
+    }
+
+    @UnionCase(4)
+    public static class GetMessage implements GnunetMessage.Body {
+        static final int DIRECTION_GET_IP = 0;
+        static final int DIRECTION_GET_NAME = 1;
+        static final int AF_UNSPEC = 0;
+        static final int AF_INET = 2;
+        static final int AF_INET6 = 10;
+
+        @UInt32
+        public int direction;
+        @UInt32
+        public int domain;
+
+        @Union(tag = "direction", optional = true)
+        public Address addr;
+    }
+
+    public interface Address extends MessageUnion {
+    }
+
+    @UnionCase(GetMessage.DIRECTION_GET_IP)
+    public static class TextualAddress implements Address {
+        @ZeroTerminatedString
+        public String addr;
+    }
+
+    @UnionCase(GetMessage.DIRECTION_GET_NAME)
+    public static class NumericAddress implements Address {
+        @FillWith @UInt8
+        public byte[] addr;
+    }
+
+
+    @UnionCase(5)
+    public static class ResolverResponse implements GnunetMessage.Body {
+        @NestedMessage(optional = true)
+        public ResponseBody responseBody;
+    }
+
+
+    public static class ResponseBody implements Message {
+        @FillWith @UInt8
+        public byte[] addr;
+    }
+
+    /**
+     * Callback object for hostname resolution.
+     */
+    public interface AddressCallback {
+        /**
+         * Called for every address the requested hostname resolves to.
+         *
+         * @param addr address for the resolved name
+         */
+        public void onAddress(InetAddress addr);
+
+        /**
+         * Called after every result (if any) has been passed to onAddress.
+         */
+        public void onFinished();
+
+        /**
+         * Called when the resolve operation times out before returning every 
result.
+         */
+        void onTimeout();
+    }
+
+
+    /**
+     * Configuration to use with the Resolver.
+     * <p/>
+     * Usually called by the entry points Program/Service.
+     *
+     * @param cfg configuration to use
+     */
+    public void setConfiguration(Configuration cfg) {
+        this.cfg = cfg;
+    }
+
+    private void lazyConnect() {
+        if (client == null) {
+            if (cfg == null) {
+                throw new AssertionError("Resolver has no Configuration");
+            }
+            client = new Client("resolver", cfg);
+        }
+    }
+
+
+    private InetAddress getInet4Localhost() {
+        try {
+            return InetAddress.getByAddress(new byte[]{127, 0, 0, 1});
+        } catch (UnknownHostException e) {
+            throw new RuntimeException();
+        }
+    }
+
+    private InetAddress getInet6Localhost() {
+        try {
+            return InetAddress.getByAddress(new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 1});
+        } catch (UnknownHostException e) {
+            throw new RuntimeException();
+        }
+    }
+
+    public class ResolveHandle implements Cancelable {
+        private String hostname;
+        private AbsoluteTime deadline;
+        private AddressCallback cb;
+        private boolean finished = false;
+        private boolean canceled = false;
+        private Cancelable transmitTask = null;
+        private Cancelable receiveTask = null;
+
+        public void cancel() {
+            if (finished) {
+                throw new AssertionError("Resolve already finished");
+            }
+            if (canceled) {
+                throw new AssertionError("ResolveHandle canceled twice");
+            }
+            if (queuedRequests.contains(this)) {
+                queuedRequests.remove(this);
+            } else {
+                if (receiveTask != null) {
+                    receiveTask.cancel();
+                }
+                if (transmitTask != null) {
+                    transmitTask.cancel();
+                }
+            }
+            canceled = true;
+        }
+    }
+
+    private LinkedList<ResolveHandle> queuedRequests = new 
LinkedList<ResolveHandle>();
+
+    private boolean resolveActive = false;
+
+    /**
+     * Resolve the hostname 'hostname'.
+     *
+     * @param hostname hostname to resolve
+     * @param timeout  timeout, calls cb.onTimeout on expiratoin
+     * @param cb       callback
+     * @return a handle to cancel the request, null if request could be 
satisfied immediately
+     */
+    public Cancelable resolveHostname(String hostname, RelativeTime timeout, 
final AddressCallback cb) {
+        // try if hostname is numeric IP or loopback
+        if (hostname.equalsIgnoreCase("localhost")) {
+            logger.debug("resolving address locally");
+            cb.onAddress(getInet6Localhost());
+            cb.onAddress(getInet4Localhost());
+            cb.onFinished();
+            return null;
+        }
+        if (hostname.equalsIgnoreCase("ip6-localhost")) {
+            cb.onAddress(getInet6Localhost());
+            cb.onFinished();
+            return null;
+        }
+        InetAddress inetAddr = getInetAddressFromString(hostname);
+
+        if (inetAddr != null) {
+            cb.onAddress(inetAddr);
+            cb.onFinished();
+            return null;
+        }
+
+        final ResolveHandle rh = new ResolveHandle();
+        rh.hostname = hostname;
+        rh.deadline = timeout.toAbsolute();
+        rh.cb = cb;
+
+        queuedRequests.addLast(rh);
+        handleNextRequest();
+        return rh;
+    }
+
+    private void handleNextRequest() {
+        if (!resolveActive && !queuedRequests.isEmpty()) {
+            ResolveHandle rh = queuedRequests.pollFirst();
+            handleRequest(rh);
+        }
+    }
+
+    private void handleRequest(final ResolveHandle rh) {
+        if (resolveActive) {
+            throw new AssertionError("resolveActive but new resolve started");
+        }
+
+        resolveActive = true;
+
+        lazyConnect();
+
+        final GetMessage req = new GetMessage();
+        req.direction = GetMessage.DIRECTION_GET_IP;
+        req.domain = GetMessage.AF_UNSPEC;
+
+        TextualAddress textAddr = new TextualAddress();
+        textAddr.addr = rh.hostname;
+
+        req.addr = textAddr;
+
+        final AbsoluteTime deadline = rh.deadline;
+
+        logger.debug("deadline is " + deadline + " | now is " + 
AbsoluteTime.now());
+
+        logger.debug("remaining is " + deadline.getRemaining());
+
+        rh.transmitTask = client.notifyTransmitReady(
+                deadline.getRemaining(), true,
+                0, new MessageTransmitter() {
+            @Override
+            public void transmit(Connection.MessageSink sink) {
+                if (sink == null) {
+                    onTimeout(rh);
+                    return;
+                }
+                sink.send(req);
+                rh.transmitTask = null;
+
+                logger.debug("recv in notifyTransmitReady cb");
+                rh.receiveTask = client.receiveOne(deadline.getRemaining(), 
new MessageReceiver() {
+                    @Override
+                    public void process(GnunetMessage.Body msg) {
+                        rh.receiveTask = null;
+                        ResolverResponse gmsg = (ResolverResponse) msg;
+                        if (gmsg.responseBody != null) {
+                            try {
+                                InetAddress in_addr;
+                                int len = gmsg.responseBody.addr.length;
+                                if (len == 4 || len == 16) {
+                                    in_addr = 
InetAddress.getByAddress(gmsg.responseBody.addr);
+                                } else {
+                                    throw new 
ProtocolViolationException("malformed address message");
+                                }
+
+                                rh.cb.onAddress(in_addr);
+                                rh.receiveTask = 
client.receiveOne(deadline.getRemaining(), this);
+                            } catch (UnknownHostException e) {
+                                throw new 
ProtocolViolationException("malformed address");
+                            }
+                        } else {
+                            resolveActive = false;
+                            rh.cb.onFinished();
+                            handleNextRequest();
+                        }
+                    }
+
+                    @Override
+                    public void handleError() {
+                        onTimeout(rh);
+                    }
+                });
+
+            }
+
+            @Override
+            public void handleError() {
+                rh.cb.onTimeout();
+            }
+        });
+    }
+
+
+    private void onTimeout(ResolveHandle h) {
+        resolveActive = false;
+        h.cb.onTimeout();
+        handleNextRequest();
+    }
+
+
+    public static Resolver getInstance() {
+        if (singletonInstance == null) {
+            singletonInstance = new Resolver();
+        }
+        return singletonInstance;
+    }
+
+
+    /**
+     * Return a textual representation of an InetAddress. Shortens IPv6 
addresses.
+     *
+     * @param addr the address to convert
+     * @return textual representation of the address
+     */
+    public static String ipToString(InetAddress addr) {
+        byte[] a = addr.getAddress();
+        if (a.length == 4) {
+            return addr.getHostAddress();
+        } else if (a.length == 16) {
+            String s = addr.getHostAddress();
+            // replace the first group of zeroes (not the longest) with ::
+            return s.replaceFirst("[:]?0[:](0[:])+0?", "::");
+        } else {
+            throw new RuntimeException("unknown InetAddress format");
+        }
+    }
+
+
+    public static void main(final String[] argv) {
+        new Program(argv) {
+            @Argument(shortname = "r", longname = "reverse",
+                    description = "do reverse dns lookup",
+                    action = ArgumentAction.SET)
+            boolean isReverse;
+
+            @Override
+            public void run() {
+                if (isReverse) {
+                    System.out.println("reverse lookup not supported");
+                } else {
+                    resolve();
+                }
+            }
+
+            public void resolve() {
+                final RelativeTime timeout = RelativeTime.SECOND;
+
+                if (unprocessedArgs.length == 0) {
+                    logger.warn("no hostname(s) given");
+                } else {
+                    logger.info("resolving hostname '" + unprocessedArgs[0] + 
"'");
+                    Resolver.getInstance().resolveHostname(unprocessedArgs[0], 
timeout, new AddressCallback() {
+                        int next = 1;
+
+                        @Override
+                        public void onAddress(InetAddress addr) {
+                            System.out.println(ipToString(addr));
+                        }
+
+                        @Override
+                        public void onFinished() {
+                            logger.info("resolve finished");
+                            next();
+                        }
+
+                        @Override
+                        public void onTimeout() {
+                            logger.warn("resolve timed out");
+                            next();
+
+                        }
+
+                        public void next() {
+                            if (unprocessedArgs.length > next) {
+                                logger.info("resolving hostname '" + 
unprocessedArgs[next] + "'");
+                                
Resolver.getInstance().resolveHostname(unprocessedArgs[next], timeout, this);
+                                next++;
+                            }
+                        }
+                    });
+                }
+
+            }
+
+            @Override
+            protected String makeHelpText() {
+                return "tool for forward and reverse DNS lookup";
+            }
+        }.start();
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/util/RunaboutMessageReceiver.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/RunaboutMessageReceiver.java      
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/RunaboutMessageReceiver.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,33 @@
+/*
+ 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.grothoff.Runabout;
+
+/**
+ * An abstract base class for message receivers that want to use the runabout, 
dispatches
+ * messages to the appropriate visit method.
+ */
+public abstract class RunaboutMessageReceiver extends Runabout implements 
MessageReceiver  {
+    public void process(GnunetMessage.Body msg) {
+        this.visitAppropriate(msg);
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/util/RunaboutUtil.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/RunaboutUtil.java                 
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/RunaboutUtil.java 2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,55 @@
+/*
+ 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.MessageLoader;
+import org.grothoff.Runabout;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+
+
+/**
+ * Utility methods for the runabout.
+ */
+public class RunaboutUtil {
+    public static ArrayList<Class> getRunaboutVisitees(Runabout r) {
+        Class rc = r.getClass();
+        ArrayList<Class> ret = new ArrayList<Class>(5);
+        for (Method m : rc.getMethods()) {
+            if (!(m.getName().equals("visit") && m.getParameterTypes().length 
== 1)) {
+                continue;
+            }
+            ret.add(m.getParameterTypes()[0]);
+        }
+        return ret;
+    }
+
+    @SuppressWarnings("unchecked")
+    public static int[] getRunaboutMessageTypes(Runabout r) {
+        ArrayList<Class> visitees = getRunaboutVisitees(r);
+        int[] msgtypes = new int[visitees.size()];
+        for (int i = 0; i < visitees.size(); ++i) {
+            msgtypes[i] = MessageLoader.getUnionTag(GnunetMessage.Body.class, 
visitees.get(i));
+        }
+        return msgtypes;
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/util/Scheduler.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/Scheduler.java                    
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/Scheduler.java    2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,678 @@
+/*
+     This file is part of GNUnet.
+     (C) 2009 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 2, 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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.nio.ByteBuffer;
+import java.nio.channels.*;
+import java.nio.channels.spi.SelectorProvider;
+import java.util.*;
+
+/**
+ * Schedule computations using continuation passing style.
+ *
+ * @author Florian Dold
+ */
+public class Scheduler {
+    private static final Logger logger = LoggerFactory
+            .getLogger(Scheduler.class);
+
+    // only valid while a task is executing
+    private static TaskConfiguration activeTask = null;
+
+    // cumulative number of tasks in the ready lists
+    private static volatile int readyCount = 0;
+
+    // for every priority, there is a list of tasks that is definitely ready 
to run
+    @SuppressWarnings("unchecked")
+    final private static LinkedList<TaskConfiguration>[] readyLists = new 
LinkedList[Priority.numberOfPriorities];
+
+    static {
+        for (int i = 0; i < Priority.numberOfPriorities; ++i) {
+            readyLists[i] = new LinkedList<TaskConfiguration>();
+        }
+    }
+
+    private static final int EVENT_READ = 0, EVENT_WRITE = 1, EVENT_ACCEPT = 
2, EVENT_CONNECT = 3;
+    private static final int[] eventToInterestOp = new 
int[]{SelectionKey.OP_READ, SelectionKey.OP_WRITE,
+            SelectionKey.OP_ACCEPT, SelectionKey.OP_CONNECT};
+    private static final Reason[] eventToReason = new 
Reason[]{Reason.READ_READY, Reason.WRITE_READY,
+            Reason.ACCEPT_READY, Reason.CONNECT_READY};
+
+
+    /**
+     * Selector, used to check file descriptors for readiness.
+     */
+    private static Selector selector = null;
+
+    static {
+        try {
+            selector = SelectorProvider.provider().openSelector();
+        } catch (final IOException e) {
+            // what to do here?
+            logger.error("fatal: cannot create selector");
+            System.exit(-1);
+        }
+    }
+
+    /**
+     * true iff the scheduler is currently running.
+     */
+    private static boolean scheduler_running = false;
+
+
+    // tasks that are waiting for an event, which are executed anyway after 
the deadline has occurred
+    final private static Queue<TaskConfiguration> pending = new 
PriorityQueue<TaskConfiguration>(5, new Comparator
+            <TaskConfiguration>() {
+        @Override
+        public int compare(TaskConfiguration a, TaskConfiguration b) {
+            return a.deadline.compareTo(b.deadline);
+        }
+    });
+
+
+    /**
+     * Reset the scheduler forcefully.
+     * Intended to be used internally in the Scheduler, as well as in test 
teardown.
+     */
+    public static void forceReset() {
+        scheduler_running = false;
+        readyCount = 0;
+        activeTask = null;
+        for (int i = 0; i < Priority.numberOfPriorities; ++i) {
+            readyLists[i] = new LinkedList<TaskConfiguration>();
+        }
+        pending.clear();
+    }
+
+
+    /**
+     * Priority for Tasks.
+     */
+    public enum Priority {
+        IDLE, BACKGROUND, DEFAULT, HIGH, UI, URGENT, SHUTDOWN;
+
+        // how many different priorities do we have?
+        private static final int numberOfPriorities = Priority.values().length;
+    }
+
+    /**
+     * Reasons for executing a task.
+     */
+    public enum Reason {
+        STARTUP, SHUTDOWN, TIMEOUT, READ_READY, WRITE_READY, ACCEPT_READY, 
CONNECT_READY
+    }
+
+    /**
+     * The context of a task that is ready to run.
+     */
+    public static class RunContext {
+        /**
+         * The reason this task has been called by the scheduler.
+         */
+        Set<Reason> reasons = EnumSet.noneOf(Reason.class);
+
+        public RunContext() {
+        }
+    }
+
+    /**
+     * A task is the basic unit of work that is managed by the scheduler.
+     */
+    public static interface Task {
+        public void run(RunContext ctx);
+    }
+
+    /**
+     * A TaskConfiguration represents a Task that will execute or has already 
been executed.
+     */
+    public static class TaskConfiguration implements Cancelable {
+        private final Task task;
+        private RunContext ctx = new RunContext();
+        private boolean lifeness = true;
+        private Priority priority;
+        private final AbsoluteTime deadline;
+
+        private ArrayList<SelectableChannel> eventChannels = null;
+        private ArrayList<Integer> eventTypes = null;
+
+        private boolean hasRun = false;
+        private boolean isCanceled = false;
+
+        /**
+         * Create a TaskIdentifier.
+         *
+         * @param delay when will the task be run?
+         *              may be null to indicate that this task may not be run
+         *              (but only queued directly)
+         * @param task task to run with this TaskIdentifier
+         */
+        TaskConfiguration(RelativeTime delay, Task task) {
+            this.task = task;
+            if (delay == null)
+                this.deadline = null;
+            else
+                this.deadline = delay.toAbsolute();
+        }
+
+        private void addChannelEvent(SelectableChannel channel, int eventType) 
{
+            if (channel == null) {
+                throw new AssertionError("channel must be non-null");
+            }
+            if (eventChannels == null) {
+                eventChannels = new ArrayList<SelectableChannel>();
+                eventTypes = new ArrayList<Integer>();
+            }
+            eventChannels.add(channel);
+            eventTypes.add(eventType);
+
+            int interestOp = eventToInterestOp[eventType];
+
+            SelectionKey key = channel.keyFor(selector);
+            if (key == null || !key.isValid()) {
+                try {
+                    key = channel.register(selector, interestOp, new 
TaskConfiguration[4]);
+                } catch (ClosedChannelException e) {
+                    throw new IOError(e);
+                }
+            } else {
+                if ((key.interestOps() & interestOp) != 0) {
+                    throw new AssertionError("interest op registered twice");
+                }
+                key.interestOps(key.interestOps() | interestOp);
+            }
+
+            TaskConfiguration[] subscribers = (TaskConfiguration[]) 
key.attachment();
+            if (subscribers[eventType] != null) {
+                throw new AssertionError("subscriber registered twice");
+            }
+            subscribers[eventType] = this;
+
+            if (subscribers[EVENT_CONNECT] != null && subscribers[EVENT_READ] 
!= null) {
+                throw new AssertionError("OP_CONNECT and OP_READ are 
incompatible in java");
+            }
+        }
+
+        private void run() {
+            if (hasRun) {
+                throw new AssertionError("same task ran twice");
+            }
+            if (isCanceled) {
+                return;
+            }
+            TaskConfiguration old = activeTask;
+            activeTask = this;
+            task.run(ctx);
+            hasRun = true;
+            activeTask = old;
+        }
+
+        public void cancel() {
+            if (isCanceled) {
+                throw new AssertionError("task canceled twice");
+            }
+            isCanceled = true;
+            pending.remove(this);
+        }
+
+        public Cancelable schedule() {
+            if (this.deadline == null)
+                throw new AssertionError("a task without deadline may not be 
scheduled");
+            if (priority == null) {
+                if (activeTask != null) {
+                    priority = activeTask.priority;
+                } else {
+                    priority = Priority.DEFAULT;
+                }
+            }
+            pending.add(this);
+            return this;
+        }
+
+        private void deregister() {
+            if (eventChannels == null) {
+                return;
+            }
+            for (int i = 0; i < eventChannels.size(); ++i) {
+                SelectionKey key = eventChannels.get(i).keyFor(selector);
+                TaskConfiguration[] subscribers = (TaskConfiguration[]) 
key.attachment();
+                int interestOp = eventToInterestOp[eventTypes.get(i)];
+                if (subscribers[eventTypes.get(i)] == null || 
(key.interestOps() | interestOp) == 0) {
+                    throw new AssertionError("deregistering event that has not 
been registered");
+                }
+                subscribers[eventTypes.get(i)] = null;
+                key.interestOps(key.interestOps() & (~interestOp));
+            }
+        }
+
+        public void selectRead(SelectableChannel channel) {
+            addChannelEvent(channel, EVENT_READ);
+        }
+
+        public void selectWrite(SelectableChannel channel) {
+            addChannelEvent(channel, EVENT_WRITE);
+        }
+
+        public void selectConnect(SelectableChannel channel) {
+            addChannelEvent(channel, EVENT_CONNECT);
+        }
+
+        public void selectAccept(SelectableChannel channel) {
+            addChannelEvent(channel, EVENT_ACCEPT);
+        }
+    }
+
+    /**
+     * Run the task regardless of any prerequisites, before any other task of
+     * the same priority.
+     */
+    public static synchronized void addContinuation(Task task, EnumSet<Reason> 
reasons) {
+        TaskConfiguration t = new TaskConfiguration(null, task);
+        t.ctx.reasons = reasons;
+        t.priority = Priority.DEFAULT;
+        queueReady(t);
+    }
+
+    /**
+     * Schedule a new task to be run as soon as possible. The task will be run
+     * with the priority of the calling task.
+     *
+     * @param task main function of the task
+     * @return unique task identifier for the job only valid until "task" is
+     *         started!
+     */
+    public static Cancelable add(Task task) {
+        return addDelayed(RelativeTime.ZERO, task);
+    }
+
+    /**
+     * Add a task to run after the specified delay.
+     *
+     * @param delay time to wait until running the task
+     * @param task  the task to run after delay
+     * @return the TaskIdentifier, can be used to cancel the task until it has 
been executed.
+     */
+    public static TaskConfiguration addDelayed(RelativeTime delay, Task task) {
+        TaskConfiguration tid = new TaskConfiguration(delay, task);
+        tid.schedule();
+        return tid;
+    }
+
+    public static TaskConfiguration addRead(RelativeTime timeout,
+                                            SelectableChannel chan, Task task) 
{
+        TaskConfiguration tid = new TaskConfiguration(timeout, task);
+        tid.addChannelEvent(chan, EVENT_READ);
+        tid.schedule();
+        return tid;
+    }
+
+    public static TaskConfiguration addWrite(RelativeTime timeout,
+                                             SelectableChannel chan, Task 
task) {
+        TaskConfiguration tid = new TaskConfiguration(timeout, task);
+        tid.addChannelEvent(chan, EVENT_WRITE);
+        tid.schedule();
+        return tid;
+    }
+
+    /**
+     * Check if the system is still life. Trigger disconnect if we have tasks, 
but
+     * none of them give us lifeness.
+     *
+     * @return true to continue the main loop, false to exit
+     */
+    private static boolean checkLiveness() {
+        if (readyCount > 0) {
+            return true;
+        }
+        for (TaskConfiguration t : pending) {
+            if (t.lifeness) {
+                return true;
+            }
+        }
+        // trigger shutdown if we still have pending tasks, but none of them 
has lifeness
+        if (!pending.isEmpty()) {
+            logger.debug("tasks pending but not alive -- disconnect");
+            shutdown();
+            return true;
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Queue a Task for execution.
+     *
+     * @param tid TaskIdentifier of the ready task
+     */
+    private static synchronized void queueReady(TaskConfiguration tid) {
+        int idx = tid.priority.ordinal();
+        readyLists[idx].add(tid);
+        readyCount++;
+        pending.remove(tid);
+    }
+
+
+    /**
+     * Queue all tasks with expired timeout.
+     *
+     * @return the minimum time to wait until the next timeout expiry
+     */
+    private static RelativeTime handleTimeouts() {
+        RelativeTime timeout = RelativeTime.FOREVER;
+
+        // check if any timeouts occurred
+        while (true) {
+            TaskConfiguration t = pending.peek();
+            if (t == null) {
+                break;
+            }
+            RelativeTime remaining = t.deadline.getRemaining();
+            if (remaining.getMicroseconds() <= 0) {
+                t.deregister();
+                t.ctx.reasons = EnumSet.of(Reason.TIMEOUT);
+                queueReady(t);
+            } else {
+                timeout = remaining;
+                break;
+            }
+        }
+        return timeout;
+    }
+
+    private static void addSubscriberTask(Collection<TaskConfiguration> 
executableTasks,
+                                          TaskConfiguration[] subscribers, int 
eventType) {
+        if (subscribers[eventType] == null) {
+            return;
+        }
+        executableTasks.add(subscribers[eventType]);
+        subscribers[eventType].ctx.reasons.add(eventToReason[eventType]);
+    }
+
+    /**
+     * Select on channels and queue tasks that become executable.
+     *
+     * @param timeout timeout for select
+     */
+    private static void handleSelect(RelativeTime timeout) {
+        long timeout_ms = timeout.getMicroseconds() / 1000;
+        try {
+            // selector.select(0) would block indefinitely (counter-intuitive, 
java's fault)
+            if (timeout_ms == 0) {
+                selector.selectNow();
+            } else if (timeout.isForever()) {
+                selector.select(0);
+            } else {
+                selector.select(timeout_ms);
+            }
+        } catch (IOException e) {
+            throw new IOError(e);
+        }
+
+        // we have to do this so we don't execute any task twice
+        Collection<TaskConfiguration> executableTasks = new 
HashSet<TaskConfiguration>();
+        for (SelectionKey sk : selector.selectedKeys()) {
+            TaskConfiguration[] subscribers = (TaskConfiguration[]) 
sk.attachment();
+
+            if (sk.isReadable()) {
+                addSubscriberTask(executableTasks, subscribers, EVENT_READ);
+            }
+            if (sk.isWritable()) {
+                addSubscriberTask(executableTasks, subscribers, EVENT_WRITE);
+            }
+            if (sk.isAcceptable()) {
+                addSubscriberTask(executableTasks, subscribers, EVENT_ACCEPT);
+            }
+            if (sk.isConnectable()) {
+                addSubscriberTask(executableTasks, subscribers, EVENT_CONNECT);
+            }
+
+        }
+        for (TaskConfiguration tt : executableTasks) {
+            // cancel subscriptions to other events, we can execute now!
+            tt.deregister();
+            queueReady(tt);
+        }
+    }
+
+
+    /**
+     * Initialize and run scheduler. This function will return when all tasks
+     * have completed.
+     */
+    public static void run() {
+        run(null);
+    }
+
+    /**
+     * Initialize and run scheduler. This function will return when all tasks
+     * have completed.
+     *
+     * @param initialTask the initial task to run immediately
+     */
+    public static void run(Task initialTask) {
+        logger.info("running scheduler");
+        if (scheduler_running) {
+            throw new AssertionError("Scheduler already running");
+        }
+        scheduler_running = true;
+        try {
+            run_unchecked(initialTask);
+        } finally {
+            logger.info("cleaning up after scheduler ran");
+            // ensure that after run returns, the scheduler is in its initial 
state,
+            // even though there was an exception (e.g. after a test case that 
expects an exception)
+            forceReset();
+        }
+    }
+
+
+    /**
+     * Initialize and run scheduler. This function will return when all tasks
+     * have completed. Don't check if the scheduler is already running or not.
+     *
+     * @param initialTask the initial task to run immediately
+     */
+    private static void run_unchecked(Task initialTask) {
+        if (initialTask != null) {
+            addContinuation(initialTask, EnumSet.of(Reason.STARTUP));
+        }
+
+        // the gnunet main loop
+        while (true) {
+            synchronized (Scheduler.class) {
+                if (checkLiveness() == false)
+                    break;
+                RelativeTime nextTimeout = handleTimeouts();
+                if (nextTimeout.getMicroseconds() < 0) {
+                    logger.warn("negative timeout for select");
+                }
+
+                // don't select if there are no tasks; we are done!
+                if (readyCount == 0 && pending.isEmpty()) {
+                    return;
+                }
+
+                // don't block in select if we have tasks ready to run!
+                if (readyCount > 0) {
+                    handleSelect(RelativeTime.ZERO);
+                } else {
+                    handleSelect(nextTimeout);
+                }
+
+                runReady();
+            }
+        }
+
+        if (readyCount != 0) {
+            throw new AssertionError("tasks ready after scheduler ran 
(count)");
+        }
+
+        for (List readyList : Scheduler.readyLists) {
+            if (!readyList.isEmpty()) {
+                throw new AssertionError("tasks ready after scheduler ran 
(list)");
+            }
+        }
+
+        if (pending.size() != 0) {
+            throw new AssertionError("pending tasks after scheduler ran");
+        }
+
+        if (activeTask != null) {
+            throw new AssertionError("active task after scheduler ran");
+        }
+    }
+
+
+    /**
+     * Execute tasks until there either
+     * <ul>
+     * <li>there are no ready tasks</li>
+     * <li>there is a pending task (which may be of higher priority)</li>
+     * </ul>
+     */
+    private static void runReady() {
+        do {
+            if (readyCount == 0) {
+                return;
+            }
+            // start executing from the highest priority down to 0
+            for (int p = Priority.numberOfPriorities - 1; p >= 0; p--) {
+                // execute all tasks with priority p
+                LinkedList<TaskConfiguration> queue = readyLists[p];
+                while (!queue.isEmpty()) {
+                    TaskConfiguration tid = queue.removeFirst();
+                    readyCount--;
+                    tid.run();
+                }
+            }
+        } while (pending.size() == 0);
+
+    }
+
+    /**
+     * Request the shutdown of the scheduler. Marks all currently pending 
tasks as
+     * ready because of disconnect. This will cause all tasks to run (as soon 
as
+     * possible, respecting priorities and prerequisite tasks). Note that tasks
+     * scheduled AFTER this call may still be delayed arbitrarily.
+     */
+    public static void shutdown() {
+        // queueReady() while iterating would yield concurrent modification 
exn otherwise
+        for (TaskConfiguration tid : new 
ArrayList<TaskConfiguration>(pending)) {
+            tid.ctx.reasons.add(Reason.SHUTDOWN);
+            queueReady(tid);
+        }
+        pending.clear();
+    }
+
+
+    /**
+     * A handle to a file system object that can be selected on.
+     */
+    public static class FilePipe {
+        private FilePipeThread filePipeThread;
+
+        private FilePipe(FilePipeThread filePipeThread) {
+            this.filePipeThread = filePipeThread;
+        }
+
+        public Pipe.SourceChannel getSource() {
+            return filePipeThread.pipe.source();
+        }
+
+    }
+
+    /**
+     * A thread that reads from a file pipe.
+     */
+    private static class FilePipeThread extends Thread {
+        public File file;
+        public Pipe pipe;
+
+        FilePipeThread(File file) {
+            this.file = file;
+            try {
+                pipe = SelectorProvider.provider().openPipe();
+                pipe.source().configureBlocking(false);
+                pipe.sink().configureBlocking(false);
+            } catch (IOException e) {
+                throw new RuntimeException("selector provider has no pipes");
+            }
+        }
+
+        @Override
+        public void run() {
+            // has to be done in thread, blocks if file is a fifo
+            FileChannel fileChannel;
+
+            try {
+                FileInputStream stream;
+                stream = new FileInputStream(file);
+                fileChannel = stream.getChannel();
+            } catch (FileNotFoundException e) {
+                throw new IOError(e);
+            }
+
+            // we have such a small buffer so that the pipe will not buffer
+            ByteBuffer buffer = ByteBuffer.allocate(1);
+
+            boolean quit = false;
+
+            while (!quit) {
+                try {
+                    buffer.clear();
+                    fileChannel.read(buffer);
+                    buffer.flip();
+                    pipe.sink().write(buffer);
+                } catch (IOException e) {
+                    quit = true;
+                    try {
+                        fileChannel.close();
+                    } catch (IOException ex) {
+                        // nothing we can do here
+                    }
+                    try {
+                        pipe.sink().close();
+                    } catch (IOException ex) {
+                        // nothing we can do here
+                    }
+                    try {
+                        pipe.source().close();
+                    } catch (IOException ex) {
+                        // nothing we can do here
+                    }
+                }
+            }
+
+        }
+    }
+
+    public static FilePipe openFilePipe(File file) {
+        FilePipeThread fpt = new FilePipeThread(file);
+        fpt.setDaemon(true);
+        fpt.start();
+        return new FilePipe(fpt);
+    }
+}
+

Added: gnunet-java/src/main/java/org/gnunet/util/Server.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/Server.java                       
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/Server.java       2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,509 @@
+/*
+ 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.Construct;
+import org.grothoff.Runabout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+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;
+
+    /**
+     * The sockets this server accepts new connections on.
+     */
+    private List<ServerSocketChannel> listenSockets = new 
ArrayList<ServerSocketChannel>();
+
+    /**
+     * The list of all clients connected to this server.
+     */
+    private List<ClientHandle> clientHandles = new LinkedList<ClientHandle>();
+
+    /**
+     * 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<DisconnectHandler> disconnectHandlers = new 
LinkedList<DisconnectHandler>();
+
+    /**
+     * 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<Class> 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.
+     * <p/>
+     * 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 {
+        /**
+         * The underlying connection to the client-
+         */
+        private Connection connection;
+
+        /**
+         * When referenceCount==0, the server is allowed to drop the client 
after a timeout.
+         */
+        private int referenceCount = 0;
+
+        /**
+         * 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;
+
+        /**
+         * 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);
+            // start receiving
+            receiveDone(true);
+        }
+
+        /**
+         * Notify us when the server has enough space to transmit
+         * a message of the given size to the given client.
+         *
+         * @param size        requested amount of buffer space
+         * @param timeout     after how long should we give up (and call
+         *                    notify with buf NULL and size 0)?
+         * @param transmitter callback
+         * @return a handle to cancel the notification
+         */
+        public Cancelable notifyTransmitReady(int size, RelativeTime timeout, 
MessageTransmitter 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
+         * message handler (or its respective continuations).
+         * <p/>
+         * The server does not automatically continue to receive messages to
+         * support flow control.
+         *
+         * @param stayConnected false if connection to the client should be 
closed
+         */
+        public void receiveDone(boolean stayConnected) {
+            if (currentReceive != null) {
+                throw new AssertionError("receiveDone() called, but still 
waiting for message");
+            }
+            if (stayConnected) {
+                currentReceive = connection.receive(RelativeTime.FOREVER, new 
MessageReceiver() {
+                    @Override
+                    public void process(GnunetMessage.Body msg) {
+                        currentReceive = null;
+                        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) {
+                            throw new AssertionError("received message, but no 
handler installed");
+                        }
+                        receivedMessageHandler.setSender(ClientHandle.this);
+                        receivedMessageHandler.visitAppropriate(msg);
+                    }
+
+                    @Override
+                    public void handleError() {
+                        logger.warn("error receiving from client");
+                        disconnect();
+                    }
+                });
+            } else {
+                if (referenceCount > 0) {
+                    this.disconnectRequested = true;
+                } else {
+                    System.out.println("disconnecting " + this.isMonitor);
+                    disconnect();
+                }
+            }
+
+        }
+
+        /**
+         * Ask the server to disconnect from the given client.
+         * <p/>
+         * The client will be disconnected from the server, no matter what the 
current reference count is.
+         */
+        public void disconnect() {
+            connection.disconnect();
+            // 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();
+        }
+
+        /**
+         * Prevent the client from being disconnected.
+         * For every keep, there should be an additional matching drop.
+         */
+        public void keep() {
+            referenceCount++;
+        }
+
+
+        /**
+         * Allow to disconnect this client, if not prevented by previous calls 
to keep.
+         * <p/>
+         * 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 && 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;
+        }
+    }
+
+
+    /**
+     * All handlers for receiving messages from clients have to inherit this 
class.
+     * <p/>
+     * 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.
+         * <p/>
+         * The return value of getSender() is only valid while executing a 
visit method.
+         *
+         * @return handle of the client whose message is currently being 
visited
+         */
+        public final ClientHandle getSender() {
+            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;
+        }
+    }
+
+    /**
+     * Create a server listening on all specified addresses.
+     *
+     * @param addresses    addresses to bind on
+     * @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(List<SocketAddress> addresses, RelativeTime idleTimeout, 
boolean requireFound) {
+        this.idleTimeout = idleTimeout;
+        this.requireFound = requireFound;
+        try {
+            for (SocketAddress addr : addresses) {
+                ServerSocketChannel socket = ServerSocketChannel.open();
+                socket.configureBlocking(false);
+                socket.socket().bind(addr);
+                logger.debug("socket listening on {}", addr.toString());
+                listenSockets.add(socket);
+                addAcceptSocket(socket);
+            }
+        } catch (IOException e) {
+            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;
+    }
+
+    /**
+     * 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();
+    }
+
+    /**
+     * Pass messages that the runabout can handle to it.
+     * There can only be one runabout per message type.
+     * (Discrepancy with the C-API, could be changed in the future)
+     *
+     * @param msgRunabout handler
+     */
+    public void setHandler(MessageRunabout 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() {
+            @Override
+            public void cancel() {
+                Server.this.disconnectHandlers.remove(disconnectHandler);
+            }
+        };
+    }
+
+    /**
+     * Stop the listen socket destroy the server as soon as only monitor 
clients are left.
+     */
+    public void stopListening() {
+        inSoftShutdown = true;
+        if (acceptTask != null) {
+            acceptTask.cancel();
+            acceptTask = null;
+        }
+        testForSoftShutdown();
+    }
+
+    /**
+     * Disconnect all clients forcefully from the server and stop listening.
+     * <p/>
+     * No methods should be called on a server and its client handles after 
destroy() has been called.
+     */
+    public void destroy() {
+        if (destroyed) {
+            return;
+        }
+        destroyed = true;
+        for (ClientHandle h : clientHandles) {
+            h.disconnect();
+        }
+        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();
+            }
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        super.finalize();
+        if (!destroyed) {
+            logger.warn("Server instance not destroyed, but finalizer called");
+        }
+        destroy();
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/util/Service.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/Service.java                      
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/Service.java      2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,154 @@
+/*
+ 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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.Pipe;
+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
+            .getLogger(Service.class);
+
+    private Server s;
+    private String serviceName;
+    private RelativeTime idleTimeout;
+    private boolean requireFound;
+
+
+    private Cancelable sigpipeTask;
+    private Pipe.SourceChannel sigpipeChannel;
+
+    public Service(String serviceName, RelativeTime idleTimeout, boolean 
requireFound, String[] args) {
+        super(args);
+        this.serviceName = serviceName;
+        this.idleTimeout = idleTimeout;
+        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 runHook() {
+        String ip4AddrList = getConfiguration().getValueString(serviceName, 
"ACCEPT_FROM").orNull();
+        String ip6AddrList = getConfiguration().getValueString(serviceName, 
"ACCEPT_FROM6").orNull();
+        int port = getConfiguration().getValueNumber(serviceName, 
"PORT").get().intValue();
+
+        LinkedList<SocketAddress> addrs = new LinkedList<SocketAddress>();
+
+        if (ip4AddrList != null) {
+            for (String ip4Addr : ip4AddrList.split("[;]")) {
+                InetAddress addr = Resolver.getInetAddressFromString(ip4Addr);
+                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 && !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();
+        }
+
+        run();
+    }
+
+    private class SigpipeTask implements Scheduler.Task {
+        @Override
+        public void run(Scheduler.RunContext ctx) {
+            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);
+            }
+        }
+    }
+}
\ No newline at end of file

Added: gnunet-java/src/main/java/org/gnunet/util/Strings.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/Strings.java                      
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/Strings.java      2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,138 @@
+/*
+ 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;
+
+/**
+ * Common functions on Strings, specific to gnunet-java
+ */
+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();
+
+        long rpos = 0;
+        long bits = 0;
+        long vbit = 0;
+        long size = data.length;
+
+        while ((rpos < size) || (vbit > 0)) {
+            if ((rpos < size) && (vbit < 5)) {
+                byte b = data[(int) rpos++];
+                // convert double to int without sign extension
+                int s = b >= 0 ? b : (256 + b);
+                // eat 8 more bits
+                bits = (bits << 8) | s;
+                vbit += 8;
+            }
+            if (vbit < 5) {
+                // zero-padding
+                bits <<= (5 - vbit);
+                vbit = 5;
+            }
+            sb.append(encTable.charAt((int) (bits >>> (vbit - 5)) & 31));
+            vbit -= 5;
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 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;
+        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;
+            shift = 5 - vbit;
+        } else {
+            vbit = 0;
+            shift = 0;
+        }
+        if ((encoded_len + shift) / 5 != enclen) {
+            throw new AssertionError();
+        }
+
+        wpos = outSize;
+        rpos = enclen;
+        bits = (ret = getValue__(string.charAt((int) (--rpos)))) >> (5 - 
encoded_len % 5);
+        if (-1 == ret) {
+            throw new AssertionError();
+        }
+        while (wpos > 0) {
+            assert rpos > 0;
+            bits = ((ret = getValue__(string.charAt((int) (--rpos)))) << vbit) 
| bits;
+            if (-1 == ret) {
+                throw new AssertionError();
+            }
+            vbit += 5;
+            if (vbit >= 8) {
+                out[(int)--wpos] = (byte)((char) bits);
+                bits >>= 8;
+                vbit -= 8;
+            }
+        }
+        assert(rpos == 0);
+        assert(vbit == 0);
+        return out;
+    }
+
+
+    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;
+    }
+
+}

Added: gnunet-java/src/main/java/org/gnunet/util/TestMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/TestMessage.java                  
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/TestMessage.java  2013-08-27 
17:16:18 UTC (rev 28880)
@@ -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.
+ */
address@hidden(1)
+public class TestMessage implements GnunetMessage.Body {
+    // empty
+}

Added: gnunet-java/src/main/java/org/gnunet/util/UnknownMessageBody.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/UnknownMessageBody.java           
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/UnknownMessageBody.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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.util;
+
+/**
+ * Special GnunetMessage body, used to signal that the message containing the 
body
+ * 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, and can not, be sent/received over the network 
directly as a message body.
+ *
+ * @author Florian Dold
+ */
+public class UnknownMessageBody implements GnunetMessage.Body {
+    public int id;
+    public byte[] data;
+}

Added: gnunet-java/src/main/java/org/gnunet/util/getopt/Argument.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/getopt/Argument.java              
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/getopt/Argument.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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.
+ */
address@hidden(RetentionPolicy.RUNTIME)
address@hidden(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();
+}

Added: gnunet-java/src/main/java/org/gnunet/util/getopt/ArgumentAction.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/getopt/ArgumentAction.java        
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/getopt/ArgumentAction.java        
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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
+}

Added: gnunet-java/src/main/java/org/gnunet/util/getopt/Parser.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/getopt/Parser.java                
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/getopt/Parser.java        
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,294 @@
+/*
+ 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 org.gnunet.construct.ReflectUtil;
+import java.lang.reflect.Field;
+import java.util.*;
+
+/**
+ * Parser for command line options, in the format indicated by the
+ * annotated members of the target object's class.
+ */
+public class Parser {
+
+    /**
+     * An ArgumentError is thrown if the command line parameters do not match 
their
+     * specification in the target object's class.
+     */
+    public static class ArgumentError extends RuntimeException {
+        public ArgumentError(String s) {
+            super(s);
+        }
+    }
+
+    /**
+     * An option together with its target field.
+     */
+    static class OptionField {
+        Argument opt;
+        Field f;
+
+        public OptionField(Argument opt, Field f) {
+            this.opt = opt;
+            this.f = f;
+        }
+    }
+
+    // todo: unify with Construct.getMessageFields
+    private List<Field> getFields(Class c) {
+        LinkedList<Field> fields = new 
LinkedList<Field>(Arrays.asList(c.getDeclaredFields()));
+        while ((c = c.getSuperclass()) != null) {
+            fields.addAll(0, Arrays.asList(c.getDeclaredFields()));
+        }
+        return fields;
+    }
+
+    private Map<String, OptionField> longOpt = new HashMap<String, 
OptionField>();
+    private Map<String, OptionField> shortOpt = new HashMap<String, 
OptionField>();
+
+    private Collection<Argument> arguments = new LinkedList<Argument>();
+
+    private Object targetObject;
+
+
+    public Parser(Object targetObject) {
+        this.targetObject = targetObject;
+        // gather option annotations
+        for (Field f : getFields(targetObject.getClass())) {
+            Argument opt = f.getAnnotation(Argument.class);
+            if (opt != null) {
+                if (opt.shortname().length() != 1) {
+                    throw new AssertionError("short name must be of length 1");
+                }
+
+                f.setAccessible(true);
+
+                longOpt.put(opt.longname(), new OptionField(opt, f));
+                shortOpt.put(opt.shortname(), new OptionField(opt, f));
+                arguments.add(opt);
+            }
+        }
+    }
+
+    public String getHelp() {
+        StringBuilder helpString = new StringBuilder();
+        for (Argument opt : arguments) {
+            StringBuilder line = new StringBuilder();
+            line.append("  -");
+            line.append(opt.shortname());
+            line.append(" --");
+            line.append(opt.longname());
+            if (!opt.argumentName().isEmpty()) {
+                line.append("=");
+                line.append(opt.argumentName());
+            }
+            while (line.length() < 30) {
+                line.append(" ");
+            }
+            helpString.append(line);
+            helpString.append(" ");
+            helpString.append(opt.description());
+            helpString.append("\n");
+
+        }
+        return helpString.toString();
+    }
+
+    private void doLongOpt(final LinkedList<String> argsList, Field 
targetField, Argument argument, String right) {
+        try {
+            Class targetFieldType = targetField.getType();
+            switch (argument.action()) {
+                case SET:
+                    if (!targetFieldType.equals(Boolean.TYPE)) {
+                        throw new AssertionError("action SET only valid on 
boolean member");
+                    }
+                    targetField.set(targetObject, true);
+                    break;
+                case RESET:
+                    if (!targetFieldType.equals(Boolean.TYPE)) {
+                        throw new AssertionError("action RESET only valid on 
boolean member");
+                    }
+                    targetField.set(targetObject, false);
+                    break;
+                case STORE_STRING:
+                    if (!targetFieldType.equals(String.class)) {
+                        throw new AssertionError("action STORE_STRING only 
valid on boolean member");
+                    }
+                    if (right == null) {
+                        argsList.removeFirst();
+                        if (argsList.isEmpty()) {
+                            throw new ArgumentError("missing string argument 
to option " + argument.longname());
+                        }
+                        targetField.set(targetObject, argsList.getFirst());
+                    } else {
+                        targetField.set(targetObject, right);
+                    }
+                    break;
+                case STORE_NUMBER:
+                    ReflectUtil.NumField nf = new 
ReflectUtil.NumField(targetField);
+                    String numString;
+                    if (right == null) {
+                        argsList.removeFirst();
+                        if (argsList.isEmpty()) {
+                            throw new ArgumentError("missing number argument 
to option " + argument.longname());
+                        }
+                        numString = argsList.getFirst();
+                    } else {
+                        numString = right;
+                    }
+                    try {
+                        nf.set(targetObject, Long.parseLong(numString));
+                    } catch (NumberFormatException e) {
+                        throw new ArgumentError("error in number format to 
option " + argument.longname());
+                    }
+                    break;
+            }
+        } catch (IllegalAccessException e) {
+            throw new AssertionError(
+                    String.format("cannot acces member %s with @Option 
annotation", targetField.getName()));
+        }
+    }
+
+    /**
+     * returns true if we processed a shortopt with a parameter, and thus have 
to discard the rest
+     * of the current argument string (that is, stop scanning for more 
shortopts)
+     */
+    private boolean doShortOpt(final LinkedList<String> argsList, Field 
targetField, Argument argument, String shortName) {
+        try {
+            switch (argument.action()) {
+                case SET:
+                    if (!targetField.getType().equals(Boolean.TYPE)) {
+                        throw new AssertionError("action SET only valid on 
boolean member");
+                    }
+                    targetField.set(targetObject, true);
+                    break;
+                case RESET:
+                    if (!targetField.getType().equals(Boolean.TYPE)) {
+                        throw new AssertionError("action RESET only valid on 
boolean field");
+                    }
+                    targetField.set(targetObject, false);
+                    break;
+                case STORE_STRING:
+                    if (!targetField.getType().equals(String.class)) {
+                        throw new AssertionError("action STORE_STRING only 
valid on 'String' field");
+                    }
+                    if (argsList.getFirst().length() == 2) { // -P xxx (with 
space)
+                        argsList.removeFirst();
+                        if (argsList.isEmpty()) {
+                            throw new ArgumentError(String.format("no argument 
for short option '%s'",
+                                    shortName));
+                        }
+                        targetField.set(targetObject, argsList.getFirst());
+                    } else {
+                        targetField.set(targetObject, 
argsList.getFirst().substring(2)); // -Pxxx...
+                    }
+                    return true;
+                case STORE_NUMBER:
+                    ReflectUtil.NumField nf = new 
ReflectUtil.NumField(targetField);
+                    String numString;
+                    if (argsList.getFirst().length() == 2) { // -X
+                        argsList.removeFirst();
+                        if (argsList.isEmpty()) {
+                            throw new ArgumentError("missing number argument 
to option " + argument.longname());
+                        }
+                        numString = argsList.getFirst();
+                    } else {
+                        numString = argsList.getFirst().substring(2);
+                    }
+                    try {
+                        nf.set(targetObject, Long.parseLong(numString));
+                    } catch (NumberFormatException e) {
+                        throw new ArgumentError("error in number format to 
option " + argument.longname());
+                    }
+                    return true;
+            }
+        } catch (IllegalAccessException e) {
+            throw new ArgumentError(
+                    String.format("cannot acces member %s with @Option 
annotation", targetField.getName()));
+        }
+        return false; // did not consume entire shortopt -Xxxxxx
+    }
+
+    /**
+     * Parses the given arguments, and sets the target object's fields
+     * according to its annotations.
+     *
+     * @param args array with the program arguments
+     * @return positional arguments
+     */
+    public String[] parse(String[] args) {
+        // unprocessed positional args
+        Deque<String> positionalArgs = new LinkedList<String>();
+
+        LinkedList<String> argsList = new 
LinkedList<String>(Arrays.asList(args));
+
+        while (!argsList.isEmpty()) {
+            // arguments after single "--" are all positional
+            if (argsList.getFirst().equals("--")) {
+                argsList.removeFirst();
+                positionalArgs.addAll(argsList);
+                break;
+            }
+            // long args
+            if (argsList.getFirst().startsWith("--")) {
+                // remove leading slashes
+                String longOptionString = argsList.getFirst().substring(2);
+                // maybe it is in the format --opt=val
+                String[] components = longOptionString.split("=", 2);
+                OptionField of = longOpt.get(components[0]);
+                if (of == null) {
+                    throw new ArgumentError(String.format("unknown long 
option: '%s'", components[0]));
+                }
+                String right = (components.length == 2) ? components[1] : null;
+                doLongOpt(argsList, of.f, of.opt, right);
+            } else if ((argsList.getFirst().length() > 1) && 
argsList.getFirst().startsWith("-")) {
+                // handle each flag after the "-"
+                for (int i = 1; i < argsList.getFirst().length(); ++i) {
+                    String optShortName = argsList.getFirst().substring(i, i + 
1);
+                    OptionField of = shortOpt.get(optShortName);
+                    if (of == null) {
+                        throw new ArgumentError(
+                                String.format("unknown short option: -%s", 
argsList.getFirst().charAt(i)));
+                    }
+
+                    boolean discard = doShortOpt(argsList, of.f, of.opt, 
optShortName);
+
+                    if (discard && (i != 1)) {
+                        throw new ArgumentError("short options with argument 
must be seperate");
+                    }
+
+                    if (discard) {
+                        break;
+                    }
+
+                }
+            } else {
+                positionalArgs.add(argsList.getFirst());
+            }
+
+            argsList.removeFirst();
+        }
+
+        return positionalArgs.toArray(new String[positionalArgs.size()]);
+    }
+
+}

Added: gnunet-java/src/main/java/org/gnunet/util/getopt/package-info.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/getopt/package-info.java          
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/getopt/package-info.java  
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,24 @@
+/*
+ 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.
+ */
+
+/**
+ * Command line option parsing
+ */
+package org.gnunet.util.getopt;

Added: gnunet-java/src/main/java/org/gnunet/util/package-info.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/package-info.java                 
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/package-info.java 2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,24 @@
+/*
+ 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.
+ */
+
+/**
+ * Common utilities for gnunet components.
+ */
+package org.gnunet.util;

Added: 
gnunet-java/src/main/java/org/gnunet/voting/CertificateAuthorityService.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/voting/CertificateAuthorityService.java    
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/voting/CertificateAuthorityService.java    
    2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,7 @@
+package org.gnunet.voting;
+
+/**
+ * Permits or denies a voter to participate in an election.
+ */
+public class CertificateAuthorityService {
+}

Added: gnunet-java/src/main/java/org/gnunet/voting/ElectionCallTool.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/ElectionCallTool.java           
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/ElectionCallTool.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,50 @@
+package org.gnunet.voting;
+
+
+import org.gnunet.util.Program;
+import org.gnunet.util.getopt.Argument;
+import org.gnunet.util.getopt.ArgumentAction;
+
+public class ElectionCallTool {
+
+    public static void main(String[] args) {
+        new Program(args) {
+            @Argument(
+                    shortname = "t",
+                    longname = "template",
+                    action = ArgumentAction.SET,
+                    description = "output election template")
+            boolean template = false;
+            @Argument(
+                    shortname = "o",
+                    longname = "outfile",
+                    argumentName = "FILE",
+                    action = ArgumentAction.STORE_STRING,
+                    description = "write spec to FILE instead of standard 
output")
+            String outfilename = null;
+            @Argument(
+                    shortname = "V",
+                    longname = "verify",
+                    action = ArgumentAction.SET,
+                    description = "verify that the ESPEC is valid")
+            boolean verify = false;
+            @Argument(
+                    shortname = "a",
+                    longname = "authorize",
+                    action = ArgumentAction.SET,
+                    description = "authorize the ESPEC with the authorities")
+            boolean authorize = false;
+
+            @Override
+            protected String makeHelpText() {
+                return "gnunet-vote-call [OPTIONS]... ESPEC\n" +
+                        "Create, authorize and verify an election 
specification";
+            }
+
+            @Override
+            public void run() {
+            }
+
+        }.start();
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/voting/ElectionSpecification.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/ElectionSpecification.java      
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/ElectionSpecification.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,5 @@
+package org.gnunet.voting;
+
+public class ElectionSpecification {
+
+}

Added: gnunet-java/src/main/java/org/gnunet/voting/TallyAuthorityService.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/TallyAuthorityService.java      
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/TallyAuthorityService.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,5 @@
+package org.gnunet.voting;
+
+
+public class TallyAuthorityService {
+}

Added: gnunet-java/src/main/java/org/gnunet/voting/VotingTool.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/VotingTool.java                 
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/VotingTool.java 2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,49 @@
+package org.gnunet.voting;
+
+
+import org.gnunet.util.Program;
+import org.gnunet.util.getopt.Argument;
+import org.gnunet.util.getopt.ArgumentAction;
+
+public class VotingTool {
+    public static void main(String[] args) {
+        new Program(args) {
+            @Argument(
+                    shortname = "q",
+                    longname = "query",
+                    action = ArgumentAction.SET,
+                    description = "query election result")
+            boolean query = false;
+
+            @Argument(
+                    shortname = "s",
+                    longname = "submit",
+                    action = ArgumentAction.STORE_STRING,
+                    argumentName = "CHOICE",
+                    description = "submit vote to the election")
+            String vote = null;
+
+            @Argument(
+                    shortname = "p",
+                    longname = "certificate",
+                    action = ArgumentAction.STORE_STRING,
+                    argumentName = "FILE",
+                    description = "certificate file with the permission to 
vote")
+            String certfile = null;
+
+            @Override
+            protected String makeHelpText() {
+                return "gnunet-vote [OPTIONS]... ESPEC\n" +
+                        "Submit a vote or query an election's result.\n" +
+                        "The election is identified in the ESPEC file.";
+            }
+
+            @Override
+            public void run() {
+
+            }
+        }.start();
+
+    }
+
+}

Added: gnunet-java/src/main/java/org/gnunet/voting/simulation/Authority.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/simulation/Authority.java       
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/simulation/Authority.java       
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,266 @@
+package org.gnunet.voting.simulation;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import java.math.BigInteger;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public class Authority {
+    private BigInteger privateKeyShare;
+    private BigInteger[] secretPolynomial;
+    private TransmitShareVerification shareVerification;
+
+    private int authorityId;
+
+    private BigInteger receivedShare = BigInteger.ZERO;
+
+    private VotingParameters parameters;
+    private List<Authority> participatingAuthorities;
+
+    private List<Ballot> ballots = Lists.newLinkedList();
+
+    private Map<Integer, TallyKeyShare> specializedKeyShares = 
Maps.newTreeMap();
+
+    private Cyphertext encryptedTally;
+    private BigInteger tallyBaseG;
+
+
+    /**
+     * The commitments of each authority to it's share of the group secret.
+     */
+    Map<Integer, BigInteger> shareCommitments = Maps.newTreeMap();
+
+    private GroupPublicKey groupPublicKey;
+
+
+    public Authority() {
+    }
+
+    public BigInteger getPublicKeyShare() {
+        return parameters.g.modPow(privateKeyShare, parameters.p);
+    }
+
+    public BigInteger createShareForAuthority(int j) {
+        return CryptoUtil.evaluatePolynomial(secretPolynomial, 
BigInteger.valueOf(j), parameters.q);
+    }
+
+    public void verifyShare(BigInteger distributionShare, 
TransmitShareVerification senderVerification) {
+        BigInteger v = parameters.g.modPow(distributionShare, parameters.p);
+        BigInteger prod = BigInteger.ONE;
+        for (int l = 0; l < parameters.authorityThreshold; ++l) {
+            BigInteger exp = BigInteger.valueOf(this.getId()).pow(l);
+            BigInteger coeff = senderVerification.coeffs[l];
+            prod = prod.multiply(coeff.modPow(exp, 
parameters.p)).mod(parameters.p);
+        }
+        if (!v.equals(prod)) {
+            throw new AssertionError("verification of transmitted shared 
failed");
+        }
+    }
+
+    public void acceptShareFromAuthority(BigInteger distributionShare, 
TransmitShareVerification senderVerification, Authority sender) {
+        verifyShare(distributionShare, senderVerification);
+        receivedShare = receivedShare.add(distributionShare);
+    }
+
+    public int getId() {
+        return authorityId;
+    }
+
+    /**
+     * Supervisor -> Authority
+     *
+     * @param authorityId
+     * @param parameters
+     * @return
+     */
+    public BigInteger inviteAuthority(int authorityId, VotingParameters 
parameters) {
+        this.authorityId = authorityId;
+        this.parameters = parameters;
+
+        this.privateKeyShare = parameters.generateZq();
+        this.secretPolynomial = new BigInteger[parameters.authorityThreshold];
+
+        this.secretPolynomial[0] = privateKeyShare;
+        for (int i = 1; i < parameters.authorityThreshold; ++i) {
+            secretPolynomial[i] = parameters.generateZq();
+        }
+
+        shareVerification = new TransmitShareVerification(secretPolynomial, 
parameters);
+
+        return getPublicKeyShare();
+    }
+
+    /**
+     * Supervisor -> Authority.
+     *
+     * @param participatingAuthorities
+     */
+    public void generateKeyWithAuthorities(List<Authority> 
participatingAuthorities) {
+        this.participatingAuthorities = participatingAuthorities;
+
+        for (Authority otherAuthority : participatingAuthorities) {
+            
otherAuthority.acceptShareFromAuthority(createShareForAuthority(otherAuthority.getId()),
 shareVerification, this);
+        }
+    }
+
+    public void acceptBallot(Ballot ballot) {
+        verifyBallot(ballot);
+        // todo: check duplicates
+        ballots.add(ballot);
+
+    }
+
+    public void acceptGroupPublicKey(GroupPublicKey key) {
+        groupPublicKey = key;
+    }
+
+    public void acceptSpecializedKeyShare(TallyKeyShare share, Authority 
sender) {
+        specializedKeyShares.put(sender.getId(), share);
+        verifyTallyKeyShare(share);
+    }
+
+    public void distributeTallyKey() {
+        computeEncryptedTally();
+
+        for (Authority other : participatingAuthorities) {
+            TallyKeyShare tallyKeyShare = new TallyKeyShare(encryptedTally.c1, 
receivedShare, parameters);
+            other.acceptSpecializedKeyShare(tallyKeyShare, this);
+        }
+    }
+
+    private void computeEncryptedTally() {
+        BigInteger votesX = BigInteger.ONE;
+        BigInteger votesY = BigInteger.ONE;
+        for (Ballot ballot : ballots) {
+            votesX = votesX.multiply(ballot.x).mod(parameters.p);
+            votesY = votesY.multiply(ballot.y).mod(parameters.p);
+        }
+
+        encryptedTally = new Cyphertext(votesX, votesY);
+    }
+
+    private void decryptTallyToBaseG() {
+        Map<Integer, BigInteger> lagrangeCoefficients = Maps.newTreeMap();
+        for (int j : specializedKeyShares.keySet()) {
+            BigInteger n = BigInteger.ONE;
+            BigInteger d = BigInteger.ONE;
+            for (int l : specializedKeyShares.keySet()) {
+                if (l != j) {
+                    n = n.multiply(BigInteger.valueOf(l));
+                    d = 
d.multiply(BigInteger.valueOf(l).subtract(BigInteger.valueOf(j)));
+                }
+            }
+            lagrangeCoefficients.put(j, 
n.multiply(d.modInverse(parameters.q)).mod(parameters.q));
+        }
+
+        BigInteger prod = BigInteger.ONE;
+        for (int authorityIndex : specializedKeyShares.keySet()) {
+            BigInteger wp = 
specializedKeyShares.get(authorityIndex).w.modPow(lagrangeCoefficients.get(authorityIndex),
 parameters.p);
+            prod = prod.multiply(wp).mod(parameters.p);
+        }
+
+        tallyBaseG = 
encryptedTally.c2.multiply(prod.modInverse(parameters.p)).mod(parameters.p);
+    }
+
+    public int decryptTally() {
+        decryptTallyToBaseG();
+
+        int resultRestored = 0;
+        boolean success = false;
+
+        for (int l = -ballots.size(); l <= ballots.size(); ++l) {
+            if (tallyBaseG.equals(parameters.g.modPow(BigInteger.valueOf(l), 
parameters.p))) {
+                success = true;
+                resultRestored = l;
+                break;
+            }
+        }
+
+        if (!success) {
+            throw new AssertionError();
+        }
+        return resultRestored;
+    }
+
+    /*
+    * Sigma is the the authority's commitment to its share
+    */
+    public void verifyTallyKeyShare(TallyKeyShare share) {
+        BigInteger p = parameters.p;
+        BigInteger g = parameters.g;
+
+        BigInteger c1 = share.c1;
+        BigInteger a = share.a;
+        BigInteger r = share.r;
+        BigInteger b = share.b;
+        BigInteger c = share.c;
+
+        // verifier
+        BigInteger expected1 = g.modPow(r, p);
+        BigInteger received1 = a.multiply(share.sigma.modPow(c, p)).mod(p);
+
+        BigInteger expected2 = c1.modPow(r, p);
+        BigInteger received2 = b.multiply(share.w.modPow(c, p)).mod(p);
+
+        if ((!expected1.equals(received1)) || (!expected2.equals(received2))) {
+            System.err.println(expected1);
+            System.err.println(received1);
+            throw new AssertionError("zero knowledge proof for decryption 
failed");
+        }
+    }
+
+
+
+    public void verifyBallot(Ballot ballot) {
+        BigInteger g = parameters.g;
+        BigInteger p = parameters.p;
+        BigInteger h = groupPublicKey.getKey();
+        // verifier
+
+        BigInteger voterId = ballot.voterId;
+        BigInteger x = ballot.x;
+        BigInteger y = ballot.y;
+        BigInteger a_1 = ballot.a_1;
+        BigInteger a_2 = ballot.a_2;
+        BigInteger b_1 = ballot.b_1;
+        BigInteger b_2 = ballot.b_2;
+        BigInteger c = ballot.c;
+        BigInteger d_1 = ballot.d_1;
+        BigInteger d_2 = ballot.d_2;
+        BigInteger r_1 = ballot.r_1;
+        BigInteger r_2 = ballot.r_2;
+
+
+        // todo: we should blame someone here, not throw exceptions
+
+
+        if (!c.equals(CryptoUtil.hash(voterId, x, y, a_1, b_1, a_2, 
b_2).mod(parameters.q))) {
+            throw new AssertionError();
+        }
+
+        if (!c.equals(d_1.add(d_2).mod(p))) {
+            throw new AssertionError();
+        }
+        if (!a_1.equals(g.modPow(r_1, p).multiply(x.modPow(d_1, p)).mod(p))) {
+            throw new AssertionError();
+        }
+        if (!b_1.equals(h.modPow(r_1, p).multiply(y.multiply(g).modPow(d_1, 
p)).mod(p))) {
+            throw new AssertionError();
+        }
+
+        if (!a_2.equals(g.modPow(r_2, p).multiply(x.modPow(d_2, p)).mod(p))) {
+            throw new AssertionError();
+        }
+
+        if (!b_2.equals(h.modPow(r_2, 
p).multiply(y.multiply(g.modInverse(p)).modPow(d_2, p)).mod(p))) {
+            throw new AssertionError();
+        }
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/voting/simulation/Ballot.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/simulation/Ballot.java          
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/simulation/Ballot.java  
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,105 @@
+package org.gnunet.voting.simulation;
+
+import java.math.BigInteger;
+
+/**
+ * A ballot together with a ZKP for its validity
+ *
+ * @author Florian Dold
+ */
+public class Ballot {
+    public final BigInteger voterId;
+
+    // the ElGamal encryption of the vote
+    public final BigInteger x, y;
+    // values for the zero knowledge proof
+    public final BigInteger a_1, b_1, a_2, b_2, r_1, d_1, r_2, d_2, c;
+    private VotingParameters parameters;
+
+
+    public Ballot(boolean v, BigInteger voterId, GroupPublicKey 
groupPublicKey, VotingParameters parameters) {
+        this.voterId = voterId;
+        this.parameters = parameters;
+
+        BigInteger g = parameters.g;
+        BigInteger p = parameters.p;
+        BigInteger h = groupPublicKey.getKey();
+
+
+        if (v) {
+            BigInteger alpha = parameters.generateZq();
+            BigInteger w = parameters.generateZq();
+            r_1 = parameters.generateZq();
+            d_1 = parameters.generateZq();
+
+            x = g.modPow(alpha, p);
+            y = h.modPow(alpha, p).multiply(g).mod(p);
+
+            a_1 = g.modPow(r_1, p).multiply(x.modPow(d_1, p)).mod(p);
+            b_1 = h.modPow(r_1, p).multiply(y.multiply(g).modPow(d_1, 
p)).mod(p);
+            a_2 = g.modPow(w, p);
+            b_2 = h.modPow(w, p);
+
+
+            c = CryptoUtil.hash(voterId, x, y, a_1, b_1, a_2, 
b_2).mod(parameters.q);
+
+            // prover
+            d_2 = c.subtract(d_1);
+            r_2 = w.subtract(alpha.multiply(d_2));
+        } else {
+            // prover
+            BigInteger alpha = parameters.generateZq();
+            BigInteger w = parameters.generateZq();
+            r_2 = parameters.generateZq();
+            d_2 = parameters.generateZq();
+
+            x = g.modPow(alpha, p);
+            y = h.modPow(alpha, p).multiply(g.modInverse(p)).mod(p);
+
+            a_1 = g.modPow(w, p);
+            b_1 = h.modPow(w, p);
+            a_2 = g.modPow(r_2, p).multiply(x.modPow(d_2, p)).mod(p);
+            b_2 = h.modPow(r_2, 
p).multiply(y.multiply(g.modInverse(p)).modPow(d_2, p)).mod(p);
+
+
+            c = CryptoUtil.hash(voterId, x, y, a_1, b_1, a_2, 
b_2).mod(parameters.q);
+
+            // prover
+            d_1 = c.subtract(d_2);
+            r_1 = w.subtract(alpha.multiply(d_1));
+        }
+    }
+
+    public void verify(GroupPublicKey groupPublicKey) {
+        BigInteger g = parameters.g;
+        BigInteger p = parameters.p;
+        BigInteger h = groupPublicKey.getKey();
+        // verifier
+
+
+        if (!c.equals(CryptoUtil.hash(voterId, x, y, a_1, b_1, a_2, 
b_2).mod(parameters.q))) {
+            throw new AssertionError();
+        }
+
+        if (!c.equals(d_1.add(d_2).mod(p))) {
+            throw new AssertionError();
+        }
+        if (!a_1.equals(g.modPow(r_1, p).multiply(x.modPow(d_1, p)).mod(p))) {
+            throw new AssertionError();
+        }
+
+        if (!a_2.equals(g.modPow(r_2, p).multiply(x.modPow(d_2, p)).mod(p))) {
+            throw new AssertionError();
+        }
+
+        if (!b_2.equals(h.modPow(r_2, 
p).multiply(y.multiply(g.modInverse(p)).modPow(d_2, p)).mod(p))) {
+            throw new AssertionError();
+        }
+
+        if (!b_1.equals(h.modPow(r_1, p).multiply(y.multiply(g).modPow(d_1, 
p)).mod(p))) {
+            throw new AssertionError();
+        }
+
+
+    }
+}

Added: 
gnunet-java/src/main/java/org/gnunet/voting/simulation/BogusAuthority.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/simulation/BogusAuthority.java  
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/simulation/BogusAuthority.java  
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,9 @@
+package org.gnunet.voting.simulation;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public class BogusAuthority extends Authority {
+}

Added: gnunet-java/src/main/java/org/gnunet/voting/simulation/CallForVoters.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/simulation/CallForVoters.java   
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/simulation/CallForVoters.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,14 @@
+package org.gnunet.voting.simulation;
+
+import java.util.List;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public class CallForVoters {
+    public List<Authority> authorities;
+    public VotingParameters parameters;
+    public GroupPublicKey publicKey;
+}

Added: gnunet-java/src/main/java/org/gnunet/voting/simulation/CryptoUtil.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/simulation/CryptoUtil.java      
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/simulation/CryptoUtil.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,86 @@
+package org.gnunet.voting.simulation;
+
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Random;
+
+/**
+ * Miscellaneous helper functions.
+ *
+ * @author Florian Dold
+ */
+public class CryptoUtil {
+    public static final Random random = new Random();
+
+
+    /**
+     * Return a random BigInteger not less than 'min' and not greater than 
'max' with uniform distribution.
+     *
+     * @param min    the least value that may be generated
+     * @param max    the greatest value that may be generated
+     * @return a random BigInteger value in the range [min,max]
+     */
+    public static BigInteger createRandomInRange(BigInteger min,
+                                                 BigInteger max) {
+        int cmp = min.compareTo(max);
+        if (cmp >= 0) {
+            if (cmp > 0) {
+                throw new IllegalArgumentException("'min' may not be greater 
than 'max'");
+            }
+
+            return min;
+        }
+
+        if (min.bitLength() > max.bitLength() / 2) {
+            return createRandomInRange(BigInteger.ZERO, 
max.subtract(min)).add(min);
+        }
+
+        for (int i = 0; i < 1000; ++i) {
+            BigInteger x = new BigInteger(max.bitLength(), random);
+            if (x.compareTo(min) >= 0 && x.compareTo(max) <= 0) {
+                return x;
+            }
+        }
+
+        // fall back to a faster (restricted) method
+        // (using only this distribution would lead to a non-uniform 
distribution, see the BigInteger constructor)
+        return new BigInteger(max.subtract(min).bitLength() - 1, 
random).add(min);
+    }
+
+
+
+    /**
+     * Evaluate a polynomial over Zp*. Uses Horner's scheme.
+     *
+     * @param coeffs coefficients of the polynomial, where coeffs[i] is the 
coefficient of x^i
+     * @param x the polynomial is evaluated at this value
+     * @param p what group are we operating in?
+     * @return the result of evaluating the polynomial at x
+     */
+    public static BigInteger evaluatePolynomial(BigInteger[] coeffs, 
BigInteger x, BigInteger p) {
+        BigInteger z = BigInteger.ZERO;
+        for (int i = 0; i < coeffs.length; ++i) {
+            // z <- zx + c
+            z = z.multiply(x).add(coeffs[coeffs.length - i - 1]);
+        }
+        return z;
+    }
+
+    public static BigInteger hash(BigInteger... x) {
+        MessageDigest md;
+        try {
+            md = MessageDigest.getInstance("SHA-512");
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("no SHA-512 available");
+        }
+
+        for (BigInteger v : x) {
+            md.update(v.toByteArray());
+        }
+
+        return new BigInteger(md.digest());
+    }
+
+}

Added: gnunet-java/src/main/java/org/gnunet/voting/simulation/Cyphertext.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/simulation/Cyphertext.java      
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/simulation/Cyphertext.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,19 @@
+package org.gnunet.voting.simulation;
+
+import java.math.BigInteger;
+
+/**
+ * ElGamal encryption of a message.
+ *
+ * @author Florian Dold
+ */
+public class Cyphertext {
+    public final BigInteger c1;
+    public final BigInteger c2;
+
+
+    public Cyphertext(BigInteger c1, BigInteger c2) {
+        this.c1 = c1;
+        this.c2 = c2;
+    }
+}

Added: 
gnunet-java/src/main/java/org/gnunet/voting/simulation/ElectionSupervisor.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/voting/simulation/ElectionSupervisor.java  
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/voting/simulation/ElectionSupervisor.java  
    2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,105 @@
+package org.gnunet.voting.simulation;
+
+import com.google.common.collect.Lists;
+
+import java.math.BigInteger;
+import java.util.List;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public class ElectionSupervisor {
+    /**
+     * Accumulator for the group public key.
+     * todo: actually we should record the group publice key share of each 
authority seperately
+     */
+    private BigInteger groupPublicKeyAccum = BigInteger.ONE;
+
+    private VotingParameters parameters;
+
+    /**
+     * Authorities that will be invited for participation
+     */
+    private List<Authority> availableAuthorities;
+
+    /**
+     * Authorities that have agreed to participate.
+     */
+    private List<Authority> participatingAuthorities = Lists.newArrayList();
+
+
+
+    private enum Phase { START, INVITED, KEYS_GENERATED, PUBKEY_PUBLISHED}
+
+    private Phase currentPhase = Phase.START;
+
+
+    public ElectionSupervisor(List<Authority> availableAuthorities, 
VotingParameters parameters) {
+        this.parameters = parameters;
+        this.availableAuthorities = availableAuthorities;
+    }
+
+    /**
+     * Send an invitation to all available authorities
+     */
+    public void inviteAuthorities() {
+        if (currentPhase != Phase.START) {
+            throw new AssertionError();
+        }
+
+        int nextAuthorityNumber = 1;
+        for (Authority authority : availableAuthorities) {
+            BigInteger part = authority.inviteAuthority(nextAuthorityNumber, 
parameters);
+            nextAuthorityNumber += 1;
+            if (part != null) {
+                participatingAuthorities.add(authority);
+                groupPublicKeyAccum = 
groupPublicKeyAccum.multiply(part).mod(parameters.p);
+            }
+        }
+
+        currentPhase = Phase.INVITED;
+    }
+
+    public CallForVoters createCallForVote() {
+        if (currentPhase != Phase.PUBKEY_PUBLISHED) {
+            throw new AssertionError();
+        }
+
+        CallForVoters callForVoters = new CallForVoters();
+        callForVoters.authorities = participatingAuthorities;
+        callForVoters.parameters = parameters;
+        callForVoters.publicKey = new GroupPublicKey(groupPublicKeyAccum);
+        return callForVoters;
+
+    }
+
+
+    public void ascertainGroupPublicKey() {
+        if (currentPhase != Phase.KEYS_GENERATED) {
+            throw new AssertionError();
+        }
+
+        for (Authority authority : participatingAuthorities) {
+            authority.acceptGroupPublicKey(new 
GroupPublicKey(groupPublicKeyAccum));
+        }
+
+        currentPhase = Phase.PUBKEY_PUBLISHED;
+
+    }
+
+
+    /**
+     * Publish participating authorities of the key generation, receiving 
authorities will start the key generation.
+     */
+    public void startKeyGeneration() {
+        if (currentPhase != Phase.INVITED) {
+            throw new AssertionError();
+        }
+        for (Authority authority : participatingAuthorities) {
+            authority.generateKeyWithAuthorities(participatingAuthorities);
+        }
+        currentPhase = Phase.KEYS_GENERATED;
+    }
+}

Added: 
gnunet-java/src/main/java/org/gnunet/voting/simulation/GroupPublicKey.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/simulation/GroupPublicKey.java  
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/simulation/GroupPublicKey.java  
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,28 @@
+package org.gnunet.voting.simulation;
+
+import java.math.BigInteger;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public class GroupPublicKey {
+    final BigInteger key;
+
+    public GroupPublicKey(Authority[] authorities, VotingParameters 
parameters) {
+        BigInteger h = BigInteger.ONE;
+        for (Authority authority : authorities) {
+            h = h.multiply(authority.getPublicKeyShare()).mod(parameters.p);
+        }
+        key = h;
+    }
+
+    public GroupPublicKey(BigInteger groupPublicKey) {
+        this.key = groupPublicKey;
+    }
+
+    public BigInteger getKey() {
+        return key;
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/voting/simulation/TallyKeyShare.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/simulation/TallyKeyShare.java   
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/simulation/TallyKeyShare.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,41 @@
+package org.gnunet.voting.simulation;
+
+import java.math.BigInteger;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public class TallyKeyShare {
+    final public BigInteger w;
+
+    final public BigInteger sigma;
+
+    final public BigInteger c1;
+    final public BigInteger a, b, c, r;
+
+    final VotingParameters parameters;
+
+    public TallyKeyShare(BigInteger c1, BigInteger share, VotingParameters 
parameters) {
+        this.parameters = parameters;
+        this.c1 = c1;
+
+        sigma = parameters.g.modPow(share, parameters.p);
+
+        w = c1.modPow(share, parameters.p);
+
+        BigInteger p = parameters.p;
+        BigInteger g = parameters.g;
+
+        // prover
+        BigInteger beta = parameters.generateZq();
+        a = g.modPow(beta, p);
+        b = c1.modPow(beta, p);
+        // verifier
+        c = CryptoUtil.hash(a, b);
+        // prover
+        r = beta.add(share.multiply(c));
+    }
+}
+

Added: 
gnunet-java/src/main/java/org/gnunet/voting/simulation/TransmitShareVerification.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/voting/simulation/TransmitShareVerification.java
                               (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/voting/simulation/TransmitShareVerification.java
       2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,18 @@
+package org.gnunet.voting.simulation;
+
+import java.math.BigInteger;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public class TransmitShareVerification {
+    public final BigInteger[] coeffs;
+    public TransmitShareVerification(BigInteger[] secretPolynomial, 
VotingParameters parameters) {
+        coeffs = new BigInteger[secretPolynomial.length];
+        for (int i = 0; i < secretPolynomial.length; ++i) {
+            coeffs[i] = parameters.g.modPow(secretPolynomial[i], parameters.p);
+        }
+    }
+}

Added: gnunet-java/src/main/java/org/gnunet/voting/simulation/Voter.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/simulation/Voter.java           
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/simulation/Voter.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,31 @@
+package org.gnunet.voting.simulation;
+
+import java.math.BigInteger;
+
+/**
+ *
+ *
+ * @author Florian Dold
+ */
+public class Voter {
+    boolean b;
+    BigInteger voterId;
+    private CallForVoters callForVoters;
+
+    public Voter(CallForVoters callForVoters) {
+        b = CryptoUtil.random.nextBoolean();
+        voterId = new BigInteger(64, CryptoUtil.random);
+        this.callForVoters = callForVoters;
+    }
+
+    public Ballot generateBallot() {
+        return new Ballot(b, voterId, callForVoters.publicKey, 
callForVoters.parameters);
+    }
+
+    public void vote() {
+        Ballot ballot = generateBallot();
+        for (Authority authority : callForVoters.authorities) {
+            authority.acceptBallot(ballot);
+        }
+    }
+}

Added: 
gnunet-java/src/main/java/org/gnunet/voting/simulation/VotingParameters.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/voting/simulation/VotingParameters.java    
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/voting/simulation/VotingParameters.java    
    2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,165 @@
+package org.gnunet.voting.simulation;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * Utilities for the modified ElGamal algorithm.
+ * <p/>
+ * p is a large prime, and g is a high-order element (or even a generator) of 
Zp*
+ *
+ * @author Florian Dold
+ */
+public class VotingParameters {
+    // large prime, p = 2q + 1
+    public final BigInteger p;
+    // large prime, so that q divides (p-1)
+    public final BigInteger q;
+    // generator of Gq
+    public final BigInteger g;
+
+    public final int authorityCount;
+    public final int authorityThreshold;
+
+    public VotingParameters(BigInteger p, BigInteger q, BigInteger g, int 
authorityCount, int authorityThreshold) {
+        this.p = p;
+        this.q = q;
+        this.g = g;
+        this.authorityCount = authorityCount;
+        this.authorityThreshold = authorityThreshold;
+    }
+
+    /**
+     * which generates the p and g values from the given parameters,
+     * returning the ElGamalScheme object.
+     * <p/>
+     * Note: can take a while...
+     */
+    public static VotingParameters generateRandomParameters(int size, int 
certainty, int authorityCount, int authorityThreshold) {
+        BigInteger[] safePrimes = generateSafePrimes(size, certainty);
+        BigInteger p = safePrimes[0];
+        BigInteger q = safePrimes[1];
+        BigInteger alpha = selectGenerator(p, q);
+        BigInteger g = selectSubgroupHigherOrderElement(alpha, p, q);
+        if (!g.modPow(q, p).equals(BigInteger.ONE)) {
+            throw new AssertionError();
+        }
+        if (!(g.compareTo(p) < 0)) {
+            throw new AssertionError();
+        }
+        return new VotingParameters(p, q, g, authorityCount, 
authorityThreshold);
+    }
+
+    /**
+     * Finds a pair of prime BigInteger's {p, q: p = 2q + 1}, called safe 
primes.
+     * <p/>
+     * (see: Handbook of Applied Cryptography 4.86)
+     *
+     * @return A 2-element array {p,q} of safe primes.
+     */
+    private static BigInteger[] generateSafePrimes(int size, int certainty) {
+        BigInteger p, q;
+        int qLength = size - 1;
+
+        while (true) {
+            q = new BigInteger(qLength, 2, CryptoUtil.random);
+
+            // p <- 2q + 1
+            p = q.shiftLeft(1).add(BigInteger.ONE);
+
+            // XXX(dold): why do we test q for primality again?
+            if (p.isProbablePrime(certainty) && (certainty <= 2 || 
q.isProbablePrime(certainty))) {
+                break;
+            }
+        }
+
+        return new BigInteger[]{p, q};
+    }
+
+    /*
+    * Select a high order element of the multiplicative group Zn*
+    */
+    private static BigInteger selectHighOrderElement(BigInteger n, 
SecureRandom random) {
+        BigInteger g;
+        final BigInteger nMinusTwo = n.subtract(BigInteger.valueOf(2));
+        do {
+            BigInteger h = 
CryptoUtil.createRandomInRange(BigInteger.valueOf(2), nMinusTwo);
+
+            g = h.modPow(BigInteger.valueOf(2), n);
+        }
+        while (g.equals(BigInteger.valueOf(1)));
+
+        return g;
+    }
+
+    /**
+     * Returns a higher-order-element of Gq, the subgroup of Zp*, with order q 
where alpha is a generator of Zp*
+     *
+     * (see Handbook of Applied Cryptography 4.81)
+     */
+    private static BigInteger selectSubgroupHigherOrderElement(BigInteger 
alpha, BigInteger p, BigInteger q) {
+        return alpha.modPow(p.subtract(BigInteger.ONE).divide(q), p);
+    }
+
+    /**
+     * Get the size of the cyclic group Gp used for ElGamal
+     *
+     * @return the size of the cyclic group Gp used for ElGamal
+     */
+    public BigInteger getP() {
+        return p;
+    }
+
+    /**
+     * Get the generator of Gq
+     *
+     * @return the generator of Gq
+     */
+    public BigInteger getG() {
+        return g;
+    }
+
+    /**
+     * Get the generator of Zp*
+     *
+     * @return the generator of Zp*
+     */
+    public BigInteger getQ() {
+        return q;
+    }
+
+    public BigInteger generateGq() {
+        BigInteger r;
+        while (true) {
+            r = CryptoUtil.createRandomInRange(BigInteger.ZERO, this.p);
+            if (r.modPow(q, p).equals(BigInteger.ONE)) {
+                break;
+            }
+        }
+        return r;
+    }
+
+    public static BigInteger selectGenerator(BigInteger p, BigInteger q) {
+        BigInteger pMinusTwo = p.subtract(BigInteger.valueOf(2));
+        BigInteger g;
+        /*
+         * (see: Handbook of Applied Cryptography 4.80)
+         */
+        do {
+            g = CryptoUtil.createRandomInRange(BigInteger.valueOf(2), 
pMinusTwo);
+        }
+        while (g.modPow(BigInteger.valueOf(2), p).equals(BigInteger.ONE) || 
g.modPow(q, p).equals(BigInteger.ONE));
+        return g;
+    }
+
+    public BigInteger generateZq() {
+        return CryptoUtil.createRandomInRange(BigInteger.ZERO, 
this.q.subtract(BigInteger.ONE));
+    }
+
+    public Cyphertext encrypt(BigInteger message, BigInteger publicKey) {
+        BigInteger secret = generateZq();
+        return new Cyphertext(g.modPow(secret, p), 
message.multiply(publicKey.modPow(secret, p).mod(p)));
+    }
+
+
+}

Added: 
gnunet-java/src/main/java/org/gnunet/voting/simulation/VotingSimulation.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/voting/simulation/VotingSimulation.java    
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/voting/simulation/VotingSimulation.java    
    2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,122 @@
+package org.gnunet.voting.simulation;
+
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Simulation of the voting protocol.
+ *
+ * @author Florian Dold
+ */
+public class VotingSimulation {
+    public static void main(String... args) {
+        final int authorityCount = 10;
+        final int authorityThreshold = 6;
+        final VotingParameters parameters = 
VotingParameters.generateRandomParameters(64, 10, authorityCount, 
authorityThreshold);
+
+        List<Authority> availableAuthorities = 
spawnAuthorities(authorityCount, authorityCount);
+
+        final ElectionSupervisor supervisor = new 
ElectionSupervisor(availableAuthorities, parameters);
+
+        supervisor.inviteAuthorities();
+
+        // todo: what if to little authorities accepted the invitation?
+
+        supervisor.startKeyGeneration();
+
+        // make sure that every authority and the supervisor has the same 
public key
+        supervisor.ascertainGroupPublicKey();
+
+        CallForVoters callForVoters = supervisor.createCallForVote();
+
+        List<Voter> voters = spawnVoters(callForVoters, 100, 50);
+
+        int checkTally = countCheckTally(voters);
+
+        for (Voter voter : voters) {
+            voter.vote();
+        }
+
+        // todo: make sure every authority has the same votes
+
+        for (Authority authority : callForVoters.authorities) {
+            authority.distributeTallyKey();
+        }
+
+        // todo: blame authorities that failed the zero knowledge proof
+
+        for (Authority authority : callForVoters.authorities) {
+            int tally = authority.decryptTally();
+
+            if (tally != checkTally) {
+                throw new AssertionError();
+            }
+        }
+
+    }
+
+    private static int countCheckTally(List<Voter> voters) {
+        int tally = 0;
+        for (Voter voter : voters) {
+            tally += voter.b ? 1 : -1;
+        }
+        return tally;
+    }
+
+
+    /**
+     * Create all authorities involved in the election, where some authorities 
are bogus authorities.
+     *
+     * @param authorityCount number of returned authorities
+     * @param authorityThreshold minimum number of honest authorities
+     * @return list of authorities
+     */
+    public static List<Authority> spawnAuthorities(int authorityCount, int 
authorityThreshold) {
+        List<Authority> authorities = Lists.newArrayList();
+        Collection<Integer> honestAuthorityIndices = 
VotingSimulation.randomIndices(authorityCount, authorityThreshold);
+        for (int i = 0; i < authorityCount; ++i) {
+            if (honestAuthorityIndices.contains(i)) {
+                authorities.add(new Authority());
+            }
+        }
+        return authorities;
+    }
+
+
+    /**
+     * Create voters, where some voters may be malicious.
+     *
+     * @param callForVote description of the election for the voter
+     * @param voterCount number of all voters
+     * @param honestVoterCount number of honest, non-malicious voters
+     */
+    private static List<Voter> spawnVoters(CallForVoters callForVote, int 
voterCount, int honestVoterCount) {
+        List<Voter> voters = Lists.newArrayList();
+        for (int i = 0; i < voterCount; ++i) {
+            voters.add(new Voter(callForVote));
+        }
+        return voters;
+    }
+
+    /*
+     * Return between authorityThreshold and authorityCount indices
+     */
+    private static List<Integer> randomIndices(int authorityCount, int 
authorityThreshold) {
+        ArrayList<Integer> x = Lists.newArrayListWithCapacity(authorityCount);
+        for (int i = 0; i < authorityCount; ++i) {
+            x.add(i);
+        }
+        Collections.shuffle(x);
+        int len;
+        if (authorityCount == authorityThreshold) {
+            len = authorityThreshold;
+        } else {
+            len = CryptoUtil.random.nextInt(authorityCount - 
authorityThreshold) + authorityThreshold;
+        }
+        return Collections.unmodifiableList(x.subList(0, len));
+    }
+}

Added: gnunet-java/src/main/java/org/grothoff/Runabout.java
===================================================================
--- gnunet-java/src/main/java/org/grothoff/Runabout.java                        
        (rev 0)
+++ gnunet-java/src/main/java/org/grothoff/Runabout.java        2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,574 @@
+/*
+ * (C) 2002, 2003, 2004, 2005, 2006 Christian Grothoff
+ *
+ * The Runabout 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 2, or (at your option) any later version. The
+ * Runabout 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
+ * the Runabout; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * This software is also licensed under the Eclipse Public License v1.0 
+ * available at http://www.eclipse.org/legal/epl-v10.html.
+ */
+package org.grothoff;
+
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+
+/**
+ * Runabout is a fast implementation of the Walkabout which is a variant of the
+ * Visitor Pattern that does not require an accept method and uses reflection
+ * instead.
+ * <p/>
+ * An instance of Runabout is able to walk over an arbitrary object graph using
+ * visit methods which take arguments of the specific type of the object to
+ * visit. For each node in the object graph the Runabout invokes the most
+ * appropriate visit method.
+ * <p/>
+ * Using the Runabout typically involves subclassing Runabout and adding a
+ * couple of visit methods. The Runabout provides a 'visitAppropriate' method
+ * which will invoke the most appropriate visit method of the current Runabout
+ * instance. If no visit method is applicable, visitAppropriate calls
+ * visitDefault() which, if not overridden, throws an exception.
+ * <p/>
+ * The elements of the object graph typically extend the Element class, which
+ * provides a generic way to quickly invoke the Runabout on all the fields of
+ * the Element.
+ * <p/>
+ * Note that the Runabout uses dynamic code generation and dynamic loading in
+ * order to be quickly able to invoke the appropriate visit methods. To make 
the
+ * dynamic code generation fast, the code inlines parts of Java class-files in
+ * binary form (ugly!).<br>
+ * A per-thread Cache is used to speed-up the creation of the Runabout by
+ * caching reflection, code creation and dynamic loading operations.
+ * <p/>
+ * <bf>Restrictions:</bf> Java semantics require:
+ * <ul>
+ * <li>all subclasses must be public, sadly this also means that <strong>you
+ * can not use an anonymous inner class</strong> (!)</li>
+ * <li>the types_length to all arguments of visit methods must be public</li>
+ * <li>all visit methods must be public (!)</li>
+ * </ul>
+ * Otherwise the visitor will die with an IllegalAccessError during execution.
+ * <p/>
+ *
+ * @author Christian Grothoff
+ * @version 5.0
+ */
+public class Runabout {
+
+    /**
+     * Singleton of the Runabout.Cache. We cache reflective information per VM;
+     * this avoids the need for repeated reflection, code generation and
+     * dispatching map updates.
+     */
+    private static final Runabout.Cache cache_ = new Runabout.Cache();
+
+    /**
+     * map_ contains a mapping from a class to the appropriate visit method.
+     * Note that at the beginning, map_ only contains the explicit mappings as
+     * given by the visit methods. Over time, map_ will be amended to also
+     * contain direct mappings for subclasses to the appropriate visit methods
+     * if they are used.
+     */
+    private final HashMap<Class, Runabout.Code> map_;
+
+    /**
+     * Code to invoke if no visitor is found (used to avoid scanning the
+     * hierarchy again and again).
+     */
+    private final Code noCode = new NoCode();
+
+    /**
+     * Set when the subclass of the runabout is not public.
+     */
+    private final boolean isPublic;
+
+    /**
+     * Create a Runabout.
+     */
+    public Runabout() {
+        this.isPublic = Modifier.isPublic(getClass().getModifiers());
+        map_ = cache_.get(this);
+    }
+
+    /**
+     * Call the appropriate visit method. Use this method if you are visiting a
+     * graph of objects (no primitives).
+     *
+     * @param o the object to visit
+     */
+    public void visitAppropriate(Object o) {
+        getAppropriateCodeInternal(o.getClass()).visit(this, o);
+    }
+
+    /**
+     * Obtain the appropriate code to call for class c. The method either
+     * obtains the code quickly from the code map (fast path) or by calling the
+     * lookup method getAppropriateCode.
+     *
+     * @return the code, never returns null
+     */
+    private Code getAppropriateCodeInternal(Class c) {
+        synchronized (cache_) {
+            Code co = map_.get(c);
+            if (co != null)
+                return co;
+            co = getAppropriateCode(c);
+            if (co == null)
+                co = noCode;
+            map_.put(c, co);
+            return co;
+        }
+    }
+
+    /**
+     * Find the appropriate Code to call in the map. If no code is found, 
return
+     * null. This lookup strategy first attempts to find a visit method defined
+     * for the parent classes of c. If no such method exists, it attempts to
+     * find an unambiguous visit method matching any interface transitively
+     * implemented by c. If that does not exist either, null is returned. If
+     * only an ambiguous visit method exists, an exception is raised.
+     *
+     * @param c the class for which to find the code
+     * @return the code to run, or null if no code was found
+     * @throws RunaboutException if the lookup would be ambiguous
+     */
+    private Code getAppropriateCode(Class c) {
+        if (c.isArray()) {
+            // uh uh, array subtyping in action...
+            int dims = 1;
+            Class component = c.getComponentType();
+            while (component.isArray()) {
+                dims++;
+                component = component.getComponentType();
+            }
+            Class superComp = component.getSuperclass();
+            while (superComp != null) {
+                Code co = map_.get(Array.newInstance(superComp, new 
int[dims]).getClass());
+                if (co != null)
+                    return co;
+                superComp = superComp.getSuperclass();
+            }
+            // now try subtyping with multi-dimensional Object[]
+            // (see crazy runabout test).
+            Class objectClass = c.getSuperclass();
+            while (dims > 1) {
+                Code co = map_.get(Array.newInstance(objectClass, new 
int[dims]).getClass());
+                if (co != null)
+                    return co;
+                dims--;
+            }
+        }
+        Class cl = c.getSuperclass();
+        while (cl != null) {
+            Code co = map_.get(cl);
+            if (co != null)
+                return co;
+            cl = cl.getSuperclass();
+        }
+        return getAppropriateCode_ifc(c, c);
+    }
+
+    /**
+     * Find the appropriate Code to call in the map. If no code is found, 
return
+     * null.
+     *
+     * @param c  the class for which to find the code
+     * @param cl the class where to start looking from
+     * @return the code to run, or null if no code was found
+     */
+    private Code getAppropriateCode_ifc(Class c, Class cl) {
+        Code co = null;
+        while (cl != null) {
+            Class[] ifc = cl.getInterfaces();
+            for (Class anIfc : ifc) {
+                Code r = map_.get(anIfc);
+                if (r != null) {
+                    if ((co != null) && (r != co))
+                        throw new RunaboutException("Ambiguous resolution for 
visit call to "
+                                + c + " in " + this.getClass().getName());
+                    co = r;
+                }
+            }
+            for (Class anIfc : ifc) {
+                Code r = getAppropriateCode_ifc(c, anIfc);
+                if (r != null) {
+                    if ((co != null) && (r != co))
+                        throw new RunaboutException("Ambiguous resolution for 
visit call to "
+                                + c + " in " + this.getClass().getName());
+                    co = r;
+                }
+            }
+            cl = cl.getSuperclass();
+        }
+        return co;
+    }
+
+    /**
+     * Generate the initial version of the map that maps classes to Code to 
call
+     * the appropriate visit method.
+     */
+    final HashMap<Class, Runabout.Code> makeMap() {
+        // get number of methods
+        int size = 0;
+        Class me = this.getClass();
+        while (me != null) {
+            size += me.getDeclaredMethods().length;
+            me = me.getSuperclass();
+        }
+        // create map with slight over-estimate
+        HashMap<Class, Runabout.Code> result = new HashMap<Class, 
Runabout.Code>(size * 2);
+        // for all methods - create call code, put
+        me = this.getClass();
+        while (me != null) {
+            Method[] methods = me.getDeclaredMethods();
+            for (final Method m : methods) {
+                if ((m.getName().equals("visit"))
+                        && (!Modifier.isStatic(m.getModifiers()))) {
+                    Class[] args = m.getParameterTypes();
+                    if (args.length != 1)
+                        throw new RunaboutException("Invalid number of 
arguments for Runabout in method "
+                                + m);
+                    final Class viC = args[0];
+                    if (result.get(viC) != null)
+                        continue;
+                    Code co;
+                    if (isPublic) {
+                        // invoke the visitor with generated code
+                        co = makeCode(viC);
+                        if (co == null) {
+                            throw new RunaboutException("Could not create/load 
dynamic code!");
+                        }
+                    } else {
+                        if (!m.isAccessible()) {
+                            m.setAccessible(true);
+                        }
+                        // invoke the visitor with an anonymous inner class,
+                        // allows for the runabout to be public as the method 
made accessible
+                        // by the Method instance.
+                        // For Java 7+ the performance of this could be 
improved by using a MethodHandle
+                        co = new Code() {
+                            @Override
+                            public void visit(Runabout r, Object o) {
+                                try {
+
+                                    m.invoke(r, o);
+                                } catch (IllegalAccessException e) {
+                                    throw new RunaboutException(e.toString());
+                                } catch (InvocationTargetException e) {
+                                    System.err.println("stacktrace:");
+                                    e.getCause().printStackTrace(System.err);
+                                    throw new 
RunaboutException(e.getCause().toString());
+                                }
+                            }
+                        };
+                    }
+
+                    result.put(viC, co);
+
+                }
+            }
+            me = me.getSuperclass();
+        }
+        return result;
+    }
+
+    /**
+     * Create the code to invoke a visit method.
+     *
+     * @param c the type of the argument to the visit method
+     */
+    private Code makeCode(Class c) {
+        byte[] myName // Lovm/util/RunaboutExample; substitute
+                = canonicalName(getClass(), false);
+        final int myNameLen = myName.length;
+        final int myNameLenM2 = myNameLen - 2;
+        byte[] cName // Ljava/lang/String; substitute
+                = canonicalName(c, false);
+        byte[] cNameCast = canonicalName(c, true);
+        final int cNameLen = cName.length;
+        final int cNameLenCast = cNameCast.length - 2;
+        byte[] code = new byte[genCodeTemplate.length - 62 + myNameLenM2
+                + cNameLenCast + cNameLen];
+
+        // Build code by substituting a few strings in genCodeTemplate.
+        // 117-145: org/grothoff/RunaboutExample => myName
+        // 148-164: java/lang/String => cName
+        // 192-200: XXXXXXXX => number
+        // 250-271: (Ljava/lang/String;)V => "("+cName+")V"
+
+        System.arraycopy(genCodeTemplate, 0, code, 0, 115);
+        code[115] = (byte) ((myNameLenM2) >> 8);
+        code[116] = (byte) ((myNameLenM2) & 255);
+        System.arraycopy(myName, 1, code, 117, myNameLenM2);
+        code[117 + myNameLenM2] = 1; // tag for string
+        code[118 + myNameLenM2] = (byte) ((cNameLenCast) >> 8);
+        code[119 + myNameLenM2] = (byte) ((cNameLenCast) & 255);
+        System.arraycopy(cNameCast, 1, code, 120 + myNameLenM2, cNameLenCast);
+        System.arraycopy(genCodeTemplate, 164, code, 120 + myNameLenM2
+                + cNameLenCast, 248 - 164);
+        code[120 + myNameLenM2 + cNameLenCast + 248 - 164] = (byte) ((cNameLen 
+ 3) >> 8);
+        code[120 + myNameLenM2 + cNameLenCast + 249 - 164] = (byte) ((cNameLen 
+ 3) & 255);
+        code[120 + myNameLenM2 + cNameLenCast + 250 - 164] = (byte) '(';
+        System.arraycopy(cName, 0, code, 120 + myNameLenM2 + cNameLenCast + 251
+                - 164, cNameLen);
+        code[120 + myNameLenM2 + cNameLenCast + 250 - 164 + cNameLen + 1] = 
(byte) ')';
+        code[120 + myNameLenM2 + cNameLenCast + 250 - 164 + cNameLen + 2] = 
(byte) 'V';
+        System.arraycopy(genCodeTemplate,
+                271,
+                code,
+                120 + myNameLenM2 + cNameLenCast + 250 - 164
+                        + cNameLen + 3,
+                genCodeTemplate.length - 271);
+        return cache_.loadCode(code, 120 + myNameLenM2 + cNameLenCast + 192
+                - 164);
+    }
+
+    /**
+     * Get the class name in canonical form.
+     *
+     * @param cls the class, may not be primitive
+     * @return the ovm name, following the convention of
+     *         address@hidden java.util.Class.forName} according to the JavaDoc
+     *         specification (JDK 1.2.2/1.3/1.4) which differs from the actual
+     *         implementation in both SUN and IBM VMs.
+     */
+    private static byte[] canonicalName(Class cls, boolean forCast) {
+        String cname = cls.getName();
+        try {
+            byte[] utf = cname.getBytes("UTF-8");
+            int len = utf.length; // may be > cname.length()!
+            if ((cname.charAt(0) != '[') || (forCast)) {
+                byte[] ret = new byte[len + 2];
+                ret[0] = (byte) 'L';
+                System.arraycopy(utf, 0, ret, 1, len);
+                ret[len + 1] = (byte) ';';
+                for (int i = len; i > 0; i--)
+                    if (ret[i] == (byte) '.')
+                        ret[i] = (byte) '/';
+                return ret;
+            }
+            for (int i = len - 1; i >= 0; i--)
+                if (utf[i] == (byte) '.')
+                    utf[i] = (byte) '/';
+            return utf;
+        } catch (UnsupportedEncodingException uee) {
+            throw new RunaboutException("UTF8 encoding not supported!?: " + 
uee);
+        }
+    }
+
+    /**
+     * The Runabout.Cache is essentially a per-class cache of the internal
+     * constant state of a Runabout instance. It contains the generated code to
+     * quicly invoke the appropriate visit methods.
+     *
+     * @author Christian Grothoff
+     */
+    static final class Cache {
+
+        /**
+         * ClassLoader to use to load the code.
+         */
+        private final ClassLoader loader_;
+
+        /**
+         * Last name used by the class loader.
+         */
+        private final byte[] lastName_;
+
+        /**
+         * Mapping of classes to Maps.
+         */
+        private final HashMap<Class, HashMap<Class, Runabout.Code>> cachemap_;
+
+        /**
+         * Code that the loader should use.
+         */
+        byte[] code;
+
+        /**
+         * Create the Cache.
+         */
+        Cache() {
+            loader_ = new ClassLoader() {
+                public Class<?> loadClass(String name)
+                        throws ClassNotFoundException {
+                    //noinspection StringEquality
+                    if (name == "Code") // == works here, as both strings are 
guaranteed to be interned
+                        return defineClass(null, code, 0, code.length);
+                    return 
Thread.currentThread().getContextClassLoader().loadClass(name);
+                }
+            };
+            cachemap_ = new HashMap<Class, HashMap<Class, Runabout.Code>>();
+            lastName_ = new byte[8];
+            for (int i = 0; i < 8; i++)
+                lastName_[i] = (byte) '0';
+        }
+
+        /**
+         * Create a class from the given bytecode. Since classes loaded by the
+         * same Loader must have a unique name, this method patches the 
bytecode
+         * at the given offset, changing the next 8 characters to a unique Java
+         * classname.
+         *
+         * @param byteCode the bytecode of the class which must describe a 
class
+         *                 of type 'Code'. The class must contain a sequence 
XXXXXXXX at
+         *                 offset xIdx where the classname is to be patched
+         * @param xIdx     the index of the XXXXXXXX sequence
+         * @return an instance of the loaded class, null on error
+         * @throws ArrayIndexOutOfBoundsException if more than 62<sup>8</sup>
+         *                                        classes are loaded :-)
+         * @throws RunaboutException              if there are problems with 
dynamic loading
+         *                                        of the byteCode
+         */
+        Code loadCode(byte[] byteCode, int xIdx) {
+            boolean overflow = true;
+            int index = 7;
+            while (overflow) {
+                overflow = false;
+                lastName_[index]++;
+                if (lastName_[index] == (byte) ('9' + 1))
+                    lastName_[index] = (byte) 'A';
+                if (lastName_[index] == (byte) ('Z' + 1))
+                    lastName_[index] = (byte) 'a';
+                if (lastName_[index] == (byte) ('z' + 1)) {
+                    lastName_[index] = (byte) '0';
+                    overflow = true;
+                    index--;
+                }
+            }
+            System.arraycopy(lastName_, 0, byteCode, xIdx, 8);
+            code = byteCode;
+
+            Code co;
+            try {
+                co = (Code) loader_.loadClass("Code").newInstance();
+            } catch (InstantiationException ie) {
+                throw new RunaboutException(ie.toString());
+            } catch (ClassNotFoundException cnfe) {
+                throw new RunaboutException(cnfe.toString());
+            } catch (IllegalArgumentException iae) {
+                throw new RunaboutException(iae.toString());
+            } catch (ClassFormatError cfe) {
+                throw new RunaboutException(cfe.toString());
+            } catch (IllegalAccessException iae) {
+                throw new RunaboutException(iae.toString());
+            }
+            code = null; // help GC
+            return co;
+        }
+
+        /**
+         * Obtain a map from the cache.
+         */
+        synchronized HashMap<Class, Runabout.Code> get(Runabout r) {
+            Class c = r.getClass();
+            HashMap<Class, Runabout.Code> map = cachemap_.get(c);
+            if (map == null) {
+                map = r.makeMap();
+                cachemap_.put(c, map);
+            }
+            return map;
+        }
+
+    } // end of Runabout.Cache
+
+    /**
+     * Code is the generic interface that all generated classes implement. It 
is
+     * used to quickly map a given class to the appropriate visit method.
+     *
+     * @author Christian Grothoff
+     */
+    public static abstract class Code {
+        public Code() {
+        }
+
+        public abstract void visit(Runabout r, Object o);
+
+    } // end of Runabout.Code
+
+    /**
+     * Implementation of Code that is called if no visit method matches (calls
+     * visitDefault).
+     *
+     * @author Christian Grothoff
+     */
+    static final class NoCode extends Code {
+        public final void visit(Runabout r, Object o) {
+            r.visitDefault(o);
+        }
+    } // end of Runabout.NoCode
+
+    /**
+     * Override this method to provide a default behavior when no other visit
+     * matches. The Runabout semantics are to search for a visit(X) and if 
there
+     * is no match, call visitDefault(). As usual with the Runabout, visit(X)
+     * looks at classes before interfaces. By default, visitDefault throws an
+     * exception.
+     */
+    protected void visitDefault(Object o) {
+        throw new RunaboutException("No visit method defined in "
+                + this.getClass() + " for " + o.getClass());
+    }
+
+    /**
+     * Generic Exception for problems in the Runabout.
+     *
+     * @author Christian Grothoff
+     */
+    public static final class RunaboutException extends RuntimeException {
+        RunaboutException(String s) {
+            super(s);
+        }
+    }
+
+    /**
+     * Compile 'GenCodeXXXXXXXX.java' with the option '-g:none' to tell javac
+     * not to include any debugging information. This is the generated class
+     * file.
+     */
+    private final static byte genCodeTemplate[] = {-54, -2, -70, -66, 0, 0, 0,
+            49, 0, 22, 10, 0, 6, 0, 12, 7, 0, 13, 7, 0, 14, 10, 0, 2, 0, 15, 7,
+            0, 16, 7, 0, 18, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40,
+            41, 86, 1, 0, 4, 67, 111, 100, 101, 1, 0, 5, 118, 105, 115, 105,
+            116, 1, 0, 44, 40, 76, 111, 114, 103, 47, 103, 114, 111, 116, 104,
+            111, 102, 102, 47, 82, 117, 110, 97, 98, 111, 117, 116, 59, 76,
+            106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99,
+            116, 59, 41, 86, 12, 0, 7, 0, 8, 1, 0, 28, 111, 114, 103, 47, 103,
+            114, 111, 116, 104, 111, 102, 102, 47, 82, 117, 110, 97, 98, 111,
+            117, 116, 69, 120, 97, 109, 112, 108, 101, 1, 0, 16, 106, 97, 118,
+            97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 12, 0,
+            10, 0, 20, 1, 0, 28, 111, 114, 103, 47, 103, 114, 111, 116, 104,
+            111, 102, 102, 47, 71, 101, 110, 67, 111, 100, 101, 88, 88, 88, 88,
+            88, 88, 88, 88, 7, 0, 21, 1, 0, 26, 111, 114, 103, 47, 103, 114,
+            111, 116, 104, 111, 102, 102, 47, 82, 117, 110, 97, 98, 111, 117,
+            116, 36, 67, 111, 100, 101, 1, 0, 12, 73, 110, 110, 101, 114, 67,
+            108, 97, 115, 115, 101, 115, 1, 0, 21, 40, 76, 106, 97, 118, 97,
+            47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 86,
+            1, 0, 21, 111, 114, 103, 47, 103, 114, 111, 116, 104, 111, 102,
+            102, 47, 82, 117, 110, 97, 98, 111, 117, 116, 0, 33, 0, 5, 0, 6, 0,
+            0, 0, 0, 0, 2, 0, 1, 0, 7, 0, 8, 0, 1, 0, 9, 0, 0, 0, 17, 0, 1, 0,
+            1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 0, 0, 1, 0, 10, 0, 11,
+            0, 1, 0, 9, 0, 0, 0, 24, 0, 2, 0, 3, 0, 0, 0, 12, 43, -64, 0, 2,
+            44, -64, 0, 3, -74, 0, 4, -79, 0, 0, 0, 0, 0, 1, 0, 19, 0, 0, 0,
+            10, 0, 1, 0, 6, 0, 17, 0, 9, 4, 9}; // GenCodeXXXXXXXX.class
+
+    public static void main(String[] args) {
+        Runabout r = new Runabout() {
+            public void visit(String s) {
+                System.out.println("hi!!");
+            }
+        };
+        r.visitAppropriate("foo");
+    }
+
+} // end of Runabout

Added: gnunet-java/src/main/java/org/grothoff/package-info.java
===================================================================
--- gnunet-java/src/main/java/org/grothoff/package-info.java                    
        (rev 0)
+++ gnunet-java/src/main/java/org/grothoff/package-info.java    2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,4 @@
+/**
+ * Pure java implementation of single argument multiple dispatch
+ */
+package org.grothoff;

Added: gnunet-java/src/main/resources/org/gnunet/construct/MsgMap.txt
===================================================================
--- gnunet-java/src/main/resources/org/gnunet/construct/MsgMap.txt              
                (rev 0)
+++ gnunet-java/src/main/resources/org/gnunet/construct/MsgMap.txt      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,46 @@
+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|274=org.gnunet.mesh.TunnelDestroyMessage
+org.gnunet.util.GnunetMessage$Body|1=org.gnunet.util.TestMessage
+org.gnunet.util.GnunetMessage$Body|70=org.gnunet.core.NotifyInboundTrafficMessage
+org.gnunet.util.GnunetMessage$Body|273=org.gnunet.mesh.TunnelCreateMessage
+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|64=org.gnunet.core.InitMessage
+org.gnunet.util.GnunetMessage$Body|4=org.gnunet.util.Resolver$GetMessage
+org.gnunet.util.GnunetMessage$Body|65=org.gnunet.core.InitReplyMessage
+org.gnunet.util.GnunetMessage$Body|5=org.gnunet.util.Resolver$ResolverResponse
+org.gnunet.util.GnunetMessage$Body|143=org.gnunet.dht.ClientGetMessage
+org.gnunet.util.GnunetMessage$Body|67=org.gnunet.core.ConnectNotifyMessage
+org.gnunet.util.GnunetMessage$Body|142=org.gnunet.dht.ClientPutMessage
+org.gnunet.util.GnunetMessage$Body|76=org.gnunet.core.SendMessage
+org.gnunet.util.GnunetMessage$Body|286=org.gnunet.mesh.LocalAckMessage
+org.gnunet.util.GnunetMessage$Body|74=org.gnunet.core.SendMessageRequest
+org.gnunet.util.GnunetMessage$Body|75=org.gnunet.core.SendMessageReady
+org.gnunet.util.GnunetMessage$Body|153=org.gnunet.dht.MonitorStartStop
+org.gnunet.util.GnunetMessage$Body|155=org.gnunet.dht.ClientPutConfirmationMessage
+org.gnunet.util.GnunetMessage$Body|323=org.gnunet.nse.UpdateMessage
+org.gnunet.util.GnunetMessage$Body|260=org.gnunet.mesh.DataMessage
+org.gnunet.util.GnunetMessage$Body|321=org.gnunet.nse.StartMessage
+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|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
+org.gnunet.util.GnunetMessage$Body|170=org.gnunet.statistics.GetResponseMessage
+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|374=org.gnunet.transport.RequestConnectMessage
+org.gnunet.util.GnunetMessage$Body|173=org.gnunet.statistics.WatchResponseMessage
+org.gnunet.util.GnunetMessage$Body|172=org.gnunet.statistics.WatchMessage
+org.gnunet.util.GnunetMessage$Body|524=org.gnunet.consensus.ConcludeMessage
+org.gnunet.util.GnunetMessage$Body|521=org.gnunet.consensus.InsertElementMessage
+org.gnunet.util.GnunetMessage$Body|523=org.gnunet.consensus.NewElementMessage
+org.gnunet.util.GnunetMessage$Body|360=org.gnunet.transport.StartMessage
+org.gnunet.construct.MessageUnion|525=org.gnunet.consensus.ConcludeDoneMessage
+# generated 2013/08/22 21:29:40

Added: gnunet-java/src/test/java/org/gnunet/construct/ByteFillMessage.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/construct/ByteFillMessage.java         
                (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/construct/ByteFillMessage.java 
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,32 @@
+/*
+ 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 ByteFillMessage implements Message {
+    
+    @UInt32
+    public int someValue;
+
+    @FillWith @UInt8
+    public byte[] rest;
+    
+}

Added: gnunet-java/src/test/java/org/gnunet/construct/ConstructTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/construct/ConstructTest.java           
                (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/construct/ConstructTest.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,82 @@
+package org.gnunet.construct;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Random;
+
+/**
+ * @author Florian Dold
+ */
+public class ConstructTest {
+    public static class ByteFillTestMessage implements Message {
+        @FrameSize
+        @UInt32
+        public int frameSize;
+        @FillWith @UInt8
+        public byte[] bytes;
+    }
+
+    @Test
+    public void test_ByteFill() {
+        ByteFillTestMessage msg = new ByteFillTestMessage();
+        msg.bytes = new byte[]{0,1,2,3};
+        Construct.patch(msg);
+        byte[] bin = Construct.toBinary(msg);
+
+        ByteFillTestMessage msg_r = Construct.parseAs(bin, 
ByteFillTestMessage.class);
+
+        Assert.assertArrayEquals(new byte[]{0,1,2,3}, msg_r.bytes);
+    }
+
+
+    @Test
+    public void test_IntMessage() {
+        IntMessage im = new IntMessage();
+        Random r = new Random();
+        im.i1 = r.nextLong();
+        im.i2 = r.nextLong();
+        im.i3 = r.nextLong();
+        im.i4 = r.nextLong();
+        im.i5 = r.nextLong();
+        im.i6 = r.nextLong();
+        im.i7 = r.nextLong();
+        im.i8 = r.nextLong();
+
+        byte[] data = Construct.toBinary(im);
+
+        Construct.parseAs(data, IntMessage.class);
+
+        Assert.assertEquals((1+2+4+8)*2, data.length);
+        Assert.assertEquals((1+2+4+8)*2, Construct.getStaticSize(im));
+    }
+
+    @Test(expected = AssertionError.class)
+    public void test_PrivateMemberMessage() {
+        PrivateMemberMessage m1 = new PrivateMemberMessage();
+        byte[] data = Construct.toBinary(m1);
+        Construct.parseAs(data, PrivateMemberMessage.class);
+    }
+
+
+    @Test
+    public void test_variable_size() {
+        VariableSizeMessage m1 = new VariableSizeMessage();
+        m1.n1 = 2;
+        m1.n2 = 3;
+        m1.a1 = new int[]{42,43};
+        m1.a2 = new int[]{1,2,1000};
+
+        byte[] data = Construct.toBinary(m1);
+
+        VariableSizeMessage m2 = Construct.parseAs(data, 
VariableSizeMessage.class);
+
+        Assert.assertEquals(m1.n1, m2.n1);
+        Assert.assertEquals(m1.n2, m2.n2);
+        Assert.assertArrayEquals(m1.a1, m2.a1);
+        Assert.assertArrayEquals(m1.a2, m2.a2);
+    }
+
+
+}
+

Added: gnunet-java/src/test/java/org/gnunet/construct/DoubleTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/construct/DoubleTest.java              
                (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/construct/DoubleTest.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,35 @@
+package org.gnunet.construct;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public class DoubleTest {
+    public static class DoubleMessage implements Message {
+        @DoubleValue
+        public double d1;
+        @DoubleValue
+        public double d2;
+    }
+
+    @Test
+    public void test_double() {
+        DoubleMessage m = new DoubleMessage();
+        m.d1 = 1.123;
+        m.d2 = java.lang.Double.NaN;
+
+        byte[] data = Construct.toBinary(m);
+
+        DoubleMessage m2 = Construct.parseAs(data, DoubleMessage.class);
+
+        Assert.assertEquals(m.d1, m2.d1, 0);
+        Assert.assertEquals(m.d2, m2.d2, 0);
+
+        Assert.assertEquals(8+8, data.length);
+    }
+
+}

Added: gnunet-java/src/test/java/org/gnunet/construct/FillParserTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/construct/FillParserTest.java          
                (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/construct/FillParserTest.java  
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,37 @@
+package org.gnunet.construct;
+
+import org.junit.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]);
+    }
+}

Added: gnunet-java/src/test/java/org/gnunet/construct/FixedSizeTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/construct/FixedSizeTest.java           
                (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/construct/FixedSizeTest.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,52 @@
+package org.gnunet.construct;
+
+import org.junit.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)};
+        Construct.toBinary(m);
+    }
+}

Added: gnunet-java/src/test/java/org/gnunet/construct/FrameSizeTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/construct/FrameSizeTest.java           
                (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/construct/FrameSizeTest.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,50 @@
+package org.gnunet.construct;
+
+import org.junit.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);
+    }
+}

Added: gnunet-java/src/test/java/org/gnunet/construct/IntMessage.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/construct/IntMessage.java              
                (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/construct/IntMessage.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,25 @@
+package org.gnunet.construct;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public class IntMessage implements Message {
+    @UInt64
+    public long i1;
+    @UInt32
+    public long i2;
+    @UInt16
+    public long i3;
+    @UInt8
+    public long i4;
+    @Int64
+    public long i5;
+    @Int32
+    public long i6;
+    @Int16
+    public long i7;
+    @Int8
+    public long i8;
+}

Added: gnunet-java/src/test/java/org/gnunet/construct/OptionalUnionTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/construct/OptionalUnionTest.java       
                        (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/construct/OptionalUnionTest.java       
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,72 @@
+package org.gnunet.construct;
+
+import org.junit.Assert;
+import org.gnunet.util.GnunetMessage;
+import org.junit.Test;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public class OptionalUnionTest {
+    public interface TestUnion extends MessageUnion {}
+    @UnionCase(1)
+    public static class UnionMember1 implements TestUnion {
+        @ZeroTerminatedString
+        public String str;
+    }
+    @UnionCase(2)
+    public static class UnionMember2 implements TestUnion {
+        @Int32
+        public int i;
+    }
+
+    public static class OptionalUnionMessage implements Message {
+        @FrameSize
+        @UInt32
+        public int size;
+        @UInt32
+        public int tag;
+        @Union(tag = "tag", optional = true)
+        public TestUnion x;
+    }
+
+    public void setupMessageMap() {
+        MessageLoader.registerUnionCase(TestUnion.class, UnionMember1.class, 
1);
+        MessageLoader.registerUnionCase(TestUnion.class, UnionMember2.class, 
2);
+    }
+
+    @Test
+    public void test_optional_union1() {
+        setupMessageMap();
+
+        OptionalUnionMessage m = new OptionalUnionMessage();
+        UnionMember1 u1 = new UnionMember1();
+        u1.str = "foo";
+        m.x = u1;
+
+        Construct.patch(m);
+        byte[] data = Construct.toBinary(m);
+
+        System.out.println(data.length);
+
+        OptionalUnionMessage m2 = Construct.parseAs(data, 
OptionalUnionMessage.class);
+
+        Assert.assertNotNull(m2.x);
+    }
+
+    @Test
+    public void test_optional_union2() {
+        setupMessageMap();
+
+        OptionalUnionMessage m = new OptionalUnionMessage();
+
+        byte[] data = Construct.toBinary(m);
+
+        OptionalUnionMessage m2 = Construct.parseAs(data, 
OptionalUnionMessage.class);
+
+        Assert.assertNull(m2.x);
+
+    }
+}

Added: gnunet-java/src/test/java/org/gnunet/construct/PrivateMemberMessage.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/construct/PrivateMemberMessage.java    
                        (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/construct/PrivateMemberMessage.java    
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,13 @@
+package org.gnunet.construct;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public class PrivateMemberMessage implements Message {
+    @UInt32
+    public int foo;
+    @UInt32
+    private int bar;
+}

Added: gnunet-java/src/test/java/org/gnunet/construct/QueryMessage.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/construct/QueryMessage.java            
                (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/construct/QueryMessage.java    
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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.construct;
+
+public class QueryMessage implements Message {
+
+
+    @UInt8
+    public int query;
+
+    @FillWith @UInt8
+    public byte[] varsize;
+}

Added: gnunet-java/src/test/java/org/gnunet/construct/SendMessageTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/construct/SendMessageTest.java         
                (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/construct/SendMessageTest.java 
2013-08-27 17:16:18 UTC (rev 28880)
@@ -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);
+    }
+}

Added: gnunet-java/src/test/java/org/gnunet/construct/StringMessage.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/construct/StringMessage.java           
                (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/construct/StringMessage.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,32 @@
+/*
+ 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 StringMessage implements Message {
+    @UInt8
+    public int num;
+    
+    @ZeroTerminatedString
+    public String str;
+    
+    @UInt8
+    public int num2;
+}

Added: gnunet-java/src/test/java/org/gnunet/construct/StringTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/construct/StringTest.java              
                (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/construct/StringTest.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,50 @@
+package org.gnunet.construct;
+
+import org.junit.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);
+    }
+}
+

Added: gnunet-java/src/test/java/org/gnunet/construct/StringTuple.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/construct/StringTuple.java             
                (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/construct/StringTuple.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,37 @@
+package org.gnunet.construct;
+
+import com.google.common.base.Objects;
+import com.google.common.hash.HashCodes;
+
+/**
+* ...
+*
+* @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);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(str1, str2);
+    }
+}

Added: gnunet-java/src/test/java/org/gnunet/construct/VariableSizeArrayTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/construct/VariableSizeArrayTest.java   
                        (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/construct/VariableSizeArrayTest.java   
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,33 @@
+package org.gnunet.construct;
+
+import org.junit.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]);
+
+    }
+}

Added: gnunet-java/src/test/java/org/gnunet/construct/VariableSizeMessage.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/construct/VariableSizeMessage.java     
                        (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/construct/VariableSizeMessage.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,17 @@
+package org.gnunet.construct;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public class VariableSizeMessage implements Message {
+    @UInt16
+    public int n1;
+    @VariableSizeIntegerArray(lengthField = "n1", signed = false, bitSize = 16)
+    public int[] a1;
+    @UInt16
+    public int n2;
+    @VariableSizeIntegerArray(lengthField = "n2", signed = false, bitSize = 16)
+    public int[] a2;
+}

Added: gnunet-java/src/test/java/org/gnunet/core/CoreTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/core/CoreTest.java                     
        (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/core/CoreTest.java     2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,111 @@
+/*
+ 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.core;
+
+
+import org.gnunet.testing.TestingFixture;
+import org.gnunet.testing.TestingSetup;
+import org.gnunet.testing.TestingSubsystem;
+import org.gnunet.util.*;
+import org.grothoff.Runabout;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+public class CoreTest extends TestingFixture {
+    @Test(timeout = 10000)
+    public void test_core_init() {
+        Program.configureLogging("DEBUG");
+        TestingSubsystem ts = new TestingSubsystem("core");
+
+        final Wrapper<Boolean> res = new Wrapper<Boolean>(false);
+
+        final Core core = new Core(ts.getConfiguration());
+        core.observeConnect(new ConnectHandler() {
+            @Override
+            public void onConnect(PeerIdentity peerIdentity) {
+            }
+        });
+        core.init(new InitCallback() {
+            @Override
+            public void onInit(PeerIdentity myIdentity) {
+                res.value = true;
+                System.out.println("in core init");
+                assertTrue(myIdentity != null);
+                core.disconnect();
+            }
+        });
+
+        Scheduler.run();
+
+        ts.destroy();
+
+        assertTrue(res.value);
+    }
+
+
+    @Test(timeout = 10000)
+    public void test_core_echo() {
+        Program.configureLogging("DEBUG");
+        new TestingSubsystem("core");
+        new TestingSubsystem("core");
+        new TestingSubsystem("core");
+        new TestingSubsystem("core");
+        TestingSubsystem ts = new TestingSubsystem("core");
+
+        final Wrapper<Boolean> gotResponse = new Wrapper<Boolean>(false);
+
+        final Core core = new Core(ts.getConfiguration());
+        core.setMessageHandler(new Runabout() {
+            public void visit(TestMessage t) {
+                gotResponse.set(true);
+                core.disconnect();
+            }
+        });
+
+        core.init(new InitCallback() {
+            @Override
+            public void onInit(PeerIdentity myIdentity) {
+                System.out.println("in core init");
+                core.notifyTransmitReady(0, RelativeTime.FOREVER, myIdentity, 
4, new MessageTransmitter() {
+                    @Override
+                    public void transmit(Connection.MessageSink sink) {
+                        System.out.println("ntr called, calling send");
+                        sink.send(new TestMessage());
+                    }
+
+                    @Override
+                    public void handleError() {
+                        Assert.fail();
+                    }
+                });
+            }
+        });
+
+        Scheduler.run();
+
+        ts.destroy();
+
+        assertTrue(gotResponse.get());
+
+    }
+}

Added: gnunet-java/src/test/java/org/gnunet/dht/DHTTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/dht/DHTTest.java                       
        (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/dht/DHTTest.java       2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,171 @@
+/*
+ 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.dht;
+
+import static org.junit.Assert.*;
+
+import org.gnunet.testing.TestingFixture;
+import org.gnunet.testing.TestingSubsystem;
+import org.gnunet.util.*;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.EnumSet;
+import java.util.List;
+
+public class DHTTest extends TestingFixture  {
+    @Test(timeout = 1000)
+    public void test_dht_put() {
+        Program.configureLogging();
+
+        final Wrapper<Boolean> putFinished = new Wrapper<Boolean>(true);
+
+        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),
+                BlockType.TEST.val, RelativeTime.HOUR.toAbsolute(), 
RelativeTime.FOREVER, new Continuation() {
+            @Override
+            public void cont(boolean success) {
+                putFinished.set(true);
+                dht.destroy();
+            }
+        });
+
+        Scheduler.run();
+        Assert.assertTrue(putFinished.get());
+    }
+
+    @Test
+    public void test_dht_put_get() {
+        Program.configureLogging();
+
+        final Wrapper<Boolean> getFinished = new Wrapper<Boolean>(true);
+
+        TestingSubsystem ts = new TestingSubsystem("dht");
+
+        final HashCode hash1 = new HashCode("gnj-test");
+        final byte[] data = new byte[]{1, 2, 3};
+
+        final DistributedHashTable dht = new 
DistributedHashTable(ts.getConfiguration());
+        dht.put(hash1, data, 1, EnumSet.noneOf(RouteOption.class),
+                BlockType.TEST.val, RelativeTime.HOUR.toAbsolute(), 
RelativeTime.FOREVER, new Continuation() {
+            @Override
+            public void cont(boolean success) {
+                dht.startGet(RelativeTime.FOREVER, BlockType.TEST.val, hash1, 
1, EnumSet.noneOf(RouteOption.class), null, new ResultCallback() {
+                    @Override
+                    public void handleResult(AbsoluteTime expiration, HashCode 
key, List<PeerIdentity> getPath, List<PeerIdentity> putPath, BlockType type, 
byte[] recData) {
+                        assertArrayEquals(data, recData);
+                        getFinished.set(true);
+                        dht.destroy();
+                    }
+                });
+            }
+        });
+
+        Scheduler.run();
+        Assert.assertTrue(getFinished.get());
+    }
+
+
+    @Test(timeout = 500)
+    public void test_dht_monitor_put() {
+        Program.configureLogging();
+
+        final Wrapper<Integer> putMonitorCount = new Wrapper<Integer>(0);
+
+        TestingSubsystem ts = new TestingSubsystem("dht");
+
+        final HashCode hash = new HashCode("gnj-test");
+        final byte[] data1 = new byte[]{1, 2, 3};
+
+        final byte[] data2 = new byte[]{5, 4, 1, 2, 6};
+
+        final DistributedHashTable dht = new 
DistributedHashTable(ts.getConfiguration());
+
+        dht.startMonitor(BlockType.TEST.val, hash, null, null, new 
MonitorPutHandler() {
+            @Override
+            public void onPut(int options, int type, int hop_count, 
AbsoluteTimeMessage expirationTime, PeerIdentity[] putPath, HashCode key, 
byte[] data) {
+                putMonitorCount.set(putMonitorCount.get() + 1);
+                if (putMonitorCount.get() == 2) {
+                    dht.destroy();
+                }
+            }
+        });
+
+        Scheduler.addDelayed(new RelativeTime(50), new Scheduler.Task() {
+            @Override
+            public void run(Scheduler.RunContext ctx) {
+                dht.put(hash, data1, 1, EnumSet.noneOf(RouteOption.class), 
BlockType.TEST.val, RelativeTime.HOUR.toAbsolute(), RelativeTime.FOREVER, null);
+            }
+        });
+
+
+        Scheduler.addDelayed(new RelativeTime(100), new Scheduler.Task() {
+            @Override
+            public void run(Scheduler.RunContext ctx) {
+                dht.put(hash, data2, 1, EnumSet.noneOf(RouteOption.class), 
BlockType.TEST.val, RelativeTime.HOUR.toAbsolute(), RelativeTime.FOREVER, null);
+            }
+        });
+
+
+        Scheduler.run();
+        Assert.assertTrue(putMonitorCount.get() == 2);
+    }
+
+    @Test(timeout = 500)
+    public void test_dht_monitor_get() {
+        Program.configureLogging("debug");
+
+        final Wrapper<Boolean> ok = new Wrapper<Boolean>(false);
+
+        TestingSubsystem ts = new TestingSubsystem("dht");
+
+        final HashCode hash = new HashCode("gnj-test");
+
+        final DistributedHashTable dht1 = new 
DistributedHashTable(ts.getConfiguration());
+        final DistributedHashTable dht2 = new 
DistributedHashTable(ts.getConfiguration());
+
+        dht1.startMonitor(BlockType.TEST.val, hash, new MonitorGetHandler() {
+            @Override
+            public void onGet(int options, int type, int hop_count, int 
desired_replication_level, PeerIdentity[] getPath, HashCode key) {
+                System.out.println("here!");
+                ok.set(true);
+                dht2.destroy();
+                dht1.destroy();
+            }
+        }, null, null);
+
+        Scheduler.addDelayed(RelativeTime.fromMilliseconds(50), new 
Scheduler.Task() {
+            @Override
+            public void run(Scheduler.RunContext ctx) {
+                dht2.startGet(RelativeTime.FOREVER, BlockType.TEST.val, hash, 
1, EnumSet.noneOf(RouteOption.class), null, null);
+            }
+        });
+
+
+        Scheduler.run();
+        Assert.assertTrue(ok.get());
+    }
+
+
+}
+

Added: gnunet-java/src/test/java/org/gnunet/mesh/MeshTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/mesh/MeshTest.java                     
        (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/mesh/MeshTest.java     2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,47 @@
+package org.gnunet.mesh;
+
+import org.gnunet.testing.TestingFixture;
+import org.gnunet.testing.TestingSubsystem;
+import org.gnunet.util.*;
+import org.junit.Test;
+import java.lang.Void;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public class MeshTest extends TestingFixture {
+    public static class MessageHandler1 extends MeshRunabout {
+        public Mesh m1;
+        public Mesh m2;
+        public void visit(TestMessage m) {
+            m1.destroy();
+            m2.destroy();
+        }
+    }
+    //@Test
+    public void test_mesh_send() {
+        Program.configureLogging("DEBUG");
+        final TestingSubsystem ts = new TestingSubsystem("mesh");
+        Configuration cfg = ts.getConfiguration();
+        MessageHandler1 mh = new MessageHandler1();
+        final Mesh mesh1 = new Mesh(cfg, null, null, null);
+        final Mesh mesh2 = new Mesh(cfg, new InboundTunnelHandler() {
+            @Override
+            public void onInboundTunnel(Mesh.Tunnel tunnel, PeerIdentity 
initiator) {
+
+            }
+        }, new TunnelEndHandler() {
+            @Override
+            public void onTunnelEnd(Mesh.Tunnel tunnel) {
+
+            }
+        }, mh, 42);
+        mh.m1 = mesh1;
+        mh.m2 = mesh2;
+        Mesh.Tunnel<Void> tunnel = mesh2.createTunnel(null, 42, false, true, 
null);
+        tunnel.send(new TestMessage());
+        Scheduler.run();
+    }
+}

Added: gnunet-java/src/test/java/org/gnunet/nse/NSETest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/nse/NSETest.java                       
        (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/nse/NSETest.java       2013-08-27 
17:16:18 UTC (rev 28880)
@@ -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<Boolean> gotResult = new Wrapper<Boolean>(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());
+    }
+}

Added: gnunet-java/src/test/java/org/gnunet/peerinfo/PeerInfoTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/peerinfo/PeerInfoTest.java             
                (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/peerinfo/PeerInfoTest.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,41 @@
+package org.gnunet.peerinfo;
+
+import org.gnunet.hello.HelloMessage;
+import org.gnunet.testing.TestingSubsystem;
+import org.gnunet.util.*;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public class PeerInfoTest {
+
+    @Test
+    public void test_peerinfo() {
+        Program.configureLogging("debug");
+        final Wrapper<Boolean> ended = new Wrapper<Boolean>(false);
+        TestingSubsystem ts = new TestingSubsystem("peerinfo");
+        final PeerInfo peerInfo = new PeerInfo(ts.getConfiguration());
+
+        peerInfo.iterate(RelativeTime.FOREVER, false, new PeerProcessor() {
+            @Override
+            public void onPeer(PeerIdentity peerIdentity, HelloMessage hello) {
+                // we can't expect to get anything here, peerinfo is the only 
running service in this setup
+            }
+
+            @Override
+            public void onEnd() {
+                peerInfo.disconnect();
+                ended.set(true);
+            }
+        });
+
+        Scheduler.run();
+        assertTrue(ended.get());
+    }
+}

Added: gnunet-java/src/test/java/org/gnunet/statistics/StatisticsTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/statistics/StatisticsTest.java         
                (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/statistics/StatisticsTest.java 
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,180 @@
+/*
+ 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.statistics;
+
+import org.junit.Assert;
+import org.gnunet.testing.TestingFixture;
+import org.gnunet.testing.TestingSetup;
+import org.gnunet.testing.TestingSubsystem;
+import org.gnunet.util.*;
+import org.junit.Test;
+
+
+public class StatisticsTest extends TestingFixture {
+    public interface Next {
+        void next();
+    }
+
+    public void assertStatisticsGet(AssertionList assertions, Statistics stat, 
final String subsystem,
+                                    final String name,
+                                    final long expectedValue, final Next next) 
{
+        Program.configureLogging();
+        final Assertion getAssertion = assertions.create("get %s:%s -> %s", 
subsystem, name, expectedValue);
+        stat.get(RelativeTime.FOREVER, subsystem, name, new 
StatisticsReceiver() {
+                    @Override
+                    public void onReceive(String rsubsystem, String rname, 
long rvalue) {
+                        if (rsubsystem.equals(subsystem) && rname.equals(name) 
&& rvalue == expectedValue) {
+                            getAssertion.assertTrue(true);
+                        } else {
+                            getAssertion.assertTrue(false);
+                        }
+                    }
+
+                    @Override
+                    public void onTimeout() {
+                        Assert.fail();
+                    }
+
+                    @Override
+                    public void onDone() {
+                        if (next != null) {
+                            next.next();
+                        }
+                    }
+                });
+    }
+
+
+    @Test(timeout = 1000)
+    public void test_simple() {
+        Program.configureLogging("DEBUG");
+        final TestingSubsystem ts = new TestingSubsystem("statistics");
+
+        final Statistics stat = new Statistics(ts.getConfiguration());
+
+        final Wrapper<Boolean> contReached = new Wrapper<Boolean>(false);
+
+        stat.set("gnj-test", "test", 42, false);
+
+        stat.get(RelativeTime.FOREVER, "gnj-test", "test",
+                new StatisticsReceiver() {
+                    @Override
+                    public void onReceive(String subsystem, String name, long 
value) {
+                        Assert.assertEquals("gnj-test", subsystem);
+                        Assert.assertEquals("test", name);
+                        Assert.assertEquals(42, value);
+                    }
+
+                    @Override
+                    public void onTimeout() {
+
+                    }
+
+                    public void onDone() {
+                        contReached.set(true);
+                        stat.destroy();
+
+                    }
+                });
+
+        Scheduler.run();
+
+        Assert.assertTrue(contReached.get());
+    }
+
+    /**
+     * Test setting, updating and getting values with two statistics handles.
+     * This test is somewhat fragile, as we have a fixed time we wait for
+     * the values to be set in the service.
+     */
+    @Test(timeout = 1000)
+    public void test_statistics_get_set() {
+        Program.configureLogging();
+        final TestingSubsystem ts = new TestingSubsystem("statistics");
+
+        final AssertionList assertions = new AssertionList();
+
+        final Statistics stat1 = new Statistics(ts.getConfiguration());
+
+        stat1.set("gnj-test", "test1", 5, false);
+        stat1.set("gnj-test", "test2", 7, false);
+        stat1.set("gnj-test", "test3", 0, false);
+        stat1.update("gnj-test", "test3", 5, false);
+        stat1.update("gnj-test", "test3", -1, false);
+
+        Scheduler.addDelayed(RelativeTime.fromMilliseconds(200), new 
Scheduler.Task() {
+            @Override
+            public void run(Scheduler.RunContext ctx) {
+                stat1.destroy();
+                final Statistics stat2 = new Statistics(ts.getConfiguration());
+                assertStatisticsGet(assertions, stat2, "gnj-test", "test1", 5, 
new Next() {
+                    @Override
+                    public void next() {
+                        assertStatisticsGet(assertions, stat2, "gnj-test", 
"test2", 7, new Next() {
+                            @Override
+                            public void next() {
+                                assertStatisticsGet(assertions, stat2, 
"gnj-test", "test3", 4, new Next() {
+                                    @Override
+                                    public void next() {
+                                        stat2.destroy();
+                                    }
+                                });
+                            }
+                        });
+
+                    }
+                });
+            }
+        });
+
+
+        Scheduler.run();
+        assertions.assertAll();
+    }
+
+
+    @Test(timeout = 1000)
+    public void test_watch() {
+        Program.configureLogging("DEBUG");
+        final TestingSubsystem ts = new TestingSubsystem("statistics");
+
+        final Statistics stat = new Statistics(ts.getConfiguration());
+
+        stat.watch("gnj-test", "test", new StatisticsWatcher() {
+            @Override
+            public void onReceive(String subsystem, String name, long value) {
+                System.out.println("received update!");
+                stat.destroy();
+            }
+
+            @Override
+            public void onTimeout() {
+                Assert.fail();
+            }
+        });
+
+        // the set is sent after the watch, thus it is guaranteed we
+        // well get the update
+        stat.set("gnj-test", "test", 42, false);
+
+        Scheduler.run();
+    }
+}

Added: gnunet-java/src/test/java/org/gnunet/testing/TestingSetupTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/testing/TestingSetupTest.java          
                (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/testing/TestingSetupTest.java  
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,54 @@
+/*
+ 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.testing;
+
+import org.gnunet.util.Program;
+import org.junit.Test;
+
+/**
+ * @author Florian Dold
+ */
+public class TestingSetupTest {
+    static final String service = "nse";
+
+    @Test(timeout = 1000)
+    public void test_testing() {
+        Program.configureLogging();
+        // could be any service, just use statistics
+        TestingSubsystem ts = new TestingSubsystem(service);
+        String port = ts.getConfiguration().getValueString(service, 
"PORT").get();
+        org.junit.Assert.assertTrue(port != null);
+
+        ts.destroy();
+    }
+
+    @Test(expected = TestingSetup.SetupException.class)
+    public void test_no_service() {
+        new TestingSubsystem("foobar _ !!!");
+    }
+
+    @Test(timeout = 1000)
+    public void test_restart() {
+        TestingSubsystem ts = new TestingSubsystem(service);
+        ts.restart();
+        ts.destroy();
+    }
+}

Added: gnunet-java/src/test/java/org/gnunet/util/Assertion.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/util/Assertion.java                    
        (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/util/Assertion.java    2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,37 @@
+/*
+ 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;
+
+
+public class Assertion {
+    public boolean success;
+    public int asserted;
+    public String message;
+
+    public Assertion(String message) {
+        this.message = message;
+    }
+
+    public void assertTrue(boolean b) {
+        success = b;
+        asserted += 1;
+    }
+}

Added: gnunet-java/src/test/java/org/gnunet/util/AssertionList.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/util/AssertionList.java                
                (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/util/AssertionList.java        
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,30 @@
+package org.gnunet.util;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class AssertionList {
+
+    List<Assertion> asyncAssertions = new LinkedList<Assertion>();
+
+
+    public Assertion create(String message, Object... args) {
+        Assertion assertion = new Assertion(String.format(message, args));
+        asyncAssertions.add(assertion);
+        return assertion;
+    }
+
+    public void assertAll() {
+        for (Assertion assertion : asyncAssertions) {
+            if (assertion.asserted != 1) {
+                throw new AssertionError(
+                        String.format("Assertion '%s' asserted %s times", 
assertion.message, assertion.asserted));
+            }
+            if (!assertion.success) {
+                throw new AssertionError(
+                        String.format("Assertion '%s' failed", 
assertion.message));
+            }
+        }
+    }
+
+}

Added: gnunet-java/src/test/java/org/gnunet/util/ClientServerTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/util/ClientServerTest.java             
                (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/util/ClientServerTest.java     
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,251 @@
+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.io.IOException;
+import java.net.*;
+import java.nio.ByteBuffer;
+import java.nio.channels.Pipe;
+import java.nio.channels.spi.SelectorProvider;
+import java.util.ArrayList;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+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
+    public void test_testing_server() {
+        Program.configureLogging("DEBUG", null);
+
+        final TestingServer srv = new TestingServer();
+
+        final Wrapper<Boolean> gotMessage = new Wrapper<Boolean>(false);
+
+        srv.server.setHandler(new Server.MessageRunabout() {
+            public void visit(TestMessage tm) {
+                gotMessage.set(true);
+                srv.server.destroy();
+            }
+        });
+
+        Scheduler.run(new Scheduler.Task() {
+            @Override
+            public void run(Scheduler.RunContext ctx) {
+                final Client cli = srv.createClient();
+                cli.notifyTransmitReady(RelativeTime.FOREVER,true, 0, new 
MessageTransmitter() {
+                    @Override
+                    public void transmit(Connection.MessageSink sink) {
+                        System.out.println("ntr!");
+                        sink.send(new TestMessage());
+                    }
+
+                    @Override
+                    public void handleError() {
+                        Assert.fail();
+                    }
+                });
+                System.out.println("done");
+            }
+        });
+
+        Assert.assertTrue(gotMessage.get());
+    }
+
+
+    /**
+     * Test what happens when a client calls notifyTransmitReady, but does not 
send
+     * a message in the callback and disconnects.
+     */
+    @Test(timeout = 1000)
+    public void test_premature_disconnect() {
+        Program.configureLogging("DEBUG", null);
+        final TestingServer srv = new TestingServer();
+
+        srv.server.notifyDisconnect(new Server.DisconnectHandler() {
+            @Override
+            public void onDisconnect(Server.ClientHandle clientHandle) {
+                srv.server.destroy();
+            }
+        });
+
+        Scheduler.run(new Scheduler.Task() {
+            @Override
+            public void run(Scheduler.RunContext ctx) {
+                final Client cli = srv.createClient();
+                cli.notifyTransmitReady(RelativeTime.FOREVER,true, 0, new 
MessageTransmitter() {
+                    @Override
+                    public void transmit(Connection.MessageSink sink) {
+                        sink.send(new TestMessage());
+                        cli.disconnect();
+                    }
+
+                    @Override
+                    public void handleError() {
+                        Assert.fail();
+                    }
+                });
+            }
+        });
+    }
+
+
+    @Test
+    public void test_receiveDone() {
+        Program.configureLogging("DEBUG", null);
+        final TestingServer srv = new TestingServer();
+
+        final Wrapper<Integer> msgCount = new Wrapper<Integer>(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<Integer> msgCount = new Wrapper<Integer>(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<Integer> msgCount = new Wrapper<Integer>(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);
+            }
+        });
+    }
+}

Added: gnunet-java/src/test/java/org/gnunet/util/FilePipeExample.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/util/FilePipeExample.java              
                (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/util/FilePipeExample.java      
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,41 @@
+package org.gnunet.util;
+
+import java.io.File;
+import java.io.IOError;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public class FilePipeExample {
+    public static void main(String... args) {
+
+        Program.configureLogging("DEBUG", null);
+
+        final Scheduler.FilePipe fp = Scheduler.openFilePipe(new 
File("test.pipe"));
+
+
+        Scheduler.addRead(RelativeTime.FOREVER, fp.getSource(), new 
Scheduler.Task() {
+            @Override
+            public void run(Scheduler.RunContext ctx) {
+                ByteBuffer b = ByteBuffer.allocate(1);
+                b.clear();
+                try {
+                    fp.getSource().read(b);
+                } catch (IOException e) {
+                    throw new IOError(e);
+                }
+                b.flip();
+                System.out.println("got: " + b.get());
+
+                Scheduler.addRead(RelativeTime.FOREVER, fp.getSource(), this);
+
+            }
+        });
+
+        Scheduler.run();
+    }
+}

Added: gnunet-java/src/test/java/org/gnunet/util/ResolverTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/util/ResolverTest.java                 
        (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/util/ResolverTest.java 2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,92 @@
+/*
+ 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.testing.TestingSetup;
+import org.gnunet.testing.TestingSubsystem;
+import org.junit.Assert;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.InetAddress;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Florian Dold
+ */
+public class ResolverTest {
+    private static final Logger logger = LoggerFactory
+            .getLogger(ResolverTest.class);
+    @Test
+    public void test_resolver() {
+        final Wrapper<Boolean> finished1 = new Wrapper<Boolean>(true);
+        final Wrapper<Boolean> finished2 = new Wrapper<Boolean>(true);
+
+        TestingSubsystem ts = new TestingSubsystem("resolver");
+
+
+        Resolver r = Resolver.getInstance();
+        r.setConfiguration(ts.getConfiguration());
+
+        r.resolveHostname("gnunet.org", RelativeTime.FOREVER, new 
Resolver.AddressCallback() {
+            @Override
+            public void onAddress(InetAddress addr) {
+                logger.info("Hostname resolved to " + addr.getHostAddress());
+            }
+
+            @Override
+            public void onFinished() {
+                finished1.set(true);
+            }
+
+            @Override
+            public void onTimeout() {
+                fail();
+            }
+        });
+        r.resolveHostname("gnu.org", RelativeTime.FOREVER, new 
Resolver.AddressCallback() {
+            @Override
+            public void onAddress(InetAddress addr) {
+                logger.info("Hostname resolved to " + addr.getHostAddress());
+            }
+
+            @Override
+            public void onFinished() {
+                finished2.set(true);
+            }
+
+            @Override
+            public void onTimeout() {
+                fail();
+            }
+        });
+
+        Scheduler.run();
+
+        assertTrue(finished1.get());
+
+        assertTrue(finished2.get());
+    }
+}
+

Added: gnunet-java/src/test/java/org/gnunet/util/ServerExample.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/util/ServerExample.java                
                (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/util/ServerExample.java        
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,79 @@
+/*
+ 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 java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.Arrays;
+
+import static org.gnunet.util.Server.*;
+
+/**
+ * Example server implementation.
+ *
+ * @author Florian Dold
+ */
+public class ServerExample {
+
+    public static void main(String[] args) {
+        // usually servers should run inside a service, this is just for 
testing
+        new Program(args) {
+            @Override
+            public void run() {
+                Server s = new Server(Arrays.asList(new SocketAddress[]{new 
InetSocketAddress("127.0.0.1", 3456)}),
+                        RelativeTime.MINUTE,
+                        false);
+                s.setHandler(new Server.MessageRunabout() {
+                    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());
+                                System.out.println("TEST message sent");
+                                sender.receiveDone(true);
+                            }
+
+                            @Override
+                            public void handleError() {
+                                System.out.println("error talking to client!");
+                            }
+                        });
+                    }
+
+                    public void visit(UnknownMessageBody b) {
+                        System.out.println("got message of unknown type " + 
b.id);
+                    }
+                });
+
+                s.notifyDisconnect(new DisconnectHandler() {
+                    @Override
+                    public void onDisconnect(Server.ClientHandle clientHandle) 
{
+                        System.out.println("client disconnected");
+
+                    }
+                });
+
+            }
+        }.start();
+    }
+}

Added: gnunet-java/src/test/java/org/gnunet/util/StringsTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/util/StringsTest.java                  
        (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/util/StringsTest.java  2013-08-27 
17:16:18 UTC (rev 28880)
@@ -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);
+    }
+}
+

Added: gnunet-java/src/test/java/org/gnunet/util/TimeTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/util/TimeTest.java                     
        (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/util/TimeTest.java     2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,29 @@
+package org.gnunet.util;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * ...
+ *
+ * @author Florian Dold
+ */
+public class TimeTest {
+
+    @Test
+    public void test_absolute_add_subtract() {
+        AbsoluteTime t1 = AbsoluteTime.now().add(RelativeTime.FOREVER);
+        Assert.assertEquals(t1, AbsoluteTime.FOREVER);
+        Assert.assertTrue(t1.isForever());
+
+        t1 = AbsoluteTime.FOREVER.add(RelativeTime.SECOND);
+        Assert.assertEquals(t1, AbsoluteTime.FOREVER);
+        Assert.assertTrue(t1.isForever());
+
+        AbsoluteTime t2 = (new 
AbsoluteTime(123000000)).add(RelativeTime.SECOND);
+        Assert.assertEquals(124000000, t2.getMicroseconds());
+
+        AbsoluteTime t3 = (new 
AbsoluteTime(123000000)).subtract(RelativeTime.SECOND);
+        Assert.assertEquals(122000000, t3.getMicroseconds());
+    }
+}

Added: gnunet-java/src/test/java/org/gnunet/util/Wrapper.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/util/Wrapper.java                      
        (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/util/Wrapper.java      2013-08-27 
17:16:18 UTC (rev 28880)
@@ -0,0 +1,43 @@
+/*
+ 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;
+
+
+/**
+ * Wrapper, used when it is necessary to store a value in a final-variable.
+ *
+ * @param <E> type of the stored value
+ */
+public class Wrapper<E> {
+    public E value;
+    public Wrapper(E initialValue) {
+        value = initialValue;
+    }
+    public Wrapper() {
+        // default constructor
+    }
+    public void set(E newValue) {
+        value = newValue;
+    }
+    public E get() {
+        return value;
+    }
+}

Added: gnunet-java/src/test/java/org/gnunet/util/getopt/GetoptTest.java
===================================================================
--- gnunet-java/src/test/java/org/gnunet/util/getopt/GetoptTest.java            
                (rev 0)
+++ gnunet-java/src/test/java/org/gnunet/util/getopt/GetoptTest.java    
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,286 @@
+/*
+ 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 org.junit.Assert;
+import org.junit.Test;
+
+class Target {
+    @Argument(
+            action = ArgumentAction.STORE_STRING,
+            shortname = "s",
+            longname = "string",
+            argumentName = "SOME_STRING",
+            description = "just some string"
+    )
+    String someString;
+
+    @Argument(
+            action = ArgumentAction.SET,
+            shortname = "y",
+            longname = "set",
+            description = "enable, default disabled"
+    )
+    boolean set = false;
+
+    @Argument(
+            action = ArgumentAction.RESET,
+            shortname = "n",
+            longname = "reset",
+            description = "disable, default enabled"
+    )
+    boolean reset = true;
+
+
+    @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 {
+    @Test
+    public void test_str() {
+        Target t = new Target();
+        Parser p = new Parser(t);
+
+
+        t.someString = null;
+        
+        // argument directly with shortopt
+        p.parse(new String[]{"-sfoo"});
+        Assert.assertEquals("foo", t.someString);
+
+        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);
+
+        t.someString = null;
+
+        p.parse(new String[]{"--string", "foo"});
+        Assert.assertEquals("foo", t.someString);
+
+
+        t.someString = null;
+
+        // last argument counts
+        p.parse(new String[]{"--string", "bar", "--string", "foo"});
+        Assert.assertEquals("foo", t.someString);
+        
+        t.someString = null;
+        
+        boolean thrown;
+                
+        thrown = false;
+        
+        // absence of argument throws ArgumentError (longopt)
+        try {
+            p.parse(new String[]{"--string"});
+        } catch (Parser.ArgumentError e) {
+            thrown = true;
+        }
+        
+        Assert.assertTrue(thrown);
+        
+        thrown = false;
+        // absence of argument throws ArgumentError (shortopt)
+        try {
+            p.parse(new String[]{"-s"});
+        } catch (Parser.ArgumentError e) {
+            thrown = true;
+        }
+
+        Assert.assertTrue(thrown);
+        
+        t.someString = null;
+        
+        // a way to specify an empty string
+        p.parse(new String[]{"--string="});
+        Assert.assertEquals("", t.someString);
+    }
+
+    @Test
+    public void test_help() {
+        Target t = new Target();
+        Parser p = new Parser(t);
+        
+        String help = p.getHelp();
+        
+        Assert.assertTrue(help.contains("--string"));
+        Assert.assertTrue(help.contains("-s"));
+
+        Assert.assertTrue(help.contains("-y"));
+        Assert.assertTrue(help.contains("--set"));
+    }
+
+
+    @Test
+    public void test_bool() {
+        Target t = new Target();
+        Parser p = new Parser(t);
+        
+        p.parse(new String[]{"--set", "--reset"});
+        Assert.assertTrue(t.set);
+        Assert.assertTrue(!t.reset);
+        
+        t.set = false;
+        t.reset = true;
+
+        p.parse(new String[]{"-y", "-n"});
+
+        Assert.assertTrue(t.set);
+        Assert.assertTrue(!t.reset);
+
+        t.set = false;
+        t.reset = true;
+
+        p.parse(new String[]{"-yn"});
+
+        Assert.assertTrue(t.set);
+        Assert.assertTrue(!t.reset);
+    }
+    
+    @Test
+    public void test_positional() {
+        Target t = new Target();
+        Parser p = new Parser(t);
+        
+        String[] rest = p.parse(new String[]{"--string=bla", "foo", "bar", 
"--set", "--", "--reset", "baz"});
+        
+        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();
+        Parser p = new Parser(t);
+
+        String[] rest;
+
+        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);
+
+        rest = p.parse(new String[]{"--value", "7"});
+        Assert.assertEquals(7, t.intVal);
+
+        rest = p.parse(new String[]{"--value", "-7"});
+        Assert.assertEquals(-7, t.intVal);
+
+        boolean thrown = false;
+        try {
+            rest = p.parse(new String[]{"--value", "x"});
+        } catch (Parser.ArgumentError e) {
+            thrown = true;
+        }
+        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);
+
+
+        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"});
+    }
+}

Added: gnunet-java/src/test/java/org/grothoff/RunaboutBenchmark.java
===================================================================
--- gnunet-java/src/test/java/org/grothoff/RunaboutBenchmark.java               
                (rev 0)
+++ gnunet-java/src/test/java/org/grothoff/RunaboutBenchmark.java       
2013-08-27 17:16:18 UTC (rev 28880)
@@ -0,0 +1,82 @@
+/*
+ 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.grothoff;
+
+
+public class RunaboutBenchmark {
+
+    public static class MyRunabout extends Runabout {
+        public void visit(String s) {
+
+        }
+        public void visit(Integer i) {
+
+        }
+    }
+
+
+    public static void main(String[] args) {
+        final int runs = 5000000;
+
+        Runabout r = new MyRunabout();
+
+        long start = System.currentTimeMillis();
+
+        Integer integer = 42;
+        for (int i = 0; i < runs; ++i) {
+            r.visitAppropriate("foo");
+            r.visitAppropriate(integer);
+        }
+
+        long end = System.currentTimeMillis();
+
+        long duration1 = end - start;
+
+
+        Runabout r2 = new Runabout() {
+            public void visit(String s) {
+
+            }
+            public void visit(Integer i) {
+
+            }
+        };
+
+
+        start = System.currentTimeMillis();
+
+        for (int i = 0; i < runs; ++i) {
+            r2.visitAppropriate("foo");
+            r2.visitAppropriate(integer);
+        }
+
+        end = System.currentTimeMillis();
+
+        long duration2 = end - start;
+
+
+        System.out.println("Runs: " + runs);
+        System.out.println("public: " + duration1);
+        System.out.println("Anon Inner Class: " + duration2);
+        System.out.println("Overhead: " + duration2 / (double) duration1);
+
+    }
+}




reply via email to

[Prev in Thread] Current Thread [Next in Thread]