aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org')
-rw-r--r--src/main/java/org/gnunet/consensus/ConcludeCallback.java5
-rw-r--r--src/main/java/org/gnunet/consensus/ConcludeDoneMessage.java13
-rw-r--r--src/main/java/org/gnunet/consensus/ConcludeMessage.java19
-rw-r--r--src/main/java/org/gnunet/consensus/Consensus.java128
-rw-r--r--src/main/java/org/gnunet/consensus/ConsensusElement.java16
-rw-r--r--src/main/java/org/gnunet/consensus/InsertDoneCallback.java5
-rw-r--r--src/main/java/org/gnunet/consensus/InsertElementMessage.java23
-rw-r--r--src/main/java/org/gnunet/consensus/NewElementCallback.java5
-rw-r--r--src/main/java/org/gnunet/consensus/NewElementMessage.java20
-rw-r--r--src/main/java/org/gnunet/construct/Construct.java482
-rw-r--r--src/main/java/org/gnunet/construct/DoubleValue.java34
-rw-r--r--src/main/java/org/gnunet/construct/FillWith.java37
-rw-r--r--src/main/java/org/gnunet/construct/FixedSizeArray.java37
-rw-r--r--src/main/java/org/gnunet/construct/FixedSizeIntegerArray.java21
-rw-r--r--src/main/java/org/gnunet/construct/FrameSize.java34
-rw-r--r--src/main/java/org/gnunet/construct/Int16.java38
-rw-r--r--src/main/java/org/gnunet/construct/Int32.java38
-rw-r--r--src/main/java/org/gnunet/construct/Int64.java37
-rw-r--r--src/main/java/org/gnunet/construct/Int8.java40
-rw-r--r--src/main/java/org/gnunet/construct/IntegerFill.java37
-rw-r--r--src/main/java/org/gnunet/construct/Message.java29
-rw-r--r--src/main/java/org/gnunet/construct/MessageIdAnnotationProcessor.java137
-rw-r--r--src/main/java/org/gnunet/construct/MessageLoader.java215
-rw-r--r--src/main/java/org/gnunet/construct/MessageUnion.java27
-rw-r--r--src/main/java/org/gnunet/construct/MsgMap.txt46
-rw-r--r--src/main/java/org/gnunet/construct/NestedMessage.java39
-rw-r--r--src/main/java/org/gnunet/construct/ProtocolViolationException.java40
-rw-r--r--src/main/java/org/gnunet/construct/ReflectUtil.java297
-rw-r--r--src/main/java/org/gnunet/construct/UInt16.java40
-rw-r--r--src/main/java/org/gnunet/construct/UInt32.java39
-rw-r--r--src/main/java/org/gnunet/construct/UInt64.java39
-rw-r--r--src/main/java/org/gnunet/construct/UInt8.java41
-rw-r--r--src/main/java/org/gnunet/construct/Union.java37
-rw-r--r--src/main/java/org/gnunet/construct/UnionCase.java39
-rw-r--r--src/main/java/org/gnunet/construct/VariableSizeArray.java39
-rw-r--r--src/main/java/org/gnunet/construct/VariableSizeIntegerArray.java41
-rw-r--r--src/main/java/org/gnunet/construct/ZeroTerminatedString.java37
-rw-r--r--src/main/java/org/gnunet/construct/package-info.java24
-rw-r--r--src/main/java/org/gnunet/construct/parsers/DoubleParser.java75
-rw-r--r--src/main/java/org/gnunet/construct/parsers/FillParser.java126
-rw-r--r--src/main/java/org/gnunet/construct/parsers/FixedSizeArrayParser.java106
-rw-r--r--src/main/java/org/gnunet/construct/parsers/FixedSizeIntegerArrayParser.java106
-rw-r--r--src/main/java/org/gnunet/construct/parsers/IntegerFillParser.java113
-rw-r--r--src/main/java/org/gnunet/construct/parsers/IntegerParser.java95
-rw-r--r--src/main/java/org/gnunet/construct/parsers/IntegerUtil.java92
-rw-r--r--src/main/java/org/gnunet/construct/parsers/NestedParser.java119
-rw-r--r--src/main/java/org/gnunet/construct/parsers/Parser.java78
-rw-r--r--src/main/java/org/gnunet/construct/parsers/SequenceParser.java106
-rw-r--r--src/main/java/org/gnunet/construct/parsers/StringParser.java145
-rw-r--r--src/main/java/org/gnunet/construct/parsers/UnionParser.java143
-rw-r--r--src/main/java/org/gnunet/construct/parsers/VariableSizeArrayParser.java106
-rw-r--r--src/main/java/org/gnunet/construct/parsers/VariableSizeIntegerArrayParser.java103
-rw-r--r--src/main/java/org/gnunet/construct/parsers/package-info.java25
-rw-r--r--src/main/java/org/gnunet/core/ConnectHandler.java30
-rw-r--r--src/main/java/org/gnunet/core/ConnectNotifyMessage.java54
-rw-r--r--src/main/java/org/gnunet/core/Core.java347
-rw-r--r--src/main/java/org/gnunet/core/DisconnectHandler.java30
-rw-r--r--src/main/java/org/gnunet/core/DisconnectNotifyMessage.java46
-rw-r--r--src/main/java/org/gnunet/core/HeaderNotify.java30
-rw-r--r--src/main/java/org/gnunet/core/InitCallback.java30
-rw-r--r--src/main/java/org/gnunet/core/InitMessage.java45
-rw-r--r--src/main/java/org/gnunet/core/InitReplyMessage.java39
-rw-r--r--src/main/java/org/gnunet/core/MessageNotify.java28
-rw-r--r--src/main/java/org/gnunet/core/NotifyInboundTrafficMessage.java47
-rw-r--r--src/main/java/org/gnunet/core/NotifyOutboundTrafficMessage.java58
-rw-r--r--src/main/java/org/gnunet/core/RequestIdentification.java35
-rw-r--r--src/main/java/org/gnunet/core/SendMessage.java71
-rw-r--r--src/main/java/org/gnunet/core/SendMessageReady.java56
-rw-r--r--src/main/java/org/gnunet/core/SendMessageRequest.java73
-rw-r--r--src/main/java/org/gnunet/core/package-info.java24
-rw-r--r--src/main/java/org/gnunet/dht/BlockType.java81
-rw-r--r--src/main/java/org/gnunet/dht/ClientGetMessage.java51
-rw-r--r--src/main/java/org/gnunet/dht/ClientGetStopMessage.java45
-rw-r--r--src/main/java/org/gnunet/dht/ClientPutConfirmationMessage.java38
-rw-r--r--src/main/java/org/gnunet/dht/ClientPutMessage.java54
-rw-r--r--src/main/java/org/gnunet/dht/ClientResultMessage.java56
-rw-r--r--src/main/java/org/gnunet/dht/DistributedHashTable.java449
-rw-r--r--src/main/java/org/gnunet/dht/MonitorGetHandler.java30
-rw-r--r--src/main/java/org/gnunet/dht/MonitorGetMessage.java75
-rw-r--r--src/main/java/org/gnunet/dht/MonitorGetRespMessage.java72
-rw-r--r--src/main/java/org/gnunet/dht/MonitorGetResponseHandler.java31
-rw-r--r--src/main/java/org/gnunet/dht/MonitorPutHandler.java31
-rw-r--r--src/main/java/org/gnunet/dht/MonitorPutMessage.java82
-rw-r--r--src/main/java/org/gnunet/dht/MonitorStartStop.java70
-rw-r--r--src/main/java/org/gnunet/dht/ResultCallback.java46
-rw-r--r--src/main/java/org/gnunet/dht/RouteOption.java55
-rw-r--r--src/main/java/org/gnunet/dht/package-info.java24
-rw-r--r--src/main/java/org/gnunet/hello/HelloMessage.java50
-rw-r--r--src/main/java/org/gnunet/hello/package-info.java25
-rw-r--r--src/main/java/org/gnunet/mesh/ClientConnectMessage.java17
-rw-r--r--src/main/java/org/gnunet/mesh/ConnectHandler.java13
-rw-r--r--src/main/java/org/gnunet/mesh/DataMessage.java19
-rw-r--r--src/main/java/org/gnunet/mesh/DisconnectHandler.java12
-rw-r--r--src/main/java/org/gnunet/mesh/InboundTunnelHandler.java12
-rw-r--r--src/main/java/org/gnunet/mesh/LocalAckMessage.java18
-rw-r--r--src/main/java/org/gnunet/mesh/Mesh.java309
-rw-r--r--src/main/java/org/gnunet/mesh/MeshRunabout.java19
-rw-r--r--src/main/java/org/gnunet/mesh/TunnelCreateMessage.java27
-rw-r--r--src/main/java/org/gnunet/mesh/TunnelDestroyMessage.java16
-rw-r--r--src/main/java/org/gnunet/mesh/TunnelEndHandler.java10
-rw-r--r--src/main/java/org/gnunet/mesh/TunnelNotificationMessage.java22
-rw-r--r--src/main/java/org/gnunet/mesh/package-info.java25
-rw-r--r--src/main/java/org/gnunet/mq/Envelope.java36
-rw-r--r--src/main/java/org/gnunet/mq/MessageQueue.java84
-rw-r--r--src/main/java/org/gnunet/mq/NotifySentHandler.java6
-rw-r--r--src/main/java/org/gnunet/nse/NetworkSizeEstimation.java167
-rw-r--r--src/main/java/org/gnunet/nse/StartMessage.java12
-rw-r--r--src/main/java/org/gnunet/nse/UpdateMessage.java26
-rw-r--r--src/main/java/org/gnunet/nse/package-info.java24
-rw-r--r--src/main/java/org/gnunet/peerinfo/InfoEnd.java14
-rw-r--r--src/main/java/org/gnunet/peerinfo/InfoMessage.java50
-rw-r--r--src/main/java/org/gnunet/peerinfo/ListAllPeersMessage.java39
-rw-r--r--src/main/java/org/gnunet/peerinfo/ListPeerMessage.java39
-rw-r--r--src/main/java/org/gnunet/peerinfo/PeerInfo.java159
-rw-r--r--src/main/java/org/gnunet/peerinfo/PeerProcessor.java12
-rw-r--r--src/main/java/org/gnunet/peerinfo/RsaPublicKeyBinaryEncoded.java56
-rw-r--r--src/main/java/org/gnunet/peerinfo/package-info.java25
-rw-r--r--src/main/java/org/gnunet/requests/MatchingRequestContainer.java51
-rw-r--r--src/main/java/org/gnunet/requests/RequestContainer.java59
-rw-r--r--src/main/java/org/gnunet/requests/SequentialRequestContainer.java86
-rw-r--r--src/main/java/org/gnunet/requests/package-info.java25
-rw-r--r--src/main/java/org/gnunet/statistics/GetMessage.java43
-rw-r--r--src/main/java/org/gnunet/statistics/GetRequest.java31
-rw-r--r--src/main/java/org/gnunet/statistics/GetResponseEndMessage.java32
-rw-r--r--src/main/java/org/gnunet/statistics/GetResponseMessage.java49
-rw-r--r--src/main/java/org/gnunet/statistics/SetMessage.java43
-rw-r--r--src/main/java/org/gnunet/statistics/SetRequest.java48
-rw-r--r--src/main/java/org/gnunet/statistics/Statistics.java310
-rw-r--r--src/main/java/org/gnunet/statistics/StatisticsReceiver.java29
-rw-r--r--src/main/java/org/gnunet/statistics/StatisticsWatcher.java13
-rw-r--r--src/main/java/org/gnunet/statistics/WatchMessage.java40
-rw-r--r--src/main/java/org/gnunet/statistics/WatchRequest.java31
-rw-r--r--src/main/java/org/gnunet/statistics/WatchResponseMessage.java39
-rw-r--r--src/main/java/org/gnunet/statistics/package-info.java24
-rw-r--r--src/main/java/org/gnunet/testbed/Controller.java84
-rw-r--r--src/main/java/org/gnunet/testbed/ControllerEventCallback.java21
-rw-r--r--src/main/java/org/gnunet/testbed/ControllerInitMessage.java30
-rw-r--r--src/main/java/org/gnunet/testbed/ControllerProc.java89
-rw-r--r--src/main/java/org/gnunet/testbed/ControllerStatusCallback.java9
-rw-r--r--src/main/java/org/gnunet/testbed/HelperInitMessage.java37
-rw-r--r--src/main/java/org/gnunet/testbed/HelperReplyMessage.java17
-rw-r--r--src/main/java/org/gnunet/testbed/Host.java51
-rw-r--r--src/main/java/org/gnunet/testbed/Operation.java11
-rw-r--r--src/main/java/org/gnunet/testbed/OperationCompletionCallback.java11
-rw-r--r--src/main/java/org/gnunet/testbed/Peer.java57
-rw-r--r--src/main/java/org/gnunet/testbed/PeerChurnCallback.java7
-rw-r--r--src/main/java/org/gnunet/testbed/PeerCreateCallback.java7
-rw-r--r--src/main/java/org/gnunet/testing/TestingFixture.java21
-rw-r--r--src/main/java/org/gnunet/testing/TestingServer.java65
-rw-r--r--src/main/java/org/gnunet/testing/TestingSetup.java56
-rw-r--r--src/main/java/org/gnunet/testing/TestingSubsystem.java133
-rw-r--r--src/main/java/org/gnunet/testing/package-info.java27
-rw-r--r--src/main/java/org/gnunet/transport/AddressIterateMessage.java34
-rw-r--r--src/main/java/org/gnunet/transport/BlacklistCallback.java12
-rw-r--r--src/main/java/org/gnunet/transport/BlacklistInitMessage.java9
-rw-r--r--src/main/java/org/gnunet/transport/HelloUpdateCallback.java9
-rw-r--r--src/main/java/org/gnunet/transport/PeerIterateCallback.java12
-rw-r--r--src/main/java/org/gnunet/transport/RequestConnectMessage.java24
-rw-r--r--src/main/java/org/gnunet/transport/StartMessage.java32
-rw-r--r--src/main/java/org/gnunet/transport/Transport.java156
-rw-r--r--src/main/java/org/gnunet/transport/TryConnectCallback.java10
-rw-r--r--src/main/java/org/gnunet/util/ATSInformation.java37
-rw-r--r--src/main/java/org/gnunet/util/AbsoluteTime.java269
-rw-r--r--src/main/java/org/gnunet/util/AbsoluteTimeMessage.java47
-rw-r--r--src/main/java/org/gnunet/util/Cancelable.java28
-rw-r--r--src/main/java/org/gnunet/util/Client.java329
-rw-r--r--src/main/java/org/gnunet/util/Configuration.java389
-rw-r--r--src/main/java/org/gnunet/util/Connection.java696
-rw-r--r--src/main/java/org/gnunet/util/Continuation.java25
-rw-r--r--src/main/java/org/gnunet/util/GnunetMessage.java81
-rw-r--r--src/main/java/org/gnunet/util/HashCode.java94
-rw-r--r--src/main/java/org/gnunet/util/Helper.java208
-rw-r--r--src/main/java/org/gnunet/util/MessageReceiver.java41
-rw-r--r--src/main/java/org/gnunet/util/MessageTransmitter.java43
-rw-r--r--src/main/java/org/gnunet/util/PeerIdentity.java69
-rw-r--r--src/main/java/org/gnunet/util/Program.java229
-rw-r--r--src/main/java/org/gnunet/util/RelativeTime.java231
-rw-r--r--src/main/java/org/gnunet/util/RelativeTimeMessage.java55
-rw-r--r--src/main/java/org/gnunet/util/Resolver.java421
-rw-r--r--src/main/java/org/gnunet/util/RunaboutMessageReceiver.java33
-rw-r--r--src/main/java/org/gnunet/util/RunaboutUtil.java55
-rw-r--r--src/main/java/org/gnunet/util/Scheduler.java678
-rw-r--r--src/main/java/org/gnunet/util/Server.java509
-rw-r--r--src/main/java/org/gnunet/util/Service.java154
-rw-r--r--src/main/java/org/gnunet/util/Strings.java138
-rw-r--r--src/main/java/org/gnunet/util/TestMessage.java31
-rw-r--r--src/main/java/org/gnunet/util/UnknownMessageBody.java35
-rw-r--r--src/main/java/org/gnunet/util/getopt/Argument.java47
-rw-r--r--src/main/java/org/gnunet/util/getopt/ArgumentAction.java29
-rw-r--r--src/main/java/org/gnunet/util/getopt/Parser.java294
-rw-r--r--src/main/java/org/gnunet/util/getopt/package-info.java24
-rw-r--r--src/main/java/org/gnunet/util/package-info.java24
-rw-r--r--src/main/java/org/gnunet/voting/CertificateAuthorityService.java7
-rw-r--r--src/main/java/org/gnunet/voting/ElectionCallTool.java50
-rw-r--r--src/main/java/org/gnunet/voting/ElectionSpecification.java5
-rw-r--r--src/main/java/org/gnunet/voting/TallyAuthorityService.java5
-rw-r--r--src/main/java/org/gnunet/voting/VotingTool.java49
-rw-r--r--src/main/java/org/gnunet/voting/simulation/Authority.java266
-rw-r--r--src/main/java/org/gnunet/voting/simulation/Ballot.java105
-rw-r--r--src/main/java/org/gnunet/voting/simulation/BogusAuthority.java9
-rw-r--r--src/main/java/org/gnunet/voting/simulation/CallForVoters.java14
-rw-r--r--src/main/java/org/gnunet/voting/simulation/CryptoUtil.java86
-rw-r--r--src/main/java/org/gnunet/voting/simulation/Cyphertext.java19
-rw-r--r--src/main/java/org/gnunet/voting/simulation/ElectionSupervisor.java105
-rw-r--r--src/main/java/org/gnunet/voting/simulation/GroupPublicKey.java28
-rw-r--r--src/main/java/org/gnunet/voting/simulation/TallyKeyShare.java41
-rw-r--r--src/main/java/org/gnunet/voting/simulation/TransmitShareVerification.java18
-rw-r--r--src/main/java/org/gnunet/voting/simulation/Voter.java31
-rw-r--r--src/main/java/org/gnunet/voting/simulation/VotingParameters.java165
-rw-r--r--src/main/java/org/gnunet/voting/simulation/VotingSimulation.java122
-rw-r--r--src/main/java/org/grothoff/Runabout.java574
-rw-r--r--src/main/java/org/grothoff/package-info.java4
212 files changed, 16519 insertions, 0 deletions
diff --git a/src/main/java/org/gnunet/consensus/ConcludeCallback.java b/src/main/java/org/gnunet/consensus/ConcludeCallback.java
new file mode 100644
index 0000000..7660c49
--- /dev/null
+++ b/src/main/java/org/gnunet/consensus/ConcludeCallback.java
@@ -0,0 +1,5 @@
1package org.gnunet.consensus;
2
3public interface ConcludeCallback {
4 void onConcludeDone();
5}
diff --git a/src/main/java/org/gnunet/consensus/ConcludeDoneMessage.java b/src/main/java/org/gnunet/consensus/ConcludeDoneMessage.java
new file mode 100644
index 0000000..51757aa
--- /dev/null
+++ b/src/main/java/org/gnunet/consensus/ConcludeDoneMessage.java
@@ -0,0 +1,13 @@
1package org.gnunet.consensus;
2
3
4import org.gnunet.construct.MessageUnion;
5import org.gnunet.construct.UnionCase;
6
7/**
8 * Notify the client that conclude has finished.
9 * Direction: service -> client
10 */
11@UnionCase(525)
12public class ConcludeDoneMessage implements MessageUnion {
13}
diff --git a/src/main/java/org/gnunet/consensus/ConcludeMessage.java b/src/main/java/org/gnunet/consensus/ConcludeMessage.java
new file mode 100644
index 0000000..7b43928
--- /dev/null
+++ b/src/main/java/org/gnunet/consensus/ConcludeMessage.java
@@ -0,0 +1,19 @@
1package org.gnunet.consensus;
2
3import org.gnunet.construct.FillWith;
4import org.gnunet.construct.UInt16;
5import org.gnunet.construct.UInt8;
6import org.gnunet.construct.UnionCase;
7import org.gnunet.util.GnunetMessage;
8
9/**
10 * Notify the client of a new element.
11 *
12 * Direction: service -> client
13 *
14 * @author Florian Dold
15 */
16@UnionCase(524)
17public class ConcludeMessage implements GnunetMessage.Body {
18 /* empty body */
19} \ No newline at end of file
diff --git a/src/main/java/org/gnunet/consensus/Consensus.java b/src/main/java/org/gnunet/consensus/Consensus.java
new file mode 100644
index 0000000..322599c
--- /dev/null
+++ b/src/main/java/org/gnunet/consensus/Consensus.java
@@ -0,0 +1,128 @@
1package org.gnunet.consensus;
2
3import org.gnunet.mq.Envelope;
4import org.gnunet.mq.MessageQueue;
5import org.gnunet.mq.NotifySentHandler;
6import org.gnunet.util.*;
7import org.slf4j.Logger;
8import org.slf4j.LoggerFactory;
9
10/**
11 * Multi-peer set reconciliation.
12 */
13public class Consensus {
14 /**
15 * Class logger.
16 */
17 private static final Logger logger = LoggerFactory
18 .getLogger(Consensus.class);
19
20 /**
21 * Callback for new elements arriving from the service.
22 * Also used to notify of consensus failure.
23 */
24 private final NewElementCallback newElementCallback;
25
26 /**
27 * Client connected to the consensus service.
28 */
29 private Client client;
30
31 /**
32 * Called when conclude has finished.
33 */
34 private ConcludeCallback concludeCallback;
35
36 /**
37 * Message dispatch for messages from the consensus service.
38 */
39 private class ConsensusMessageReceiver extends RunaboutMessageReceiver {
40 public void visit(ConcludeDoneMessage m) {
41 if (null == concludeCallback)
42 {
43 logger.error("unexpected conclude done message");
44 return;
45 }
46 concludeCallback.onConcludeDone();
47 }
48
49 public void visit(NewElementMessage m) {
50 ConsensusElement element = new ConsensusElement();
51 element.element_type = m.element_type;
52 element.data = m.element_data;
53 newElementCallback.onNewElement(element);
54 }
55
56 @Override
57 public void handleError() {
58 newElementCallback.onNewElement(null);
59 }
60 }
61
62 /**
63 * Create a consensus session. The set being reconciled is initially
64 * empty. Only reconcile with other peers after
65 * GNUNET_CONSENSUS_reconcile has been called.
66 *
67 * @param num_peers number of peers in the session
68 * @param peers array of peers participating in this consensus session
69 * Inclusion of the local peer is optional.
70 * @param sessionId session identifier
71 * Allows a group of peers to have more than consensus session.
72 * @param newElementCallback callback, called when a new element is added to the set by
73 * another peer
74 */
75 public Consensus(Configuration cfg, int num_peers, PeerIdentity[] peers, HashCode sessionId,
76 NewElementCallback newElementCallback) {
77 client = new Client("consensus", cfg);
78 client.installReceiver(new ConsensusMessageReceiver());
79 this.newElementCallback = newElementCallback;
80 }
81
82 /**
83 * Insert an element into the consensus set.
84 *
85 * @param element element to insert in the consnesus
86 * @param idc called when the element has been sent to the service
87 */
88 public void insertElement (ConsensusElement element, final InsertDoneCallback idc) {
89 InsertElementMessage m = new InsertElementMessage();
90 m.element_data = element.data;
91 m.element_type = element.element_type;
92 Envelope ev = new Envelope(m);
93 ev.notifySent(new NotifySentHandler() {
94 @Override
95 public void onSent() {
96 idc.onInsertDone();
97 }
98 });
99 client.send(ev);
100 }
101
102 /**
103 * We are done with inserting new elements into the consensus;
104 * try to conclude the consensus within a given time window.
105 * After conclude has been called, no further elements may be
106 * inserted by the client.
107 * @param concludeCallback called when the consensus has concluded
108 */
109 public void conclude(ConcludeCallback concludeCallback) {
110 if (null == concludeCallback)
111 throw new AssertionError("conclude with empty callback");
112 if (null != this.concludeCallback)
113 throw new AssertionError("called conclude twice");
114 this.concludeCallback = concludeCallback;
115 ConcludeMessage m = new ConcludeMessage();
116 client.send(m);
117 }
118
119 /**
120 * Destroy a consensus handle.
121 * Free all state associated with
122 * it, no longer call any of the callbacks.
123 */
124 public void destroy() {
125 client.disconnect();
126 client = null;
127 }
128}
diff --git a/src/main/java/org/gnunet/consensus/ConsensusElement.java b/src/main/java/org/gnunet/consensus/ConsensusElement.java
new file mode 100644
index 0000000..846e72a
--- /dev/null
+++ b/src/main/java/org/gnunet/consensus/ConsensusElement.java
@@ -0,0 +1,16 @@
1package org.gnunet.consensus;
2
3
4public class ConsensusElement {
5 /**
6 * Type of the element.
7 * 0 <= element_type <= 2^16
8 */
9 int element_type;
10
11 /**
12 * Data for the element.
13 * 0 <= data.length <= 2^16
14 */
15 byte[] data;
16}
diff --git a/src/main/java/org/gnunet/consensus/InsertDoneCallback.java b/src/main/java/org/gnunet/consensus/InsertDoneCallback.java
new file mode 100644
index 0000000..f0e86ca
--- /dev/null
+++ b/src/main/java/org/gnunet/consensus/InsertDoneCallback.java
@@ -0,0 +1,5 @@
1package org.gnunet.consensus;
2
3public interface InsertDoneCallback {
4 void onInsertDone();
5}
diff --git a/src/main/java/org/gnunet/consensus/InsertElementMessage.java b/src/main/java/org/gnunet/consensus/InsertElementMessage.java
new file mode 100644
index 0000000..fd0ff67
--- /dev/null
+++ b/src/main/java/org/gnunet/consensus/InsertElementMessage.java
@@ -0,0 +1,23 @@
1package org.gnunet.consensus;
2
3import org.gnunet.construct.FillWith;
4import org.gnunet.construct.UInt16;
5import org.gnunet.construct.UInt8;
6import org.gnunet.construct.UnionCase;
7import org.gnunet.util.GnunetMessage;
8
9/**
10 * Send an element to the service, insert it into the consensus set.
11 *
12 * Direction: client -> service
13 *
14 * @author Florian Dold
15 */
16@UnionCase(521)
17public class InsertElementMessage implements GnunetMessage.Body {
18 @UInt16
19 public int element_type;
20 @FillWith
21 @UInt8
22 public byte[] element_data;
23} \ No newline at end of file
diff --git a/src/main/java/org/gnunet/consensus/NewElementCallback.java b/src/main/java/org/gnunet/consensus/NewElementCallback.java
new file mode 100644
index 0000000..4b07a71
--- /dev/null
+++ b/src/main/java/org/gnunet/consensus/NewElementCallback.java
@@ -0,0 +1,5 @@
1package org.gnunet.consensus;
2
3public interface NewElementCallback {
4 void onNewElement(ConsensusElement element);
5}
diff --git a/src/main/java/org/gnunet/consensus/NewElementMessage.java b/src/main/java/org/gnunet/consensus/NewElementMessage.java
new file mode 100644
index 0000000..deb3634
--- /dev/null
+++ b/src/main/java/org/gnunet/consensus/NewElementMessage.java
@@ -0,0 +1,20 @@
1package org.gnunet.consensus;
2
3import org.gnunet.construct.*;
4import org.gnunet.util.GnunetMessage;
5
6/**
7 * Notify the client of a new element.
8 *
9 * Direction: service -> client
10 *
11 * @author Florian Dold
12 */
13@UnionCase(523)
14public class NewElementMessage implements GnunetMessage.Body {
15 @UInt16
16 public int element_type;
17 @FillWith
18 @UInt8
19 public byte[] element_data;
20} \ No newline at end of file
diff --git a/src/main/java/org/gnunet/construct/Construct.java b/src/main/java/org/gnunet/construct/Construct.java
new file mode 100644
index 0000000..a74a5c2
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/Construct.java
@@ -0,0 +1,482 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23import org.gnunet.construct.parsers.*;
24import org.grothoff.Runabout;
25import org.slf4j.Logger;
26import org.slf4j.LoggerFactory;
27
28import java.lang.annotation.Annotation;
29import java.lang.reflect.Field;
30import java.lang.reflect.Modifier;
31import java.nio.ByteBuffer;
32import java.util.*;
33
34
35/*
36Wanted syntax (not fully implemented yet)
37- @(U)Int<n> => signed or unsigned fixnum, represented by n bits
38- @NestedMessage => nested message
39- @FillWith @(U)Int<n> => fill the rest of the message with the specified fixnum, annotation valid on primitive arrays
40- @FillWith @NestedMessage => fill the rest of the message with the specified fixnum, annotation valid on message arrays
41 of the wanted type
42- @VariableSizeArray(lengthField = "<field>") => same syntax as @FillWith
43- @FixedSizeArray(length = n) => same syntax as @FillWith
44- @DoubleValue / @Float => floating point number, should also work with the array annotations
45- @FrameSize => specifies the fixnum that determines the containing frame's size
46- @ZeroTerminatedString => self-explanatory
47- @Constructable => annotation on a class that implements the ConstructableMessage interface,
48 providing methods to serialize/unserialize itself.
49*/
50
51
52/**
53 * Parse and write the binary representation of java classes, as defined by org.gnunet.construct.*-annotations
54 * on their members.
55 *
56 * @author Christian Grothoff
57 * @author Florian Dold
58 */
59@SuppressWarnings("unchecked")
60public class Construct {
61 private static final Logger logger = LoggerFactory
62 .getLogger(Construct.class);
63
64
65 private static Map<Class<? extends Message>, Parser> parserCache = new HashMap<Class<? extends Message>,
66 Parser>(100);
67
68
69 /**
70 * The class Construct is not intended to be instantiated, this its constructor is private.
71 */
72 private Construct() {
73
74 }
75
76
77 /**
78 * Given a byte buffer with a message, parse it into an object of type c. The
79 * fields of the class are expected to be annotated with annotations from
80 * the construct package.
81 *
82 * @param srcBuf buffer with the serialized binary data
83 * @param c desired object type to return
84 * @return instance of the desired object type
85 */
86 public static <T extends Message> T parseAs(ByteBuffer srcBuf, Class<T> c) {
87 T m = ReflectUtil.justInstantiate(c);
88
89 try {
90 getParser(c).parse(srcBuf, 0, m, m, null);
91 } catch (ProtocolViolationException e) {
92 e.augmentPath("on " + c);
93 }
94
95 return m;
96 }
97
98 /**
99 * Given a byte array with a message, parse it into an object of type c. The
100 * fields of the class are expected to be annotated with annotations from
101 * the construct package.
102 *
103 * @param srcBuf buffer with the serialized binary data
104 * @param c desired object type to return
105 * @return instance of the desired object type
106 */
107 public static <T extends Message> T parseAs(byte[] srcBuf, Class<T> c) {
108 return parseAs(ByteBuffer.wrap(srcBuf), c);
109 }
110
111 /**
112 * Create a Parser for a sub-class of Message. The result is always cached.
113 *
114 * @param c annotated sub-class of message
115 * @return a parser
116 */
117 public static Parser getParser(Class<? extends Message> c) {
118
119 if (parserCache.containsKey(c)) {
120 return parserCache.get(c);
121 }
122
123 Parser p = getParser(c, new ParserGenerator());
124
125 parserCache.put(c, p);
126
127 return p;
128 }
129
130 private static List<Field> getMessageFields(Class c) {
131 LinkedList<Field> fields = new LinkedList<Field>(Arrays.asList(c.getDeclaredFields()));
132 while ((c = c.getSuperclass()) != null && Message.class.isAssignableFrom(c)) {
133 // fields of the superclass have to be parsed *before* the subclass
134 fields.addAll(0, Arrays.asList(c.getDeclaredFields()));
135 }
136 return fields;
137 }
138
139 private static Parser getParser(Class<? extends Message> c,
140 final ParserGenerator pg) {
141
142
143 SequenceParser parser = new SequenceParser();
144
145 if (!Modifier.isPublic(c.getModifiers())) {
146 throw new AssertionError(String.format("Construct Message %s not declared public", c));
147 }
148
149 for (Field f : getMessageFields(c)) {
150 pg.c = c;
151 Annotation[] as = f.getAnnotations();
152 if (as.length == 0 || f.isSynthetic() || Modifier.isStatic(f.getModifiers())) {
153 continue;
154 }
155 if (!Modifier.isPublic(f.getModifiers())) {
156 throw new AssertionError(String.format("Field %s of Message %s not declared public", f, c));
157 }
158 pg.field = f;
159 pg.annotations = as;
160 pg.annotationsIdx = 0;
161
162 pg.visitAppropriate(as[0]);
163
164 parser.add(pg.parser);
165 }
166
167 parser.setFrameSizePath(pg.frameSizePath);
168
169 return parser;
170 }
171
172 @SuppressWarnings("UnusedDeclaration")
173 static class ParserGenerator extends Runabout {
174
175 // the field we are currently generating a parser for
176 Field field;
177 // all annotations on the field
178 Annotation[] annotations;
179 // the index of the annotation we are supposed to process right now
180 int annotationsIdx;
181
182 // the message class for which the parser is generated
183 Class c;
184
185 // the parser we are actually generating, used by the caller, set as
186 // return value of
187 // the runabout invocation
188 Parser parser;
189
190 // where are we currently, seen from the root message object
191 List<Field> path = new LinkedList<Field>();
192
193 // path of the object that has a frame size field
194 List<Field> frameSizePath;
195
196 private ParserGenerator() {
197 }
198
199 public void visit(Union u) {
200 parser = new UnionParser(u.optional(), (Class<MessageUnion>) field.getType(),
201 ReflectUtil.getFieldPathFromString(u.tag(), c), field);
202 }
203
204 public void visit(FrameSize ts) {
205
206 frameSizePath = new LinkedList<Field>(path);
207 frameSizePath.add(field);
208
209 if (annotationsIdx != 0) {
210 throw new AssertionError(
211 "FrameSize must be the first annotation on a Field");
212 }
213
214 annotationsIdx++;
215 if (annotationsIdx >= annotations.length) {
216 throw new AssertionError(
217 "FrameSize must be followed by an numeric parser");
218 }
219 visitAppropriate(annotations[annotationsIdx]);
220
221 }
222
223 public void visit(UInt8 i) {
224 parser = new IntegerParser(1, IntegerParser.UNSIGNED, field);
225 }
226
227 public void visit(UInt16 i) {
228 parser = new IntegerParser(2, IntegerParser.UNSIGNED, field);
229 }
230
231 public void visit(UInt32 i) {
232 parser = new IntegerParser(4, IntegerParser.UNSIGNED, field);
233 }
234
235 public void visit(UInt64 i) {
236 parser = new IntegerParser(8, IntegerParser.UNSIGNED, field);
237 }
238
239 public void visit(Int8 i) {
240 parser = new IntegerParser(1, IntegerParser.SIGNED, field);
241 }
242
243 public void visit(Int16 i) {
244 parser = new IntegerParser(2, IntegerParser.SIGNED, field);
245 }
246
247 public void visit(Int32 i) {
248 parser = new IntegerParser(4, IntegerParser.SIGNED, field);
249 }
250
251 public void visit(Int64 i) {
252 parser = new IntegerParser(8, IntegerParser.SIGNED, field);
253 }
254
255
256 public void visit(ZeroTerminatedString zts) {
257 parser = new StringParser(zts.charset(), zts.optional(), field);
258 }
259
260 public void visit(IntegerFill i) {
261 parser = new IntegerFillParser(field, i.signed(), i.bitSize() / 8);
262 }
263
264 public void visit(NestedMessage n) {
265 if (!Message.class.isAssignableFrom(field.getType())) {
266 throw new AssertionError("@NestedMessage only works on messages, " + field.getType()
267 + " is not a message (origin: " + c + ")");
268 }
269
270 Field nestedField = field;
271
272 if (n.newFrame()) {
273 Parser p = getParser((Class<Message>) nestedField.getType());
274
275 parser = new NestedParser(p, n.optional(), nestedField, true);
276
277 } else {
278 Field old_f = field;
279 List<Field> old_path = new ArrayList<Field>(path);
280 Class old_c = c;
281
282 path.add(field);
283
284 Parser p = getParser((Class<Message>) nestedField.getType(), this);
285
286 path = old_path;
287 c = old_c;
288
289 parser = new NestedParser(p, n.optional(), old_f, false);
290 }
291 }
292
293 public void visit(FixedSizeArray fsa) {
294 Field f = field;
295 int elemNumber = fsa.length();
296
297 getParser((Class<? extends Message>) field.getType()
298 .getComponentType(), this);
299
300 parser = new FixedSizeArrayParser(elemNumber, parser, f);
301 }
302
303
304 public void visit(FixedSizeIntegerArray fsa) {
305 Field f = field;
306 int elemNumber = fsa.length();
307 parser = new FixedSizeIntegerArrayParser(elemNumber, fsa.signed(), fsa.bitSize() / 8, f);
308 }
309
310 public void visit(DoubleValue d) {
311 if (!field.getType().equals(java.lang.Double.TYPE)) {
312 throw new AssertionError("@DoubleValue target must be a primitive 'double' field");
313 }
314 parser = new DoubleParser(field);
315 }
316
317 public void visit(FillWith fw) {
318 annotationsIdx++;
319 // if there's no further annotation, act like there is @Nested
320 if (annotationsIdx >= annotations.length) {
321 Parser p = getParser((Class<? extends Message>) field.getType().getComponentType());
322 parser = new FillParser(p, field);
323 } else {
324 FillWithParserRunabout r = new FillWithParserRunabout(field);
325 r.visitAppropriate(annotations[annotationsIdx]);
326 if (r.p == null) {
327 throw new AssertionError();
328 }
329 parser = r.p;
330 }
331 }
332
333 public void visit(VariableSizeArray vsa) {
334 Parser p = getParser((Class<? extends Message>) field.getType()
335 .getComponentType());
336
337 if (!Message.class.isAssignableFrom(field.getType().getComponentType())) {
338 throw new AssertionError("VariableSizeArray only valid on arrays of messages.");
339 }
340
341 try {
342 parser = new VariableSizeArrayParser(p, c.getField(vsa
343 .lengthField()), field);
344
345 } catch (SecurityException e) {
346 throw new AssertionError(
347 String.format(
348 "VariableSizeArray: length field '%s' not declared public",
349 vsa.lengthField()));
350 } catch (NoSuchFieldException e) {
351 throw new AssertionError(String.format(
352 "VariableSizeArray: length field '%s' does not exist in class %s",
353 vsa.lengthField(), c));
354 }
355 }
356
357
358 public void visit(VariableSizeIntegerArray a) {
359 try {
360 parser = new VariableSizeIntegerArrayParser(c.getField(a.lengthField()), field, a.signed(), a.bitSize() / 8);
361 } catch (NoSuchFieldException e) {
362 throw new AssertionError(String.format(
363 "VariableSizeIntegerArray: length field '%s' does not exist in class %s",
364 a.lengthField(), c));
365 }
366 }
367
368 /*
369 * We override this to improve the error message, otherwise obfuscated by internal java proxy objects
370 */
371 @Override
372 public void visitDefault(Object obj) {
373 if (obj instanceof Annotation) {
374 Annotation ann = (Annotation) obj;
375 throw new AssertionError("invalid Construct annotation: " + ann.annotationType().getName());
376 } else {
377 throw new AssertionError();
378 }
379 }
380 }
381
382
383 private static class FillWithParserRunabout extends Runabout {
384 public Parser p;
385 private Field f;
386
387 public FillWithParserRunabout(Field f) {
388 this.f = f;
389 }
390
391 public void visit(Int8 x) {
392 p = new IntegerFillParser(f, true, 1);
393 }
394 public void visit(Int16 x) {
395 p = new IntegerFillParser(f, true, 2);
396 }
397 public void visit(Int32 x) {
398 p = new IntegerFillParser(f, true, 4);
399 }
400 public void visit(UInt8 x) {
401 p = new IntegerFillParser(f, false, 1);
402 }
403 public void visit(UInt16 x) {
404 p = new IntegerFillParser(f, false, 2);
405 }
406 public void visit(UInt32 x) {
407 p = new IntegerFillParser(f, false, 4);
408 }
409 public void visit(NestedMessage n) {
410 Parser componentParser = getParser((Class<? extends Message>) f.getType().getComponentType());
411 p = new FillParser(componentParser, f);
412 }
413
414 }
415
416 /**
417 * Serialize a given message object to a binary byte array. The fields of
418 * the object are expected to be annotated with annotations from the
419 * construct package.
420 *
421 * @param dstBuf where to write the binary object data
422 * @param msg object to serialize
423 * @return number of bytes written to data, -1 on error
424 */
425 public static int write(ByteBuffer dstBuf, Message msg) {
426 Parser p = getParser(msg.getClass());
427 return p.write(dstBuf, msg);
428 }
429
430 /**
431 * Compute the exact size of a serialized message.
432 *
433 * @param m object to serialize
434 * @return number of bytes required to store the message in binary form
435 */
436 public static int getSize(Message m) {
437 if (m == null) {
438 return 0;
439 }
440 Parser p = getParser(m.getClass());
441 return p.getSize(m);
442 }
443
444
445 /**
446 * Return the binary representation of the message m
447 *
448 * @param m the message to serialize
449 * @return a byte array containing the serialized message
450 */
451 public static byte[] toBinary(Message m) {
452 byte[] a = new byte[getSize(m)];
453 ByteBuffer buf = ByteBuffer.wrap(a);
454 write(buf, m);
455 return a;
456 }
457
458 /**
459 * Fill in all fields of a message that are inferable from existing information.
460 *
461 * Examples: The size field for variable size arrays, the type of unions, ...
462 *
463 * @param m the message that should be patched
464 */
465 public static void patch(Message m) {
466 Parser p = getParser(m.getClass());
467 p.patch(m, p.getSize(m), null, m);
468 }
469
470 /**
471 * Get the minimum static size for the message, determinable even if the message's members
472 * are not filled in.
473 *
474 * @param m the message of interest
475 * @return the static minimum size of the message
476 */
477 public static int getStaticSize(Message m) {
478 Parser p = getParser(m.getClass());
479 return p.getStaticSize();
480 }
481
482}
diff --git a/src/main/java/org/gnunet/construct/DoubleValue.java b/src/main/java/org/gnunet/construct/DoubleValue.java
new file mode 100644
index 0000000..0856fe8
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/DoubleValue.java
@@ -0,0 +1,34 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23import java.lang.annotation.ElementType;
24import java.lang.annotation.Retention;
25import java.lang.annotation.RetentionPolicy;
26import java.lang.annotation.Target;
27
28/**
29 * A number stored in the IEEE 754 double-precision binary floating-point format.
30 */
31@Retention(RetentionPolicy.RUNTIME)
32@Target(ElementType.FIELD)
33public @interface DoubleValue {
34}
diff --git a/src/main/java/org/gnunet/construct/FillWith.java b/src/main/java/org/gnunet/construct/FillWith.java
new file mode 100644
index 0000000..f95aa3d
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/FillWith.java
@@ -0,0 +1,37 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23import java.lang.annotation.ElementType;
24import java.lang.annotation.Retention;
25import java.lang.annotation.RetentionPolicy;
26import java.lang.annotation.Target;
27
28/**
29 * An array of messages filling the rest of the frame
30 *
31 * @author Florian Dold
32 *
33 */
34@Retention(RetentionPolicy.RUNTIME)
35@Target(ElementType.FIELD)
36public @interface FillWith {
37}
diff --git a/src/main/java/org/gnunet/construct/FixedSizeArray.java b/src/main/java/org/gnunet/construct/FixedSizeArray.java
new file mode 100644
index 0000000..699ae68
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/FixedSizeArray.java
@@ -0,0 +1,37 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23import java.lang.annotation.ElementType;
24import java.lang.annotation.Retention;
25import java.lang.annotation.RetentionPolicy;
26import java.lang.annotation.Target;
27
28/**
29 * An array of messages with static size.
30 *
31 * @author Florian Dold
32 */
33@Retention(RetentionPolicy.RUNTIME)
34@Target(ElementType.FIELD)
35public @interface FixedSizeArray {
36 int length();
37}
diff --git a/src/main/java/org/gnunet/construct/FixedSizeIntegerArray.java b/src/main/java/org/gnunet/construct/FixedSizeIntegerArray.java
new file mode 100644
index 0000000..b2418b8
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/FixedSizeIntegerArray.java
@@ -0,0 +1,21 @@
1package org.gnunet.construct;
2
3
4import java.lang.annotation.ElementType;
5import java.lang.annotation.Retention;
6import java.lang.annotation.RetentionPolicy;
7import java.lang.annotation.Target;
8
9
10/**
11 * An array of integers with static size.
12 *
13 * @author Florian Dold
14 */
15@Retention(RetentionPolicy.RUNTIME)
16@Target(ElementType.FIELD)
17public @interface FixedSizeIntegerArray {
18 int length();
19 int bitSize();
20 boolean signed();
21}
diff --git a/src/main/java/org/gnunet/construct/FrameSize.java b/src/main/java/org/gnunet/construct/FrameSize.java
new file mode 100644
index 0000000..0533d0b
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/FrameSize.java
@@ -0,0 +1,34 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23import java.lang.annotation.*;
24
25/**
26 * Marker for the field storing the size of the enclosing frame in bytes.
27 *
28 * @author Florian Dold
29 *
30 */
31@Retention(RetentionPolicy.RUNTIME)
32@Target(ElementType.FIELD)
33public @interface FrameSize {
34}
diff --git a/src/main/java/org/gnunet/construct/Int16.java b/src/main/java/org/gnunet/construct/Int16.java
new file mode 100644
index 0000000..677324b
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/Int16.java
@@ -0,0 +1,38 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23import java.lang.annotation.ElementType;
24import java.lang.annotation.Retention;
25import java.lang.annotation.RetentionPolicy;
26import java.lang.annotation.Target;
27
28/**
29 * Signed 16-bit integer value.
30 *
31 * @author Florian Dold
32 *
33 */
34@Retention(RetentionPolicy.RUNTIME)
35@Target(ElementType.FIELD)
36public @interface Int16 {
37
38}
diff --git a/src/main/java/org/gnunet/construct/Int32.java b/src/main/java/org/gnunet/construct/Int32.java
new file mode 100644
index 0000000..da993ca
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/Int32.java
@@ -0,0 +1,38 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23import java.lang.annotation.ElementType;
24import java.lang.annotation.Retention;
25import java.lang.annotation.RetentionPolicy;
26import java.lang.annotation.Target;
27
28/**
29 * Signed 32-bit integer value.
30 *
31 * @author Florian Dold
32 *
33 */
34@Retention(RetentionPolicy.RUNTIME)
35@Target(ElementType.FIELD)
36public @interface Int32 {
37
38}
diff --git a/src/main/java/org/gnunet/construct/Int64.java b/src/main/java/org/gnunet/construct/Int64.java
new file mode 100644
index 0000000..be5d940
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/Int64.java
@@ -0,0 +1,37 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23import java.lang.annotation.ElementType;
24import java.lang.annotation.Retention;
25import java.lang.annotation.RetentionPolicy;
26import java.lang.annotation.Target;
27
28/**
29 * Signed 64-bit integer value.
30 *
31 */
32
33@Retention(RetentionPolicy.RUNTIME)
34@Target(ElementType.FIELD)
35public @interface Int64 {
36
37}
diff --git a/src/main/java/org/gnunet/construct/Int8.java b/src/main/java/org/gnunet/construct/Int8.java
new file mode 100644
index 0000000..a0aaa14
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/Int8.java
@@ -0,0 +1,40 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23import java.lang.annotation.ElementType;
24import java.lang.annotation.Retention;
25import java.lang.annotation.RetentionPolicy;
26import java.lang.annotation.Target;
27
28
29/**
30 * Signed 8-bit integer value.
31 *
32 * @author Florian Dold
33 *
34 */
35
36@Retention(RetentionPolicy.RUNTIME)
37@Target(ElementType.FIELD)
38public @interface Int8 {
39
40}
diff --git a/src/main/java/org/gnunet/construct/IntegerFill.java b/src/main/java/org/gnunet/construct/IntegerFill.java
new file mode 100644
index 0000000..4564c83
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/IntegerFill.java
@@ -0,0 +1,37 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23import java.lang.annotation.ElementType;
24import java.lang.annotation.Retention;
25import java.lang.annotation.RetentionPolicy;
26import java.lang.annotation.Target;
27
28/**
29 * Fills the rest of the message with integers of the specified kind. The annotation may only be present on the
30 * last serialized field of message.
31 */
32@Retention(RetentionPolicy.RUNTIME)
33@Target(ElementType.FIELD)
34public @interface IntegerFill {
35 boolean signed();
36 int bitSize();
37}
diff --git a/src/main/java/org/gnunet/construct/Message.java b/src/main/java/org/gnunet/construct/Message.java
new file mode 100644
index 0000000..d0068b5
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/Message.java
@@ -0,0 +1,29 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23/**
24 * Base interface for all messages (anything that 'Construct' can serialize or
25 * deserialize). Really just an annotation, but also for sanity checking by the
26 * compiler.
27 */
28public interface Message {
29}
diff --git a/src/main/java/org/gnunet/construct/MessageIdAnnotationProcessor.java b/src/main/java/org/gnunet/construct/MessageIdAnnotationProcessor.java
new file mode 100644
index 0000000..ab80be3
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/MessageIdAnnotationProcessor.java
@@ -0,0 +1,137 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23import com.google.common.collect.HashBasedTable;
24import com.google.common.collect.Table;
25
26import javax.annotation.processing.*;
27import javax.lang.model.SourceVersion;
28import javax.lang.model.element.Element;
29import javax.lang.model.element.TypeElement;
30import javax.lang.model.type.TypeMirror;
31import javax.lang.model.util.Elements;
32import javax.lang.model.util.Types;
33import javax.tools.Diagnostic;
34import javax.tools.FileObject;
35import javax.tools.StandardLocation;
36import java.io.IOException;
37import java.io.Writer;
38import java.lang.Integer;
39import java.text.DateFormat;
40import java.text.SimpleDateFormat;
41import java.util.*;
42
43
44/**
45 * Creates a resource file 'MsgMap.txt' in the package 'org.gnunet.construct' of the source tree.
46 */
47@SupportedAnnotationTypes("org.gnunet.construct.UnionCase")
48@SupportedSourceVersion(SourceVersion.RELEASE_6)
49public class MessageIdAnnotationProcessor extends AbstractProcessor {
50 private final Table<String, Integer, String> idToMember = HashBasedTable.create();
51
52 @Override
53 public boolean process(Set<? extends TypeElement> typeElements, RoundEnvironment roundEnvironment) {
54 if (roundEnvironment.errorRaised()) {
55 return false;
56 }
57
58 Types types = processingEnv.getTypeUtils();
59 Elements elements = processingEnv.getElementUtils();
60
61 if (roundEnvironment.processingOver()) {
62 Filer filer = processingEnv.getFiler();
63 FileObject outfile;
64 try {
65 outfile = filer.createResource(StandardLocation.SOURCE_OUTPUT, "org.gnunet.construct", "MsgMap.txt");
66 } catch (IOException e) {
67 processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Could not create MsgMap.txt");
68 return false;
69 }
70
71 try {
72 Writer w = outfile.openWriter();
73 for (Table.Cell<String, Integer, String> cell : idToMember.cellSet()) {
74 w.write(cell.getRowKey() + '|' + cell.getColumnKey() + '=' + cell.getValue() + '\n');
75 }
76
77 DateFormat fmt = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
78
79 w.write("# generated " + fmt.format(new Date()) + '\n');
80 w.close();
81 } catch (IOException e) {
82 processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Could not write MsgMap.txt");
83 }
84
85 processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "message map written to " + outfile.toUri());
86
87 } else {
88 for (Element e : roundEnvironment.getElementsAnnotatedWith(UnionCase.class)) {
89 UnionCase ann = e.getAnnotation(UnionCase.class);
90 // get the uppermost parent class that implements MessageUnion. This is the union type.
91 // processingEnv.getElementUtils().
92 //processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "element :" + e.toString());
93 List<? extends TypeMirror> parents = processingEnv.getTypeUtils().directSupertypes(e.asType());
94 TypeMirror msg = elements.getTypeElement("org.gnunet.construct.MessageUnion").asType();
95 TypeMirror unionInterface = null;
96 for (TypeMirror p : parents) {
97 if (types.isSubtype(p, msg)) {
98 unionInterface = p;
99 break;
100 }
101 }
102 if (unionInterface == null) {
103 processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, String.format(
104 "class %s annotated with @UnionCase does not implement an interface inheriting MessageUnion", e.getSimpleName()));
105 return false;
106 }
107 String unionName = getClassName(types.asElement(unionInterface));
108 idToMember.put(unionName, ann.value(), getClassName(e));
109 }
110 }
111
112 return false;
113 }
114
115 /**
116 * Get the fully qualified class name, where packages are seperated with '.', and
117 * inner classes are separated with '$'
118 *
119 * @param e the Element representing a class
120 * @return the fully qualified class name
121 */
122 private String getClassName(Element e) {
123
124 assert e.getKind().isClass();
125
126 String name = e.getSimpleName().toString();
127 String pkg = processingEnv.getElementUtils().getPackageOf(e).getQualifiedName().toString() + '.';
128
129 String outer = "";
130
131 while (((e = e.getEnclosingElement()) != null) && e.getKind().isClass()) {
132 outer = String.format("%s$%s", e.getSimpleName(), outer);
133 }
134
135 return pkg + outer + name;
136 }
137}
diff --git a/src/main/java/org/gnunet/construct/MessageLoader.java b/src/main/java/org/gnunet/construct/MessageLoader.java
new file mode 100644
index 0000000..71e2719
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/MessageLoader.java
@@ -0,0 +1,215 @@
1/*
2 *
3 * This file is part of GNUnet.
4 * (C) 2011 Christian Grothoff (and other contributing authors)
5 *
6 * GNUnet is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published
8 * by the Free Software Foundation; either version 2, or (at your
9 * option) any later version.
10 *
11 * GNUnet is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GNUnet; see the file COPYING. If not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 *
21 */
22
23package org.gnunet.construct;
24
25
26import com.google.common.base.Charsets;
27import org.slf4j.Logger;
28import org.slf4j.LoggerFactory;
29
30import java.io.BufferedReader;
31import java.io.Closeable;
32import java.io.IOException;
33import java.io.InputStreamReader;
34import java.net.URL;
35import java.util.Enumeration;
36import java.util.HashMap;
37import java.util.Map;
38
39
40/**
41 * Load message maps, which contain the information the parse/write unions.
42 */
43public class MessageLoader {
44 private static final Logger logger = LoggerFactory
45 .getLogger(MessageLoader.class);
46
47
48 /**
49 * Thrown when a trying to serialize an object that is not registered as a union type.
50 */
51 public static class UnknownUnionException extends RuntimeException {
52 public UnknownUnionException(String msg) {
53 super(msg);
54 }
55 }
56
57
58 /**
59 * Thrown when parsing a union whose ID is not known.
60 */
61 public static class UnknownUnionIdException extends RuntimeException {
62
63 }
64
65 /**
66 * Maps a class and tag to the corresponding union case.
67 * <p/>
68 * XXX: how much of generics is too much?
69 */
70 private static Map<Class<? extends MessageUnion>, Map<Integer, Class<? extends MessageUnion>>> unionmap
71 = new HashMap<Class<? extends MessageUnion>, Map<Integer, Class<? extends MessageUnion>>>(100);
72
73 /*
74 * Maps a union interface and union case to the corresponding tag.
75 */
76 private static Map<Class<? extends MessageUnion>, Map<Class<? extends MessageUnion>, Integer>> tagmap
77 = new HashMap<Class<? extends MessageUnion>, Map<Class<? extends MessageUnion>, Integer>>(100);
78
79
80 static {
81 ClassLoader classLoader = MessageLoader.class.getClassLoader();
82 Enumeration<URL> resources;
83 try {
84 resources = classLoader.getResources("org/gnunet/construct/MsgMap.txt");
85 } catch (IOException e) {
86 throw new RuntimeException("something went wrong with loading MsgMap.txt");
87 }
88
89 while (resources.hasMoreElements()) {
90 loadMessageMap(resources.nextElement());
91 }
92
93 if (tagmap.isEmpty()) {
94 logger.warn("message map empty");
95 }
96
97 }
98
99 public static void loadMessageMap(URL loc) {
100 if (loc == null) {
101 throw new RuntimeException("could not load message map");
102 }
103 BufferedReader in = null;
104 try {
105 in = new BufferedReader(new InputStreamReader(loc.openStream(), Charsets.UTF_8));
106 String line;
107 while ((line = in.readLine()) != null) {
108 // skip empty lines and comments
109 if (line.isEmpty() || line.charAt(0) == '#') {
110 continue;
111 }
112 String[] m = line.split("=");
113 if (m.length != 2) {
114 throw new RuntimeException("invalid message map format (separation by '=')");
115 }
116 String[] left = m[0].split("[|]");
117 if (left.length != 2) {
118 logger.debug(m[0]);
119 logger.debug(m[1]);
120 logger.debug("split in " + left.length);
121 throw new RuntimeException("invalid message map format (left hand side)");
122 }
123 int id = java.lang.Integer.parseInt(left[1].trim());
124 String unionCaseName = m[1].trim();
125 String unionInterfaceName = left[0];
126
127 Class<? extends MessageUnion> unionInterface = loadClass(unionInterfaceName);
128 Class<? extends MessageUnion> unionCase = loadClass(unionCaseName);
129
130 if (!unionmap.containsKey(unionInterface)) {
131 unionmap.put(unionInterface, new HashMap<Integer, Class<? extends MessageUnion>>(5));
132 }
133 unionmap.get(unionInterface).put(id, unionCase);
134
135
136 if (!tagmap.containsKey(unionInterface)) {
137 tagmap.put(unionInterface, new HashMap<Class<? extends MessageUnion>, Integer>(5));
138 }
139 tagmap.get(unionInterface).put(unionCase, id);
140
141 }
142 } catch (IOException e) {
143 throw new RuntimeException("could not read message map");
144 } finally {
145 maybeClose(in);
146 }
147 }
148
149 private static void maybeClose(Closeable in) {
150 try {
151 if (in != null) {
152 in.close();
153 }
154 } catch (IOException e) {
155 throw new RuntimeException("error closing stream: " + e.getMessage());
156 }
157 }
158
159
160 @SuppressWarnings("unchecked")
161 private static Class<? extends MessageUnion> loadClass(String className) {
162 ClassLoader cl = Thread.currentThread().getContextClassLoader();
163 Class<MessageUnion> msgClass;
164 try {
165 msgClass = (Class<MessageUnion>) cl.loadClass(className);
166 } catch (ClassNotFoundException e) {
167 throw new AssertionError(String.format("message class '%s' not found in classpath", className));
168 } catch (ClassCastException e) {
169 throw new AssertionError(String.format("Class %s does not inherit from MessageUnion", className));
170 }
171 return msgClass;
172 }
173
174 public static Class<? extends MessageUnion> getUnionClass(Class<? extends MessageUnion> unionInterface, int tag) {
175 Map<Integer, Class<? extends MessageUnion>> map = unionmap.get(unionInterface);
176 if (map == null) {
177 throw new UnknownUnionException("don't know how to handle unions of type '" + unionInterface + "'");
178 }
179
180 Class<? extends MessageUnion> cls = map.get(tag);
181 if (cls == null) {
182 throw new ProtocolViolationException("don't know how to translate message of type " + tag);
183 }
184
185 return cls;
186 }
187
188
189 public static int getUnionTag(Class<? extends MessageUnion> unionInterface, Class<? extends MessageUnion> unionCase) {
190 Map<Class<? extends MessageUnion>, Integer> map = tagmap.get(unionInterface);
191 if (map == null) {
192 throw new AssertionError(String.format("%s is not a known union type", unionInterface));
193 }
194 if (!map.containsKey(unionCase)) {
195 throw new AssertionError(String.format("%s is not a known instance of %s", unionCase, unionInterface));
196 }
197 return map.get(unionCase);
198 }
199
200 public static void registerUnionCase(Class<? extends MessageUnion> unionInterface,
201 Class<? extends MessageUnion> unionCase, int tag) {
202 if (!unionmap.containsKey(unionInterface)) {
203 unionmap.put(unionInterface, new HashMap<Integer, Class<? extends MessageUnion>>(5));
204 }
205 unionmap.get(unionInterface).put(tag, unionCase);
206
207
208 if (!tagmap.containsKey(unionInterface)) {
209 tagmap.put(unionInterface, new HashMap<Class<? extends MessageUnion>, Integer>(5));
210 }
211 tagmap.get(unionInterface).put(unionCase, tag);
212
213
214 }
215}
diff --git a/src/main/java/org/gnunet/construct/MessageUnion.java b/src/main/java/org/gnunet/construct/MessageUnion.java
new file mode 100644
index 0000000..0481df2
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/MessageUnion.java
@@ -0,0 +1,27 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23/**
24 * Marker interface for message unions
25 */
26public interface MessageUnion extends Message {
27}
diff --git a/src/main/java/org/gnunet/construct/MsgMap.txt b/src/main/java/org/gnunet/construct/MsgMap.txt
new file mode 100644
index 0000000..30a4656
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/MsgMap.txt
@@ -0,0 +1,46 @@
1org.gnunet.util.Resolver$Address|0=org.gnunet.util.Resolver$TextualAddress
2org.gnunet.util.Resolver$Address|1=org.gnunet.util.Resolver$NumericAddress
3org.gnunet.util.GnunetMessage$Body|68=org.gnunet.core.DisconnectNotifyMessage
4org.gnunet.util.GnunetMessage$Body|274=org.gnunet.mesh.TunnelDestroyMessage
5org.gnunet.util.GnunetMessage$Body|1=org.gnunet.util.TestMessage
6org.gnunet.util.GnunetMessage$Body|70=org.gnunet.core.NotifyInboundTrafficMessage
7org.gnunet.util.GnunetMessage$Body|273=org.gnunet.mesh.TunnelCreateMessage
8org.gnunet.util.GnunetMessage$Body|71=org.gnunet.core.NotifyOutboundTrafficMessage
9org.gnunet.util.GnunetMessage$Body|272=org.gnunet.mesh.ClientConnectMessage
10org.gnunet.util.GnunetMessage$Body|64=org.gnunet.core.InitMessage
11org.gnunet.util.GnunetMessage$Body|4=org.gnunet.util.Resolver$GetMessage
12org.gnunet.util.GnunetMessage$Body|65=org.gnunet.core.InitReplyMessage
13org.gnunet.util.GnunetMessage$Body|5=org.gnunet.util.Resolver$ResolverResponse
14org.gnunet.util.GnunetMessage$Body|143=org.gnunet.dht.ClientGetMessage
15org.gnunet.util.GnunetMessage$Body|67=org.gnunet.core.ConnectNotifyMessage
16org.gnunet.util.GnunetMessage$Body|142=org.gnunet.dht.ClientPutMessage
17org.gnunet.util.GnunetMessage$Body|76=org.gnunet.core.SendMessage
18org.gnunet.util.GnunetMessage$Body|286=org.gnunet.mesh.LocalAckMessage
19org.gnunet.util.GnunetMessage$Body|74=org.gnunet.core.SendMessageRequest
20org.gnunet.util.GnunetMessage$Body|75=org.gnunet.core.SendMessageReady
21org.gnunet.util.GnunetMessage$Body|153=org.gnunet.dht.MonitorStartStop
22org.gnunet.util.GnunetMessage$Body|155=org.gnunet.dht.ClientPutConfirmationMessage
23org.gnunet.util.GnunetMessage$Body|323=org.gnunet.nse.UpdateMessage
24org.gnunet.util.GnunetMessage$Body|260=org.gnunet.mesh.DataMessage
25org.gnunet.util.GnunetMessage$Body|321=org.gnunet.nse.StartMessage
26org.gnunet.util.GnunetMessage$Body|144=org.gnunet.dht.ClientGetStopMessage
27org.gnunet.util.GnunetMessage$Body|145=org.gnunet.dht.ClientResultMessage
28org.gnunet.util.GnunetMessage$Body|332=org.gnunet.peerinfo.InfoMessage
29org.gnunet.util.GnunetMessage$Body|333=org.gnunet.peerinfo.InfoEnd
30org.gnunet.util.GnunetMessage$Body|149=org.gnunet.dht.MonitorGetMessage
31org.gnunet.util.GnunetMessage$Body|331=org.gnunet.peerinfo.ListAllPeersMessage
32org.gnunet.util.GnunetMessage$Body|150=org.gnunet.dht.MonitorGetRespMessage
33org.gnunet.util.GnunetMessage$Body|151=org.gnunet.dht.MonitorPutMessage
34org.gnunet.util.GnunetMessage$Body|171=org.gnunet.statistics.GetResponseEndMessage
35org.gnunet.util.GnunetMessage$Body|170=org.gnunet.statistics.GetResponseMessage
36org.gnunet.util.GnunetMessage$Body|169=org.gnunet.statistics.GetMessage
37org.gnunet.util.GnunetMessage$Body|168=org.gnunet.statistics.SetMessage
38org.gnunet.util.GnunetMessage$Body|374=org.gnunet.transport.RequestConnectMessage
39org.gnunet.util.GnunetMessage$Body|173=org.gnunet.statistics.WatchResponseMessage
40org.gnunet.util.GnunetMessage$Body|172=org.gnunet.statistics.WatchMessage
41org.gnunet.util.GnunetMessage$Body|524=org.gnunet.consensus.ConcludeMessage
42org.gnunet.util.GnunetMessage$Body|521=org.gnunet.consensus.InsertElementMessage
43org.gnunet.util.GnunetMessage$Body|523=org.gnunet.consensus.NewElementMessage
44org.gnunet.util.GnunetMessage$Body|360=org.gnunet.transport.StartMessage
45org.gnunet.construct.MessageUnion|525=org.gnunet.consensus.ConcludeDoneMessage
46# generated 2013/08/22 21:14:59
diff --git a/src/main/java/org/gnunet/construct/NestedMessage.java b/src/main/java/org/gnunet/construct/NestedMessage.java
new file mode 100644
index 0000000..fe65323
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/NestedMessage.java
@@ -0,0 +1,39 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23import java.lang.annotation.ElementType;
24import java.lang.annotation.Retention;
25import java.lang.annotation.RetentionPolicy;
26import java.lang.annotation.Target;
27
28/**
29 * Embed another constructable message.
30 *
31 * @author Florian Dold
32 *
33 */
34@Retention(RetentionPolicy.RUNTIME)
35@Target(ElementType.FIELD)
36public @interface NestedMessage {
37 boolean newFrame() default false;
38 boolean optional() default false;
39} \ No newline at end of file
diff --git a/src/main/java/org/gnunet/construct/ProtocolViolationException.java b/src/main/java/org/gnunet/construct/ProtocolViolationException.java
new file mode 100644
index 0000000..9de46d4
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/ProtocolViolationException.java
@@ -0,0 +1,40 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23
24import java.util.LinkedList;
25
26/**
27 * Thrown when a received message is invalid.
28 *
29 * @author Florian Dold
30 *
31 */
32public class ProtocolViolationException extends RuntimeException {
33 public ProtocolViolationException(String s) {
34 super(s);
35 }
36
37 public ProtocolViolationException augmentPath(String pathMessage) {
38 return new ProtocolViolationException(this.getMessage() + "\n" + pathMessage);
39 }
40}
diff --git a/src/main/java/org/gnunet/construct/ReflectUtil.java b/src/main/java/org/gnunet/construct/ReflectUtil.java
new file mode 100644
index 0000000..1115641
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/ReflectUtil.java
@@ -0,0 +1,297 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23
24import java.lang.Integer;
25import java.lang.reflect.Array;
26import java.lang.reflect.Field;
27import java.lang.reflect.InvocationTargetException;
28import java.math.BigInteger;
29import java.util.ArrayList;
30import java.util.List;
31
32/**
33 * Utilities for convenient use of the java reflection API.
34 * All methods only throw non-checked exceptions.
35 */
36public class ReflectUtil {
37 public static <T> T justInstantiate(Class<T> c) {
38 try {
39 return c.getConstructor().newInstance();
40 } catch (InstantiationException e) {
41 throw new AssertionError("Cannot instantiate " + c);
42 } catch (IllegalAccessException e) {
43 throw new AssertionError(
44 String.format("Cannot instantiate Message %s (illegal access)", c));
45 } catch (NoSuchMethodException e) {
46 if (c.isMemberClass()) {
47 throw new AssertionError(String.format("Can not instantiate non-static member class %s", c));
48 } else {
49 throw new AssertionError(
50 String.format("No suitable default constructor for class %s", c));
51 }
52 } catch (InvocationTargetException e) {
53 throw new AssertionError(
54 String.format("Exception thrown while constructing object of class %s", c));
55 }
56 }
57
58 public static void justSetArray(Object arr, int i, long v) {
59 Class t = arr.getClass().getComponentType();
60 if (t.equals(Long.TYPE)) {
61 Array.setLong(arr, i, v);
62 } else if (t.equals(Integer.TYPE)) {
63 Array.setInt(arr, i, (int) v);
64 } else if (t.equals(Short.TYPE)) {
65 Array.setShort(arr, i, (short) v);
66 } else if (t.equals(Byte.TYPE)) {
67 Array.setByte(arr, i, (byte) v);
68 } else if (t.equals(Character.TYPE)) {
69 Array.setChar(arr, i, (char) v);
70 }
71 }
72
73 public static long justGetArrayLong(Object arr, int i) {
74 return Array.getLong(arr, i);
75 }
76
77 /**
78 * An enumeration of all built-in type that can store integers.
79 */
80 public enum NumFieldType {
81 BIGNUM, BYTE_PRIM, SHORT_PRIM, INT_PRIM, LONG_PRIM, CHAR_PRIM
82 }
83
84 /**
85 * Convenience wrapper for a field that stores a numeric value.
86 */
87 public static class NumField {
88 final private Field targetField;
89 final private NumFieldType targetType;
90
91
92 public NumFieldType getNumFieldType() {
93 return targetType;
94 }
95
96 public NumField(Field f) {
97 this.targetField = f;
98 if (f.getType().equals(Long.TYPE)) {
99 targetType = NumFieldType.LONG_PRIM;
100 } else if (f.getType().equals(Integer.TYPE)) {
101 targetType = NumFieldType.INT_PRIM;
102 } else if (f.getType().equals(Short.TYPE)) {
103 targetType = NumFieldType.SHORT_PRIM;
104 } else if (f.getType().equals(Byte.TYPE)) {
105 targetType = NumFieldType.BYTE_PRIM;
106 } else if (f.getType().equals(Character.TYPE)) {
107 targetType = NumFieldType.CHAR_PRIM;
108 } else if (f.getType().equals(BigInteger.class)) {
109 targetType = NumFieldType.BIGNUM;
110 } else {
111 throw new AssertionError(
112 "expected numeric type, got: " + f.getType());
113 }
114 }
115
116 public void set(Object obj, long val) {
117 try {
118 switch (targetType) {
119 case LONG_PRIM:
120 targetField.setLong(obj, val);
121 break;
122 case INT_PRIM:
123 targetField.setInt(obj, (int) val);
124 break;
125 case SHORT_PRIM:
126 targetField.setShort(obj, (short) val);
127 break;
128 case BYTE_PRIM:
129 targetField.setByte(obj, (byte) val);
130 break;
131 case CHAR_PRIM:
132 targetField.setChar(obj, (char) val);
133 break;
134 case BIGNUM:
135 targetField.set(obj, BigInteger.valueOf(val));
136 break;
137 }
138 } catch (IllegalArgumentException e) {
139 throw new AssertionError("cannot access field");
140 } catch (IllegalAccessException e) {
141 throw new AssertionError("cannot access field");
142 }
143 }
144
145 public void set(Object obj, BigInteger val) {
146 try {
147 targetField.set(obj, val);
148 } catch (IllegalAccessException e) {
149 throw new AssertionError("cannot access field");
150 }
151 }
152
153 public long get(Object obj) {
154 try {
155 switch (targetType) {
156 case LONG_PRIM:
157 return targetField.getLong(obj);
158 case INT_PRIM:
159 return targetField.getInt(obj);
160 case SHORT_PRIM:
161 return targetField.getShort(obj);
162 case BYTE_PRIM:
163 return targetField.getByte(obj);
164 case CHAR_PRIM:
165 return targetField.getChar(obj);
166 case BIGNUM:
167 throw new RuntimeException("get() called on NumField that is a BigInteger");
168 default:
169 throw new AssertionError("unreachable");
170 }
171 } catch (IllegalAccessException e) {
172 throw new AssertionError("cannot access field");
173 }
174 }
175
176 public BigInteger getBig(Object obj) {
177 if (isBig()) {
178 return (BigInteger) justGet(obj, targetField);
179 } else {
180 return BigInteger.valueOf(this.get(obj));
181 }
182 }
183
184 public boolean isBig() {
185 return targetType.equals(NumFieldType.BIGNUM);
186 }
187 }
188
189
190 public static Object followFieldPath(List<Field> fl, Object obj,
191 int depth) {
192 for (int i = 0; i < depth; ++i) {
193 try {
194 obj = fl.get(i).get(obj);
195 } catch (IllegalArgumentException e) {
196 throw new RuntimeException(e);
197 } catch (IllegalAccessException e) {
198 throw new AssertionError("cannot access field " + fl.get(i)
199 + " of " + obj.getClass());
200 }
201 }
202 return obj;
203 }
204
205 public static Object followFieldPath(List<Field> fl, Object obj) {
206 return followFieldPath(fl, obj, fl.size());
207 }
208
209 public static Object followFieldPathToParent(List<Field> fl, Object obj) {
210 return followFieldPath(fl, obj, fl.size() - 1);
211 }
212
213 public static Object justGet(Object obj, Field f) {
214 try {
215 return f.get(obj);
216 } catch (IllegalAccessException e) {
217 throw new AssertionError(
218 String.format("Cannot access private field '%s' in class %s", f, obj.getClass()));
219 } catch (IllegalArgumentException e) {
220 throw new AssertionError("Cannot access field '" + f.getName() + "' in class " + obj.getClass());
221 }
222 }
223
224 public static void justSet(Object obj, Field f, Object val) {
225 try {
226 f.set(obj, val);
227 } catch (IllegalAccessException e) {
228 throw new AssertionError(
229 String.format("Cannot access private field %s in class %s", f, obj.getClass()));
230 }
231 }
232
233
234 public static int justGetInt(Object obj, List<Field> path) {
235 for (int i = 0; i < path.size() - 1; ++i) {
236 try {
237 obj = path.get(i).get(obj);
238 } catch (IllegalArgumentException e) {
239 throw new RuntimeException(e);
240 } catch (IllegalAccessException e) {
241 throw new AssertionError("cannot access field " + path.get(i)
242 + " of " + obj.getClass());
243 }
244 }
245
246 try {
247 return path.get(path.size() - 1).getInt(obj);
248 } catch (IllegalAccessException e) {
249 throw new AssertionError("cannot access field " + path.get(path.size() - 1)
250 + " of " + obj.getClass());
251 }
252 }
253
254 public static void justSetInt(Object obj, List<Field> path, int val) {
255 for (int i = 0; i < path.size() - 1; ++i) {
256 try {
257 obj = path.get(i).get(obj);
258
259 } catch (IllegalArgumentException e) {
260 throw new RuntimeException(e);
261 } catch (IllegalAccessException e) {
262 throw new AssertionError("cannot access field " + path.get(i)
263 + " of " + obj.getClass());
264 }
265 }
266
267 try {
268 path.get(path.size() - 1).setInt(obj, val);
269 } catch (IllegalAccessException e) {
270 throw new AssertionError("cannot access field " + path.get(path.size() - 1)
271 + " of " + obj.getClass());
272 }
273 }
274
275
276 public static List<Field> getFieldPathFromString(final String p, final Class root) {
277 Class current = root;
278
279 String[] components = p.split("[.]");
280
281 List<Field> fp = new ArrayList<Field>(components.length);
282 for (String member : components) {
283 Field f;
284 try {
285 f = current.getField(member);
286 } catch (NoSuchFieldException e) {
287 throw new AssertionError("invalid field path, component " + member + " not found");
288 }
289
290 fp.add(f);
291
292 current = f.getType();
293 }
294
295 return fp;
296 }
297}
diff --git a/src/main/java/org/gnunet/construct/UInt16.java b/src/main/java/org/gnunet/construct/UInt16.java
new file mode 100644
index 0000000..18c24b2
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/UInt16.java
@@ -0,0 +1,40 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23import java.lang.annotation.ElementType;
24import java.lang.annotation.Retention;
25import java.lang.annotation.RetentionPolicy;
26import java.lang.annotation.Target;
27
28
29/**
30 * Unsigned 16-bit integer value
31 *
32 * @author Florian Dold
33 *
34 */
35
36@Retention(RetentionPolicy.RUNTIME)
37@Target(ElementType.FIELD)
38public @interface UInt16 {
39
40}
diff --git a/src/main/java/org/gnunet/construct/UInt32.java b/src/main/java/org/gnunet/construct/UInt32.java
new file mode 100644
index 0000000..ab4a278
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/UInt32.java
@@ -0,0 +1,39 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23import java.lang.annotation.ElementType;
24import java.lang.annotation.Retention;
25import java.lang.annotation.RetentionPolicy;
26import java.lang.annotation.Target;
27
28/**
29 * Unsigned 32-bit integer value
30 *
31 * @author Florian Dold
32 *
33 */
34
35@Retention(RetentionPolicy.RUNTIME)
36@Target(ElementType.FIELD)
37public @interface UInt32 {
38
39}
diff --git a/src/main/java/org/gnunet/construct/UInt64.java b/src/main/java/org/gnunet/construct/UInt64.java
new file mode 100644
index 0000000..d45cf69
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/UInt64.java
@@ -0,0 +1,39 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23import java.lang.annotation.ElementType;
24import java.lang.annotation.Retention;
25import java.lang.annotation.RetentionPolicy;
26import java.lang.annotation.Target;
27
28/**
29 * Unsigned 64-bit integer value
30 *
31 * @author Florian Dold
32 *
33 */
34
35@Retention(RetentionPolicy.RUNTIME)
36@Target(ElementType.FIELD)
37public @interface UInt64 {
38
39}
diff --git a/src/main/java/org/gnunet/construct/UInt8.java b/src/main/java/org/gnunet/construct/UInt8.java
new file mode 100644
index 0000000..58b8335
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/UInt8.java
@@ -0,0 +1,41 @@
1
2/*
3 This file is part of GNUnet.
4 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
5
6 GNUnet is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; either version 3, or (at your
9 option) any later version.
10
11 GNUnet is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNUnet; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.
20 */
21
22package org.gnunet.construct;
23
24import java.lang.annotation.ElementType;
25import java.lang.annotation.Retention;
26import java.lang.annotation.RetentionPolicy;
27import java.lang.annotation.Target;
28
29
30/**
31 * Unsigned 8-bit integer value.
32 *
33 * @author Florian Dold
34 *
35 */
36
37@Retention(RetentionPolicy.RUNTIME)
38@Target(ElementType.FIELD)
39public @interface UInt8 {
40
41}
diff --git a/src/main/java/org/gnunet/construct/Union.java b/src/main/java/org/gnunet/construct/Union.java
new file mode 100644
index 0000000..d35512e
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/Union.java
@@ -0,0 +1,37 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23
24import java.lang.annotation.ElementType;
25import java.lang.annotation.Retention;
26import java.lang.annotation.RetentionPolicy;
27import java.lang.annotation.Target;
28
29/**
30 * A field that stores a union, whose cases are discriminated by the field specified with {@literal tag}.
31 */
32@Target(ElementType.FIELD)
33@Retention(RetentionPolicy.RUNTIME)
34public @interface Union {
35 String tag();
36 boolean optional() default false;
37} \ No newline at end of file
diff --git a/src/main/java/org/gnunet/construct/UnionCase.java b/src/main/java/org/gnunet/construct/UnionCase.java
new file mode 100644
index 0000000..7622dd4
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/UnionCase.java
@@ -0,0 +1,39 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23
24import java.lang.annotation.ElementType;
25import java.lang.annotation.Retention;
26import java.lang.annotation.RetentionPolicy;
27import java.lang.annotation.Target;
28
29/**
30 * Annotation for messages that are a case of a union, with a distinct value to discriminate the case.
31 *
32 * Classes annotated by {@literal UnionCase} should implement
33 * the marker interface for their respective union type.
34 */
35@Retention(RetentionPolicy.SOURCE)
36@Target(ElementType.TYPE)
37public @interface UnionCase {
38 int value();
39} \ No newline at end of file
diff --git a/src/main/java/org/gnunet/construct/VariableSizeArray.java b/src/main/java/org/gnunet/construct/VariableSizeArray.java
new file mode 100644
index 0000000..64daece
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/VariableSizeArray.java
@@ -0,0 +1,39 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23import java.lang.annotation.ElementType;
24import java.lang.annotation.Retention;
25import java.lang.annotation.RetentionPolicy;
26import java.lang.annotation.Target;
27
28/**
29 * An array of messages, where the length of the array is specified in a
30 * field of the containing message.
31 *
32 * @author Florian Dold
33 *
34 */
35@Retention(RetentionPolicy.RUNTIME)
36@Target(ElementType.FIELD)
37public @interface VariableSizeArray {
38 String lengthField();
39}
diff --git a/src/main/java/org/gnunet/construct/VariableSizeIntegerArray.java b/src/main/java/org/gnunet/construct/VariableSizeIntegerArray.java
new file mode 100644
index 0000000..a3a9f28
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/VariableSizeIntegerArray.java
@@ -0,0 +1,41 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23import java.lang.annotation.ElementType;
24import java.lang.annotation.Retention;
25import java.lang.annotation.RetentionPolicy;
26import java.lang.annotation.Target;
27
28/**
29 * An array of integers, where the length of the array is specified in a
30 * field of the containing message.
31 *
32 * @author Florian Dold
33 *
34 */
35@Retention(RetentionPolicy.RUNTIME)
36@Target(ElementType.FIELD)
37public @interface VariableSizeIntegerArray {
38 String lengthField();
39 boolean signed();
40 int bitSize();
41}
diff --git a/src/main/java/org/gnunet/construct/ZeroTerminatedString.java b/src/main/java/org/gnunet/construct/ZeroTerminatedString.java
new file mode 100644
index 0000000..2d70d71
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/ZeroTerminatedString.java
@@ -0,0 +1,37 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct;
22
23import java.lang.annotation.Retention;
24import java.lang.annotation.RetentionPolicy;
25
26/**
27 * A zero-terminated string with the specified encoding.
28 * The default encoding is UTF-8.
29 *
30 * @author Florian Dold
31 *
32 */
33@Retention(RetentionPolicy.RUNTIME)
34public @interface ZeroTerminatedString {
35 String charset() default "UTF-8";
36 boolean optional() default false;
37}
diff --git a/src/main/java/org/gnunet/construct/package-info.java b/src/main/java/org/gnunet/construct/package-info.java
new file mode 100644
index 0000000..add5d40
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/package-info.java
@@ -0,0 +1,24 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21/**
22 * Write and read an object's binary representation, as determined by its members annotations.
23 */
24package org.gnunet.construct;
diff --git a/src/main/java/org/gnunet/construct/parsers/DoubleParser.java b/src/main/java/org/gnunet/construct/parsers/DoubleParser.java
new file mode 100644
index 0000000..c6cc835
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/parsers/DoubleParser.java
@@ -0,0 +1,75 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct.parsers;
22
23
24import org.gnunet.construct.Message;
25
26import java.lang.reflect.Field;
27import java.nio.ByteBuffer;
28import java.util.List;
29
30public class DoubleParser implements Parser {
31
32 private final Field targetField;
33
34 public DoubleParser(Field f) {
35 targetField = f;
36 }
37
38 @Override
39 public int getSize(Message srcObj) {
40 return Double.SIZE / 8;
41 }
42
43 @Override
44 public int parse(ByteBuffer srcBuf, int frameOffset, Message frameObj, Message dstObj, List<Field> frameSizePath) {
45 double d = srcBuf.getDouble();
46 try {
47 targetField.setDouble(dstObj, d);
48 } catch (IllegalAccessException e) {
49 throw new AssertionError("cannot access field (should have been caught in Construct)");
50 }
51 return Double.SIZE / 8;
52 }
53
54 @Override
55 public int write(ByteBuffer dstBuf, Message srcObj) {
56 double d;
57 try {
58 d = targetField.getDouble(srcObj);
59 } catch (IllegalAccessException e) {
60 throw new AssertionError("field does not exist (should be caught in Construct)");
61 }
62 dstBuf.putDouble(d);
63 return 8;
64 }
65
66 @Override
67 public void patch(Message m, int frameSize, List<Field> frameSizePath, Message frameObj) {
68 // nothing to do here
69 }
70
71 @Override
72 public int getStaticSize() {
73 return Double.SIZE / 8;
74 }
75}
diff --git a/src/main/java/org/gnunet/construct/parsers/FillParser.java b/src/main/java/org/gnunet/construct/parsers/FillParser.java
new file mode 100644
index 0000000..156e120
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/parsers/FillParser.java
@@ -0,0 +1,126 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct.parsers;
22
23import org.gnunet.construct.Message;
24import org.gnunet.construct.ReflectUtil;
25import java.lang.reflect.Array;
26import java.lang.reflect.Field;
27import java.nio.ByteBuffer;
28import java.util.ArrayList;
29import java.util.List;
30
31/**
32 * Parse an array that takes up all the available space.
33 *
34 * @author Florian Dold
35 */
36public class FillParser implements Parser {
37 private final Parser elemParser;
38
39 private final Field targetField;
40
41 public FillParser(Parser p, Field field) {
42 targetField = field;
43 elemParser = p;
44 }
45
46 @Override
47 public int getSize(final Message src) {
48 int size = 0;
49 final Object arr = ReflectUtil.justGet(src, targetField);
50
51 if (arr == null) {
52 throw new RuntimeException("array not initialized");
53 }
54
55 for (int i = 0; i < Array.getLength(arr); ++i) {
56 size += elemParser.getSize((Message) Array.get(arr, i));
57 }
58 return size;
59 }
60
61 @Override
62 public int parse(ByteBuffer srcBuf, int frameOffset,
63 Message frameObj, final Message dstObj, List<Field> frameSizePath) {
64
65 if (frameSizePath == null) {
66 throw new AssertionError("FillParser expects a non-null frameSizePath. Does the message have a @FrameSizePath annotation?");
67 }
68
69
70
71 final int frameSize = ReflectUtil.justGetInt(dstObj, frameSizePath);
72 int remaining = frameOffset + frameSize - srcBuf.position();
73 int size = 0;
74
75 Class<?> elemType = targetField.getType().getComponentType();
76
77
78 ArrayList<Message> list = new ArrayList<Message>(10);
79
80 while (remaining > 0) {
81 @SuppressWarnings("unchecked")
82 Message next = ReflectUtil.justInstantiate((Class<Message>) targetField.getType().getComponentType());
83 int s = elemParser.parse(srcBuf, frameOffset, frameObj, next, null);
84 size += s;
85 remaining -= s;
86 list.add(next);
87 }
88
89 Object arr = Array.newInstance(elemType, list.size());
90
91 try {
92 targetField.set(dstObj, list.toArray((Object[]) arr));
93 } catch (IllegalAccessException e) {
94 throw new AssertionError("cannot access field");
95 }
96
97 return size;
98 }
99
100 @Override
101 public int write(final ByteBuffer dstBuf, final Message src) {
102 int size = 0;
103 final Object arr = ReflectUtil.justGet(src, targetField);
104 for (int i = 0; i < Array.getLength(arr); ++i) {
105 size += elemParser.write(dstBuf, (Message) Array.get(arr, i));
106 }
107 return size;
108 }
109
110 @Override
111 public void patch(Message m, int frameSize, List<Field> frameSizePath, Message frameObj) {
112 if (frameSizePath == null) {
113 throw new AssertionError();
114 }
115 ReflectUtil.justSetInt(frameObj, frameSizePath, frameSize);
116
117 // todo: patch nested messages
118 }
119
120 @Override
121 public int getStaticSize() {
122 // not known
123 return 0;
124 }
125
126}
diff --git a/src/main/java/org/gnunet/construct/parsers/FixedSizeArrayParser.java b/src/main/java/org/gnunet/construct/parsers/FixedSizeArrayParser.java
new file mode 100644
index 0000000..3dbc720
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/parsers/FixedSizeArrayParser.java
@@ -0,0 +1,106 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct.parsers;
22
23import org.gnunet.construct.Message;
24import org.gnunet.construct.ReflectUtil;
25
26import java.lang.reflect.Array;
27import java.lang.reflect.Field;
28import java.nio.ByteBuffer;
29import java.util.List;
30
31public class FixedSizeArrayParser implements Parser {
32
33 private final Parser elemParser;
34
35 private final Field targetField;
36
37 private final int elemNumber;
38
39 public FixedSizeArrayParser(final int elemNumber,
40 final Parser elemParser, final Field f) {
41 targetField = f;
42 this.elemNumber = elemNumber;
43 this.elemParser = elemParser;
44 }
45
46 @Override
47 public int getSize(final Message srcObj) {
48 int size = 0;
49 final Object arr = ReflectUtil.justGet(srcObj, targetField);
50
51 if (arr == null) {
52 throw new RuntimeException("array not initialized");
53 }
54
55 for (int i = 0; i < Array.getLength(arr); ++i) {
56 size += elemParser.getSize((Message) Array.get(arr, i));
57 }
58 return size;
59 }
60
61 @Override
62 public int parse(ByteBuffer srcBuf, int frameOffset,
63 Message frameObj, final Message dstObj, List<Field> frameSizePath) {
64 int size = 0;
65
66 final Object arr = Array.newInstance(targetField.getType().getComponentType(), elemNumber);
67 ReflectUtil.justSet(dstObj, targetField, arr);
68
69 for (int i = 0; i < elemNumber; ++i) {
70 @SuppressWarnings("unchecked")
71 Message elemObj = ReflectUtil.justInstantiate((Class<Message>)targetField.getType().getComponentType());
72 Array.set(arr, i, elemObj);
73
74 size += elemParser.parse(srcBuf, frameOffset - size, frameObj, elemObj, null);
75 }
76
77 return size;
78 }
79
80 @Override
81 public int write(final ByteBuffer dstBuf,
82 final Message srcObj) {
83 int size = 0;
84 final Object arr = ReflectUtil.justGet(srcObj, targetField);
85 if (Array.getLength(arr) != elemNumber) {
86 throw new AssertionError("wrong number of elements");
87 }
88 for (int i = 0; i < Array.getLength(arr); ++i) {
89 size += elemParser.write(dstBuf, (Message) Array.get(arr, i));
90 }
91 return size;
92 }
93
94 @Override
95 public void patch(Message m, int frameSize, List<Field> frameSizePath, Message frameObj) {
96 final Object arr = ReflectUtil.justGet(m, targetField);
97 for (int i = 0; i < Array.getLength(arr); ++i) {
98 elemParser.patch((Message) Array.get(arr, i), frameSize, null, frameObj);
99 }
100 }
101
102 @Override
103 public int getStaticSize() {
104 return elemNumber * elemParser.getStaticSize();
105 }
106}
diff --git a/src/main/java/org/gnunet/construct/parsers/FixedSizeIntegerArrayParser.java b/src/main/java/org/gnunet/construct/parsers/FixedSizeIntegerArrayParser.java
new file mode 100644
index 0000000..ae633b0
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/parsers/FixedSizeIntegerArrayParser.java
@@ -0,0 +1,106 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct.parsers;
22
23import org.gnunet.construct.Message;
24import org.gnunet.construct.ProtocolViolationException;
25import org.gnunet.construct.ReflectUtil;
26
27import java.lang.reflect.Array;
28import java.lang.reflect.Field;
29import java.nio.BufferUnderflowException;
30import java.nio.ByteBuffer;
31import java.util.List;
32
33public class FixedSizeIntegerArrayParser implements Parser {
34
35 private final int byteSize;
36 private final boolean signed;
37
38 private final Field targetField;
39
40 private final int elemNumber;
41
42 public FixedSizeIntegerArrayParser(final int elemNumber, boolean signed, int byteSize, final Field f) {
43 targetField = f;
44 this.elemNumber = elemNumber;
45 this.signed = signed;
46 this.byteSize = byteSize;
47 }
48
49 @Override
50 public int getSize(final Message srcObj) {
51 return byteSize * elemNumber;
52 }
53
54 @Override
55 public int parse(ByteBuffer srcBuf, int frameOffset,
56 Message frameObj, final Message dstObj, List<Field> frameSizePath) {
57 int size = 0;
58
59 @SuppressWarnings("unchecked")
60 final Class<Message> arrayElementType = (Class<Message>) targetField.getType().getComponentType();
61
62 if (!arrayElementType.isPrimitive()) {
63 throw new AssertionError("IntegerFillParser is expected to be of primitive type, not " + arrayElementType);
64 }
65
66 final Object arr = Array.newInstance(targetField.getType().getComponentType(), elemNumber);
67 ReflectUtil.justSet(dstObj, targetField, arr);
68
69 for (int i = 0; i < elemNumber; ++i) {
70 long v;
71 try {
72 v = IntegerUtil.readLong(srcBuf, signed, byteSize);
73 } catch (BufferUnderflowException e) {
74 throw new ProtocolViolationException("fixed size array underflow: " + targetField.toString());
75 }
76 ReflectUtil.justSetArray(arr, i, v);
77 }
78
79 return size;
80 }
81
82 @Override
83 public int write(final ByteBuffer dstBuf,
84 final Message srcObj) {
85 int size = 0;
86 final Object arr = ReflectUtil.justGet(srcObj, targetField);
87 if (Array.getLength(arr) != elemNumber) {
88 throw new AssertionError("wrong number of elements");
89 }
90 for (int i = 0; i < Array.getLength(arr); ++i) {
91 IntegerUtil.writeLong(Array.getLong(arr, i), dstBuf, signed, byteSize);
92 size += byteSize;
93 }
94 return size;
95 }
96
97 @Override
98 public void patch(Message m, int frameSize, List<Field> frameSizePath, Message frameObj) {
99 // nothing to patch!
100 }
101
102 @Override
103 public int getStaticSize() {
104 return elemNumber * byteSize;
105 }
106}
diff --git a/src/main/java/org/gnunet/construct/parsers/IntegerFillParser.java b/src/main/java/org/gnunet/construct/parsers/IntegerFillParser.java
new file mode 100644
index 0000000..7027afc
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/parsers/IntegerFillParser.java
@@ -0,0 +1,113 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct.parsers;
22
23import org.gnunet.construct.Message;
24import org.gnunet.construct.ReflectUtil;
25
26import java.lang.reflect.Array;
27import java.lang.reflect.Field;
28import java.nio.ByteBuffer;
29import java.util.List;
30
31public class IntegerFillParser implements Parser {
32 private final Field targetField;
33 private final boolean signed;
34 private final int byteSize;
35
36 public IntegerFillParser(Field targetField,
37 boolean signed, int byteSize) {
38
39 this.targetField = targetField;
40 this.signed = signed;
41 this.byteSize = byteSize;
42 }
43
44
45 @Override
46 public int getSize(Message srcObj) {
47 final Object arr = ReflectUtil.justGet(srcObj, targetField);
48
49 if (arr == null) {
50 throw new RuntimeException("array not initialized");
51 }
52
53 return byteSize * Array.getLength(arr);
54 }
55
56 @Override
57 public int parse(ByteBuffer srcBuf, int frameStart, Message frameObj, Message dstObj, List<Field> frameSizePath) {
58 if (frameSizePath == null) {
59 throw new AssertionError("IntegerFillParser expects a non-null frameSizePath. Did you specify a @FrameSize field?");
60 }
61 final int frameSize = ReflectUtil.justGetInt(frameObj, frameSizePath);
62 int remaining = frameStart + frameSize - srcBuf.position();
63
64 int elemNumber = remaining / byteSize;
65
66 @SuppressWarnings("unchecked")
67 final Class<Message> arrayElementType = (Class<Message>) targetField.getType().getComponentType();
68
69 if (!arrayElementType.isPrimitive()) {
70 throw new AssertionError("IntegerFillParser is expected to be of primitive type, not " + arrayElementType);
71 }
72
73 final Object arr = Array.newInstance(arrayElementType, elemNumber);
74 ReflectUtil.justSet(dstObj, targetField, arr);
75
76
77 for (int i = 0; i < elemNumber; ++i) {
78 long v = IntegerUtil.readLong(srcBuf, signed, byteSize);
79 ReflectUtil.justSetArray(arr, i, v);
80 }
81
82 return remaining;
83 }
84
85 @Override
86 public int write(ByteBuffer dstBuf, Message srcObj) {
87 final Object arr = ReflectUtil.justGet(srcObj, targetField);
88
89 if (arr == null) {
90 throw new RuntimeException("array not initialized");
91 }
92
93 for (int i = 0; i < Array.getLength(arr); ++i) {
94 IntegerUtil.writeLong(Array.getLong(arr, i), dstBuf, signed, byteSize);
95 }
96
97 return getSize(srcObj);
98 }
99
100 @Override
101 public void patch(Message m, int frameSize, List<Field> frameSizePath, Message frameObj) {
102 if (frameSizePath == null) {
103 throw new AssertionError("IntegerFillParser expects a non-null frameSizePath. Did you specify a @FrameSize field?");
104 }
105 ReflectUtil.justSetInt(frameObj, frameSizePath, frameSize);
106 }
107
108 @Override
109 public int getStaticSize() {
110 // not known
111 return 0;
112 }
113}
diff --git a/src/main/java/org/gnunet/construct/parsers/IntegerParser.java b/src/main/java/org/gnunet/construct/parsers/IntegerParser.java
new file mode 100644
index 0000000..4f71aa5
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/parsers/IntegerParser.java
@@ -0,0 +1,95 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct.parsers;
22
23import org.gnunet.construct.Message;
24import org.gnunet.construct.ReflectUtil;
25
26import java.lang.reflect.Field;
27import java.nio.ByteBuffer;
28import java.util.List;
29
30/**
31 *
32 * todo: error checking on numeric overflow
33 */
34public class IntegerParser implements Parser {
35
36 public static final boolean UNSIGNED = false;
37 public static final boolean SIGNED = true;
38
39 private final int byteSize;
40
41 private final boolean isSigned;
42
43 private final ReflectUtil.NumField targetField;
44
45
46 public IntegerParser(final int byteSize, final boolean isSigned,
47 final Field f) {
48 this.byteSize = byteSize;
49 this.isSigned = isSigned;
50
51 targetField = new ReflectUtil.NumField(f);
52 }
53
54 @Override
55 public int getSize(final Message srcObj) {
56 return byteSize;
57 }
58
59 @Override
60 public int parse(final ByteBuffer srcBuf, int frameOffset, Message frameObj, final Message dstObj, List<Field>
61 frameSizePath) {
62 if (targetField.isBig()) {
63 targetField.set(dstObj, IntegerUtil.readBigInteger(srcBuf, isSigned, byteSize));
64 } else {
65 targetField.set(dstObj, IntegerUtil.readLong(srcBuf, isSigned, byteSize));
66 }
67 return byteSize;
68 }
69
70 @Override
71 public int write(final ByteBuffer dstBuf, final Message srcObj) {
72 if (targetField.isBig()) {
73 IntegerUtil.writeBitInteger(targetField.getBig(srcObj), dstBuf, isSigned, byteSize);
74 } else {
75 // todo: error checking on numeric overflow, if requested
76 IntegerUtil.writeLong(targetField.get(srcObj), dstBuf, isSigned, byteSize);
77 }
78 return byteSize;
79 }
80
81 @Override
82 public void patch(Message m, int frameSize, List<Field> frameSizePath, Message frameObj) {
83 // todo: optimize this!
84 /*
85 if (frameSizePath != null) {
86 ReflectUtil.justSetInt(frameObj, frameSizePath, frameSize);
87 }
88 */
89 }
90
91 @Override
92 public int getStaticSize() {
93 return byteSize;
94 }
95}
diff --git a/src/main/java/org/gnunet/construct/parsers/IntegerUtil.java b/src/main/java/org/gnunet/construct/parsers/IntegerUtil.java
new file mode 100644
index 0000000..83391de
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/parsers/IntegerUtil.java
@@ -0,0 +1,92 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct.parsers;
22
23
24import java.math.BigInteger;
25import java.nio.ByteBuffer;
26
27public class IntegerUtil {
28 public static long readLong(ByteBuffer srcBuf, boolean isSigned, int byteSize) {
29 long val = 0;
30
31 final int first = srcBuf.position();
32 final int last = first + byteSize - 1;
33
34 // read all bytes except the last
35 while (srcBuf.position() != last) {
36 byte b = srcBuf.get();
37 // byte b may be signed, if so interpret it as unsigned byte; store it in an int
38 int s = b >= 0 ? b : (256 + b);
39
40 val |= s;
41 val <<= 8;
42 }
43
44 // read the last byte, we don't have to shift val after that
45 byte b = srcBuf.get();
46 int s = b >= 0 ? b : (256 + b);
47 val |= s;
48
49 if (isSigned) {
50 // explicitly OR sign bit to the right place if the source buffer is
51 // too large
52 long sign = (srcBuf.get(first) & 0x80);
53 val |= (sign << 7);
54 }
55
56 return val;
57 }
58
59 public static void writeLong(final long val, final ByteBuffer dstBuf, boolean isSigned, int byteSize) {
60 long myval = val;
61
62 // position of the last byte we are responsible to write
63 int last = dstBuf.position() + byteSize - 1;
64
65 while (last >= dstBuf.position()) {
66 dstBuf.put(last, (byte) (myval & 0xFF));
67 myval >>>= 8;
68 last -= 1;
69 }
70
71 if (isSigned) {
72 // a long has 8 bytes, shift by 7 bytes (non-arithmetically) to get the sign
73 byte sign = (byte) ((val >>> (7*8)) & 0x80);
74 // remove the sign bit from the buffer
75 dstBuf.put(dstBuf.position() + byteSize - 1, (byte) (dstBuf.get(dstBuf.position() + byteSize - 1) & ~sign));
76 // ... and put it in the right place (lowest byte)
77 dstBuf.put(dstBuf.position(), (byte) (dstBuf.get(dstBuf.position()) | sign));
78
79 }
80
81 dstBuf.position(dstBuf.position() + byteSize);
82 }
83
84
85 public static void writeBitInteger(BigInteger big, ByteBuffer dstBuf, boolean isSigned, int byteSize) {
86 throw new UnsupportedOperationException("not yet implemented");
87 }
88
89 public static BigInteger readBigInteger(final ByteBuffer srcBuf, boolean isSigned, int byteSize) {
90 throw new UnsupportedOperationException("not yet implemented");
91 }
92}
diff --git a/src/main/java/org/gnunet/construct/parsers/NestedParser.java b/src/main/java/org/gnunet/construct/parsers/NestedParser.java
new file mode 100644
index 0000000..76aa397
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/parsers/NestedParser.java
@@ -0,0 +1,119 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct.parsers;
22
23import org.gnunet.construct.Message;
24import org.gnunet.construct.ProtocolViolationException;
25import org.gnunet.construct.ReflectUtil;
26
27import java.lang.reflect.Field;
28import java.nio.ByteBuffer;
29import java.util.List;
30
31
32public class NestedParser implements Parser {
33 private final Field targetField;
34
35 private final Parser nestedParser;
36 private boolean newFrame;
37
38 boolean optional;
39
40 public NestedParser(final Parser p, boolean optional, final Field f, boolean newFrame) {
41 targetField = f;
42 this.optional = optional;
43 this.nestedParser = p;
44 this.newFrame = newFrame;
45 }
46
47 @Override
48 public int getSize(final Message src) {
49 Message inner = (Message) ReflectUtil.justGet(src, targetField);
50 if (inner == null) {
51 if (optional)
52 return 0;
53 throw new AssertionError(String.format("empty non-optional nested message in field '%s'", targetField));
54 }
55 return nestedParser.getSize(inner);
56 }
57
58 @Override
59 public int parse(final ByteBuffer srcBuf, int frameOffset, Message frameObj, final Message dstObj, List<Field>
60 frameSizePath) {
61 if (newFrame) {
62 frameObj = dstObj;
63 frameOffset = 0;
64 }
65
66 if (optional) {
67 if (frameSizePath == null) {
68 throw new AssertionError("optional nested message needs @FrameSize");
69 }
70
71 int remaining = frameOffset + ReflectUtil.justGetInt(frameObj, frameSizePath) - srcBuf.position();
72 if (remaining < 0) {
73 throw new ProtocolViolationException("remaining size negative");
74 }
75 if (remaining == 0) {
76 if (!optional) {
77 throw new ProtocolViolationException("not optional");
78 }
79 ReflectUtil.justSet(dstObj, targetField, null);
80 return 0;
81 }
82 }
83
84 ReflectUtil.justSet(dstObj, targetField, ReflectUtil.justInstantiate(targetField.getType()));
85
86 try {
87 return nestedParser.parse(srcBuf, frameOffset,
88 frameObj, (Message) ReflectUtil.justGet(dstObj, targetField), frameSizePath);
89 } catch (ProtocolViolationException e) {
90 throw e.augmentPath("nested parser on " + targetField.toString());
91 }
92 }
93
94 @Override
95 public int write(final ByteBuffer dstBuf, final Message src) {
96 Object nestedMessage = ReflectUtil.justGet(src, targetField);
97 if (nestedMessage == null) {
98 return 0;
99 }
100 return nestedParser.write(dstBuf, (Message) nestedMessage);
101 }
102
103 @Override
104 public void patch(Message m, int frameSize, List<Field> frameSizePath, Message frameObj) {
105 Message nestedMessage = (Message) ReflectUtil.justGet(m, targetField);
106
107 if (newFrame) {
108 nestedParser.patch(nestedMessage, nestedParser.getSize(nestedMessage), null, nestedMessage);
109 } else {
110 nestedParser.patch(nestedMessage, frameSize, frameSizePath, frameObj);
111 }
112 }
113
114 @Override
115 public int getStaticSize() {
116 return nestedParser.getStaticSize();
117 }
118
119}
diff --git a/src/main/java/org/gnunet/construct/parsers/Parser.java b/src/main/java/org/gnunet/construct/parsers/Parser.java
new file mode 100644
index 0000000..3eb02a6
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/parsers/Parser.java
@@ -0,0 +1,78 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct.parsers;
22
23import org.gnunet.construct.Message;
24
25import java.lang.reflect.Field;
26import java.nio.ByteBuffer;
27import java.util.List;
28
29
30public interface Parser {
31 /**
32 * Compute the exact size of the object's binary representation in bytes.
33 *
34 * @param srcObj a message object with all fields filled out appropriately
35 * @return the exact size of the object's binary representation in bytes
36 */
37 public int getSize(Message srcObj);
38
39
40 /**
41 * Parse from a ByteBuffer into a destination object.
42 *
43 * @param srcBuf the buffer containing the binary data to construct this object
44 * @param frameStart start of the current frame, relative to the beginning of srcBuf
45 * @param frameObj the object containing the dstObj, dstObj if dstObj itself is the frame object
46 * @param dstObj the object whose members are written according according to the data in srcBuf
47 * @param frameSizePath
48 * @return number of byres read from srcBuf
49 */
50 public int parse(ByteBuffer srcBuf, int frameStart, Message frameObj, Message dstObj, List<Field> frameSizePath);
51
52 /**
53 *
54 * @param dstBuf destination buffer for the binary representation of the object
55 * @param srcObj object to serialize to binary form
56 * @return number of bytes written to buf (todo: we are using a ByteBuffer now, this is obsolete)
57 */
58 public int write(ByteBuffer dstBuf, Message srcObj);
59
60 /**
61 * Parser-dependent method; sets members of the Message m (or Messages nested in m) which are
62 * values inferable by the parser.
63 * Examples: Union tags, size fields.
64 *
65 * @param m the message object to patch
66 * @param frameSize the size of the containing message
67 * @param frameSizePath
68 * @param frameObj the object containing the message (and possibly size fields)
69 */
70 public void patch(Message m, int frameSize, List<Field> frameSizePath, Message frameObj);
71
72 /**
73 * Return a lower bound for the size of the message in bytes
74 *
75 * @return minimum static size of the message in bytes
76 */
77 int getStaticSize();
78}
diff --git a/src/main/java/org/gnunet/construct/parsers/SequenceParser.java b/src/main/java/org/gnunet/construct/parsers/SequenceParser.java
new file mode 100644
index 0000000..4128e1b
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/parsers/SequenceParser.java
@@ -0,0 +1,106 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct.parsers;
22
23import org.gnunet.construct.Message;
24import org.gnunet.construct.ProtocolViolationException;
25import org.gnunet.construct.ReflectUtil;
26
27import java.lang.reflect.Field;
28import java.nio.ByteBuffer;
29import java.util.LinkedList;
30import java.util.List;
31
32/**
33 * A Sequence of Parsers that operate on the same object.
34 * @author Florian Dold
35 *
36 */
37public class SequenceParser implements Parser {
38
39 private final List<Parser> childParsers = new LinkedList<Parser>();
40 private List<Field> myFrameSizePath;
41
42 public SequenceParser() {
43 }
44
45 public void add(final Parser p) {
46 childParsers.add(p);
47 }
48
49 @Override
50 public int getSize(final Message src) {
51 int size = 0;
52 for (final Parser p : childParsers) {
53 size += p.getSize(src);
54 }
55 return size;
56 }
57
58 @Override
59 public int parse(final ByteBuffer srcBuf, int frameOffset,
60 Message frameObj, final Message dst, List<Field> frameSizePath) {
61 int size = 0;
62 for (final Parser p : childParsers) {
63 try {
64 size += p.parse(srcBuf, frameOffset, frameObj, dst,
65 frameSizePath == null ? myFrameSizePath : frameSizePath);
66 } catch (ProtocolViolationException e) {
67 throw e.augmentPath("(sequence parser)");
68 }
69 }
70 return size;
71 }
72
73 @Override
74 public int write(final ByteBuffer dstBuf, final Message src) {
75 int size = 0;
76 for (final Parser p : childParsers) {
77 size += p.write(dstBuf, src);
78 }
79 return size;
80 }
81
82 @Override
83 public void patch(Message m, int frameSize, List<Field> frameSizePath, Message frameObj) {
84 // todo: this should be optimized / only be done by the topmost sequence parse => introduce a boolean parameter
85 if (myFrameSizePath != null) {
86 ReflectUtil.justSetInt(frameObj, myFrameSizePath, frameSize);
87 }
88
89 for (final Parser p : childParsers) {
90 p.patch(m, frameSize, frameSizePath == null ? myFrameSizePath : frameSizePath, frameObj);
91 }
92 }
93
94 @Override
95 public int getStaticSize() {
96 int accum = 0;
97 for (Parser p : childParsers) {
98 accum += p.getStaticSize();
99 }
100 return accum;
101 }
102
103 public void setFrameSizePath(List<Field> frameSizePath) {
104 this.myFrameSizePath = frameSizePath;
105 }
106}
diff --git a/src/main/java/org/gnunet/construct/parsers/StringParser.java b/src/main/java/org/gnunet/construct/parsers/StringParser.java
new file mode 100644
index 0000000..46d98ae
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/parsers/StringParser.java
@@ -0,0 +1,145 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct.parsers;
22
23import org.gnunet.construct.Message;
24import org.gnunet.construct.ProtocolViolationException;
25import org.gnunet.construct.ReflectUtil;
26
27import java.io.UnsupportedEncodingException;
28import java.lang.reflect.Field;
29import java.nio.ByteBuffer;
30import java.util.List;
31
32public class StringParser implements Parser {
33
34 private final String cset;
35 private final boolean optional;
36 private final Field targetField;
37
38 public StringParser(final String charset, boolean optional, final Field f) {
39 this.targetField = f;
40 this.optional = optional;
41 this.cset = charset;
42 }
43
44 @Override
45 public int getSize(final Message srcObj) {
46 final String s = (String) ReflectUtil.justGet(srcObj, targetField);
47 if (s == null) {
48 if (optional) {
49 return 0;
50 } else {
51 throw new AssertionError("non-optional string cannot be null");
52 }
53 }
54 try {
55 final byte[] b = s.getBytes(cset);
56 return b.length + 1;
57 } catch (final UnsupportedEncodingException e) {
58 throw new RuntimeException();
59 }
60 }
61
62 @Override
63 public int parse(final ByteBuffer srcBuf, int frameOffset, Message frameObj, final Message dstObj, List<Field>
64 frameSizePath) {
65
66 if (optional) {
67 if (frameSizePath == null) {
68 throw new AssertionError("optional string with no length field in the message!");
69 }
70 final int frameSize = ReflectUtil.justGetInt(dstObj, frameSizePath);
71 int remaining = frameOffset + frameSize - srcBuf.position();
72
73 if (remaining == 0) {
74 if (!optional) {
75 throw new ProtocolViolationException("no data received for non-optional string");
76 }
77 ReflectUtil.justSet(dstObj, targetField, null);
78 return 0;
79 }
80 }
81
82 int length = 0;
83
84 while (srcBuf.get(srcBuf.position() + length) != 0) {
85 length++;
86 }
87
88 final byte[] stringData = new byte[length];
89
90 srcBuf.get(stringData);
91
92 if (srcBuf.get() != 0) {
93 throw new AssertionError("programming error");
94 }
95
96 String str;
97 try {
98 str = new String(stringData, cset);
99 } catch (final UnsupportedEncodingException e) {
100 throw new RuntimeException();
101 }
102
103 ReflectUtil.justSet(dstObj, targetField, str);
104
105 return length + 1;
106 }
107
108 @Override
109 public int write(final ByteBuffer dstBuf, final Message srcObj) {
110 String s = (String) ReflectUtil.justGet(srcObj, targetField);
111
112 if (s == null) {
113 if (!optional) {
114 throw new AssertionError("non-optional string cannot be null");
115 }
116 return 0;
117 }
118
119 byte[] b;
120 try {
121 b = s.getBytes(cset);
122 } catch (final UnsupportedEncodingException e) {
123 throw new RuntimeException();
124 }
125
126 dstBuf.put(b);
127 dstBuf.put((byte) 0);
128
129 // +1 for the 0-byte
130 return b.length + 1;
131 }
132
133 @Override
134 public void patch(Message m, int frameSize, List<Field> frameSizePath, Message frameObj) {
135 if (frameSizePath != null) {
136 ReflectUtil.justSetInt(frameObj, frameSizePath, frameSize);
137 }
138 }
139
140 @Override
141 public int getStaticSize() {
142 return optional ? 0 : 1;
143 }
144
145}
diff --git a/src/main/java/org/gnunet/construct/parsers/UnionParser.java b/src/main/java/org/gnunet/construct/parsers/UnionParser.java
new file mode 100644
index 0000000..e9fcc92
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/parsers/UnionParser.java
@@ -0,0 +1,143 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct.parsers;
22
23import org.gnunet.construct.*;
24import org.gnunet.construct.ProtocolViolationException;
25
26import java.lang.reflect.Field;
27import java.nio.ByteBuffer;
28import java.util.List;
29
30
31
32// unchecked casts are necessary
33@SuppressWarnings("unchecked")
34public class UnionParser implements Parser {
35
36 private final Field targetField;
37
38 private final List<Field> unionTagPath;
39 private final ReflectUtil.NumField unionTagField;
40 private final Class<? extends MessageUnion> unionType;
41
42 boolean optional;
43
44 public UnionParser(boolean optional, Class<? extends MessageUnion> unionType,
45 List<Field> unionTagPath, Field f) {
46 targetField = f;
47 this.optional = optional;
48 this.unionTagPath = unionTagPath;
49 this.unionTagField = new ReflectUtil.NumField(unionTagPath.get(unionTagPath.size() - 1));
50 this.unionType = unionType;
51 }
52
53 @Override
54 public int getSize(final Message src) {
55 Object target = ReflectUtil.justGet(src, targetField);
56 if (target == null) {
57 if (optional) {
58 return 0;
59 } else {
60 throw new AssertionError("non-optional union member must not be null");
61 }
62 }
63 Class cls = ReflectUtil.justGet(src, targetField).getClass();
64
65 Parser parser = Construct.getParser(cls);
66
67 return parser.getSize((Message)ReflectUtil.justGet(src, targetField));
68 }
69
70 @Override
71 public int parse(final ByteBuffer srcBuf, int frameOffset, Message frameObj, final Message dstObj, List<Field>
72 frameSizePath) {
73 if (optional) {
74 if (frameSizePath == null) {
75 throw new AssertionError("missing @FrameSize");
76 }
77
78 int remaining = frameOffset + ReflectUtil.justGetInt(frameObj, frameSizePath) - srcBuf.position();
79 if (remaining <= 0) {
80 if (!optional) {
81 throw new ProtocolViolationException("not optional");
82 }
83 ReflectUtil.justSet(dstObj, targetField, null);
84 return 0;
85 }
86 }
87
88 long unionTag = unionTagField.get(ReflectUtil.followFieldPathToParent(unionTagPath, dstObj));
89
90 final Class cls;
91
92 cls = MessageLoader.getUnionClass(unionType, (int) unionTag);
93
94 ReflectUtil.justSet(dstObj, targetField, ReflectUtil.justInstantiate(cls));
95
96 final Message theUnion = (Message) ReflectUtil.justGet(dstObj, targetField);
97
98 Parser parser = Construct.getParser(cls);
99
100 return parser.parse(srcBuf, frameOffset, frameObj, theUnion, frameSizePath);
101 }
102
103 @Override
104 public int write(final ByteBuffer dstBuf, final Message src) {
105 final Object target = ReflectUtil.justGet(src, targetField);
106
107 if (target == null) {
108 if (optional) {
109 return 0;
110 } else {
111 throw new AssertionError("non-optional union member must not be null");
112 }
113 }
114
115 final Class currentUnionClass = target.getClass();
116 final Parser p = Construct.getParser(currentUnionClass);
117
118 return p.write(dstBuf, (Message) ReflectUtil.justGet(src, targetField));
119 }
120
121 @SuppressWarnings("unchecked")
122 public int getTag(Message m) {
123 return MessageLoader.getUnionTag(unionType, (Class<MessageUnion>) ReflectUtil.justGet(m, targetField).getClass());
124 }
125
126 @Override
127 public void patch(Message m, int frameSize, List<Field> frameSizePath, Message frameObj) {
128 final Class currentUnionClass = ReflectUtil.justGet(m, targetField).getClass();
129 final Parser p = Construct.getParser(currentUnionClass);
130
131 p.patch((Message) ReflectUtil.justGet(m, targetField), frameSize, frameSizePath, frameObj);
132
133 unionTagField.set(ReflectUtil.followFieldPathToParent(unionTagPath, m),
134 getTag(m));
135 }
136
137 @Override
138 public int getStaticSize() {
139 // we can't say anything about the static size
140 // todo: in a more elaborate implementation, try all union members
141 return 0;
142 }
143}
diff --git a/src/main/java/org/gnunet/construct/parsers/VariableSizeArrayParser.java b/src/main/java/org/gnunet/construct/parsers/VariableSizeArrayParser.java
new file mode 100644
index 0000000..fd55925
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/parsers/VariableSizeArrayParser.java
@@ -0,0 +1,106 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct.parsers;
22
23import org.gnunet.construct.Message;
24import org.gnunet.construct.ProtocolViolationException;
25import org.gnunet.construct.ReflectUtil;
26
27import java.lang.reflect.Array;
28import java.lang.reflect.Field;
29import java.nio.ByteBuffer;
30import java.util.List;
31
32public class VariableSizeArrayParser implements Parser {
33 private final Field targetField;
34 private final Parser elemParser;
35 private ReflectUtil.NumField sizeField;
36
37
38 public VariableSizeArrayParser(final Parser elemParser, Field sizeField, Field arrayField) {
39 targetField = arrayField;
40 this.elemParser = elemParser;
41 this.sizeField = new ReflectUtil.NumField(sizeField);
42 }
43
44 @Override
45 public int getSize(final Message src) {
46 int size = 0;
47 final Object arr = ReflectUtil.justGet(src, targetField);
48
49 if (arr == null) {
50 throw new RuntimeException("array not initialized");
51 }
52
53 for (int i = 0; i < Array.getLength(arr); ++i) {
54 size += elemParser.getSize((Message) Array.get(arr, i));
55 }
56 return size;
57 }
58
59 @Override
60 public int parse(final ByteBuffer srcBuf, int frameOffset, Message frameObj, final Message dstObj, List<Field>
61 frameSizePath) {
62 final int elemNumber = (int) sizeField.get(dstObj);
63
64 @SuppressWarnings("unchecked")
65 final Class<Message> arrayElementType = (Class<Message>) targetField.getType().getComponentType();
66
67 int size = 0;
68
69 final Object arr = Array.newInstance(arrayElementType, elemNumber);
70 ReflectUtil.justSet(dstObj, targetField, arr);
71
72 for (int i = 0; i < elemNumber; ++i) {
73 Message elemObj;
74
75 elemObj = ReflectUtil.justInstantiate(arrayElementType);
76
77 Array.set(arr, i, elemObj);
78
79 size += elemParser.parse(srcBuf, frameOffset - size, null, elemObj, null);
80 }
81
82 return size;
83 }
84
85 @Override
86 public int write(final ByteBuffer dstBuf, final Message src) {
87 int size = 0;
88 final Object arr = ReflectUtil.justGet(src, targetField);
89 for (int i = 0; i < Array.getLength(arr); ++i) {
90 size += elemParser.write(dstBuf, (Message) Array.get(arr, i));
91 }
92 return size;
93 }
94
95 @Override
96 public void patch(Message m, int frameSize, List<Field> frameSizePath, Message frameObj) {
97 int size = Array.getLength(ReflectUtil.justGet(m, targetField));
98 sizeField.set(m, size);
99 }
100
101 @Override
102 public int getStaticSize() {
103 return 0;
104 }
105
106}
diff --git a/src/main/java/org/gnunet/construct/parsers/VariableSizeIntegerArrayParser.java b/src/main/java/org/gnunet/construct/parsers/VariableSizeIntegerArrayParser.java
new file mode 100644
index 0000000..b3f134d
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/parsers/VariableSizeIntegerArrayParser.java
@@ -0,0 +1,103 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.construct.parsers;
22
23import org.gnunet.construct.Message;
24import org.gnunet.construct.ReflectUtil;
25
26import java.lang.reflect.Array;
27import java.lang.reflect.Field;
28import java.nio.ByteBuffer;
29import java.util.List;
30
31public class VariableSizeIntegerArrayParser implements Parser {
32 private final Field targetField;
33 private ReflectUtil.NumField sizeField;
34 private int byteSize;
35 private boolean signed;
36
37
38 public VariableSizeIntegerArrayParser(Field sizeField, Field arrayField,
39 boolean signed, int byteSize) {
40 targetField = arrayField;
41 this.sizeField = new ReflectUtil.NumField(sizeField);
42 this.signed = signed;
43 this.byteSize = byteSize;
44 }
45
46 @Override
47 public int getSize(final Message src) {
48 final Object arr = ReflectUtil.justGet(src, targetField);
49
50 if (arr == null) {
51 throw new RuntimeException("array not initialized");
52 }
53
54 return Array.getLength(arr) * (byteSize);
55 }
56
57 @Override
58 public int parse(final ByteBuffer srcBuf, int frameOffset, Message frameObj, final Message dstObj, List<Field>
59 frameSizePath) {
60 final int elemNumber = (int) sizeField.get(dstObj);
61
62
63 @SuppressWarnings("unchecked")
64 final Class<Message> arrayElementType = (Class<Message>) targetField.getType().getComponentType();
65
66 if (!arrayElementType.isPrimitive()) {
67 throw new AssertionError("VariableSizeIntegerArray is expected to be of primitive type, not " + arrayElementType);
68 }
69
70 final Object arr = Array.newInstance(arrayElementType, elemNumber);
71 ReflectUtil.justSet(dstObj, targetField, arr);
72
73 for (int i = 0; i < elemNumber; ++i) {
74 long v = IntegerUtil.readLong(srcBuf, signed, byteSize);
75 ReflectUtil.justSetArray(arr, i, v);
76 }
77
78 return byteSize * elemNumber;
79 }
80
81 @Override
82 public int write(final ByteBuffer dstBuf, final Message src) {
83 int size = 0;
84 final Object arr = ReflectUtil.justGet(src, targetField);
85 for (int i = 0; i < Array.getLength(arr); ++i) {
86 IntegerUtil.writeLong(ReflectUtil.justGetArrayLong(arr, i), dstBuf, signed, byteSize);
87 size += byteSize;
88 }
89 return size;
90 }
91
92 @Override
93 public void patch(Message m, int frameSize, List<Field> frameSizePath, Message frameObj) {
94 int size = Array.getLength(ReflectUtil.justGet(m, targetField));
95 sizeField.set(m, size);
96 }
97
98 @Override
99 public int getStaticSize() {
100 return 0;
101 }
102
103}
diff --git a/src/main/java/org/gnunet/construct/parsers/package-info.java b/src/main/java/org/gnunet/construct/parsers/package-info.java
new file mode 100644
index 0000000..5da3a94
--- /dev/null
+++ b/src/main/java/org/gnunet/construct/parsers/package-info.java
@@ -0,0 +1,25 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21
22/**
23 * The actual parsers for reading and writing annotated messages.
24 */
25package org.gnunet.construct.parsers;
diff --git a/src/main/java/org/gnunet/core/ConnectHandler.java b/src/main/java/org/gnunet/core/ConnectHandler.java
new file mode 100644
index 0000000..a36d798
--- /dev/null
+++ b/src/main/java/org/gnunet/core/ConnectHandler.java
@@ -0,0 +1,30 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.core;
22
23import org.gnunet.util.PeerIdentity;
24
25/**
26 * Called when a new peer (with a compatible set of messages) connects to core
27 */
28public interface ConnectHandler {
29 void onConnect(PeerIdentity peerIdentity);
30}
diff --git a/src/main/java/org/gnunet/core/ConnectNotifyMessage.java b/src/main/java/org/gnunet/core/ConnectNotifyMessage.java
new file mode 100644
index 0000000..4eafe02
--- /dev/null
+++ b/src/main/java/org/gnunet/core/ConnectNotifyMessage.java
@@ -0,0 +1,54 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.core;
22
23import org.gnunet.construct.*;
24import org.gnunet.util.GnunetMessage;
25import org.gnunet.util.PeerIdentity;
26
27/**
28 * Message sent by the service to clients to notify them
29 * about a peer connecting.
30 */
31@UnionCase(67)
32public class ConnectNotifyMessage implements GnunetMessage.Body {
33 /**
34 * Number of ATS key-value pairs that follow this struct
35 * (excluding the 0-terminator).
36 */
37 @UInt32
38 public long atsCount;
39
40 /**
41 * Identity of the connecting peer.
42 */
43 @NestedMessage
44 public PeerIdentity peer;
45
46
47 @FillWith @UInt8
48 public byte[] atsInfo;
49
50
51 //@FillWith
52 //public ATSInformation[] atsInformation;
53
54}
diff --git a/src/main/java/org/gnunet/core/Core.java b/src/main/java/org/gnunet/core/Core.java
new file mode 100644
index 0000000..2895e41
--- /dev/null
+++ b/src/main/java/org/gnunet/core/Core.java
@@ -0,0 +1,347 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.core;
22
23import com.google.common.collect.Maps;
24import org.gnunet.construct.Construct;
25import org.gnunet.construct.MessageLoader;
26import org.gnunet.mq.Envelope;
27import org.gnunet.requests.MatchingRequestContainer;
28import org.gnunet.requests.RequestContainer;
29import org.gnunet.util.*;
30import org.grothoff.Runabout;
31import org.slf4j.Logger;
32import org.slf4j.LoggerFactory;
33
34import java.util.HashMap;
35
36
37/**
38 * API for the gnunet core service.
39 * <p/>
40 * Sends messages to connected peers.
41 */
42public class Core {
43 /**
44 * Logger for org.gnunet.Core.
45 */
46 private static final Logger logger = LoggerFactory
47 .getLogger(Core.class);
48
49 /**
50 * Client for connecting to the core service
51 */
52 private final Client client;
53
54 /*
55 * set to null once connected for the first time
56 */
57 private InitCallback initCallback;
58
59 /*
60 * Callback for traffic notifications. null if not interested.
61 */
62 private HeaderNotify notifyOutboundHeaders;
63
64 /*
65 * Callback for traffic notifications. null if not interested.
66 */
67 private HeaderNotify notifyInboundHeaders;
68
69 /*
70 * Callback for traffic notifications. null if not interested.
71 */
72 private MessageNotify notifyOutboundMessages;
73
74 /*
75 * Callback for traffic notifications. null if not interested.
76 */
77 private MessageNotify notifyInboundMessages;
78
79 /*
80 * Callbacks for connect events
81 */
82 private ConnectHandler connectHandler;
83
84 /**
85 * Callback for disconnect events.
86 */
87 private DisconnectHandler disconnectHandler;
88
89 /**
90 * Messages we are interested in.
91 * Per default we are interested in all messages => specific interest set is empty.
92 */
93 private int[] interested = new int[0];
94
95 /**
96 * Handler for the messages we are interested in.
97 */
98 private Runabout messageHandler;
99
100 /**
101 * Peers that we were notified about being connected to them.
102 * Every connected peer is mapped to a generator for unique request IDs.
103 */
104 private HashMap<PeerIdentity, Integer> connectedPeers = Maps.newHashMap();
105
106 /**
107 * Request container for notify transmit requests.
108 */
109 private MatchingRequestContainer<RequestIdentification, NotifyTransmitReadyRequest> ntr_requests;
110
111 public static class NotifyTransmitReadyRequest extends RequestContainer.Request {
112 private final int size;
113 final public PeerIdentity target;
114 final public long priority;
115 public int smrId;
116 final public MessageTransmitter transmitter;
117 final public AbsoluteTime deadline;
118
119 public NotifyTransmitReadyRequest(int priority, int size, PeerIdentity target, RelativeTime timeout, MessageTransmitter transmitter) {
120 this.deadline = timeout.toAbsolute();
121 this.priority = priority;
122 this.size = size;
123 this.target = target;
124 this.transmitter = transmitter;
125 }
126
127 @Override
128 public Envelope assembleRequest() {
129 SendMessageRequest m = new SendMessageRequest();
130 m.peer = target;
131 m.smrId = smrId;
132 m.priority = priority;
133 m.size = size;
134 m.deadline = deadline.asMessage();
135 return new Envelope(m);
136 }
137
138 public void cancel() {
139 // do nothing
140 }
141 }
142
143
144 public final class CoreReceiver extends RunaboutMessageReceiver {
145 public void visit(InitReplyMessage m) {
146 PeerIdentity myIdentity = m.myIdentity;
147 connectedPeers.put(myIdentity, 1);
148
149 if (initCallback != null) {
150 initCallback.onInit(m.myIdentity);
151 initCallback = null;
152 }
153 }
154
155 public void visit(ConnectNotifyMessage m) {
156 if (connectHandler != null) {
157 connectHandler.onConnect(m.peer);
158 }
159 }
160
161 public void visit(DisconnectNotifyMessage m) {
162 if (disconnectHandler != null) {
163 disconnectHandler.onDisconnect(m.peer);
164 }
165 }
166
167 public void visit(NotifyInboundTrafficMessage m) {
168 boolean found = false;
169 if (notifyInboundHeaders != null) {
170 notifyInboundHeaders.notify(m.payloadHeader);
171 }
172 if (notifyInboundMessages != null) {
173 // todo: call corresponding notify on notifyInboundMessages
174 }
175
176 for (int i : interested) {
177 if (i == m.payloadHeader.messageType) {
178 found = true;
179 break;
180 }
181 }
182 if (found) {
183 Class bodyClass = MessageLoader.getUnionClass(GnunetMessage.Body.class, m.payloadHeader.messageType);
184 @SuppressWarnings("unchecked")
185 GnunetMessage.Body b = (GnunetMessage.Body) Construct.parseAs(m.payloadBody, bodyClass);
186 messageHandler.visitAppropriate(b);
187 }
188 }
189
190 public void visit(NotifyOutboundTrafficMessage m) {
191 if (notifyOutboundHeaders != null) {
192 notifyOutboundHeaders.notify(m.payloadHeader);
193 }
194 if (notifyOutboundMessages != null) {
195 // todo
196 }
197 }
198
199 public void visit(SendMessageReady m) {
200 RequestIdentification rid = new RequestIdentification(m.smrId, m.peer);
201 NotifyTransmitReadyRequest req = ntr_requests.getRequest(rid);
202
203 final SendMessage sm = new SendMessage();
204 sm.cork = 0;
205 sm.peer = req.target;
206 sm.priority = req.priority;
207 sm.deadline = req.deadline.asMessage();
208
209 req.transmitter.transmit(new Connection.MessageSink() {
210 boolean sent;
211 @Override
212 public void send(GnunetMessage.Body m) {
213 if (sent) {
214 throw new AssertionError("sending multiple messages not supported");
215 }
216 sm.payloadMessage = GnunetMessage.fromBody(m);
217 sent = true;
218 }
219 });
220
221
222 if (sm.payloadMessage == null)
223 throw new AssertionError();
224
225 client.send(sm);
226 }
227
228 @Override
229 public void visitDefault(Object o) {
230 logger.warn("received unexpected message from core: {}", o.getClass());
231 }
232
233 @Override
234 public void handleError() {
235 if (disconnectHandler != null) {
236 for (PeerIdentity e : connectedPeers.keySet()) {
237 disconnectHandler.onDisconnect(e);
238 }
239 }
240 connectedPeers.clear();
241 }
242 }
243
244 public Core(Configuration cfg) {
245 client = new Client("core", cfg);
246 client.installReceiver(new CoreReceiver());
247 ntr_requests = new MatchingRequestContainer<RequestIdentification, NotifyTransmitReadyRequest>(client);
248 }
249
250 /**
251 * Send to the service which messages are we interested in.
252 *
253 * @param initCallback called after the init message has been sent
254 */
255 public void init(InitCallback initCallback) {
256 this.initCallback = initCallback;
257 InitMessage initMessage = new InitMessage();
258
259 initMessage.interested = interested;
260 initMessage.options = 0;
261
262 for (int i : interested) {
263 logger.debug("we are interested in " + i);
264 }
265 client.sendPrefered(initMessage);
266 }
267
268 /**
269 * Ask the core to call "notify" once it is ready to transmit the
270 * given number of bytes to the specified "target". Must only be
271 * called after a connection to the respective peer has been
272 * established (and the client has been informed about this).
273 *
274 * @param priority how important is the message?
275 * @param maxdelay how long can the message wait?
276 * @param target the identity of the receiver
277 * @param size the size of the message we want to transmit
278 * @param transmitter called once the core service is ready to send message
279 * @return a handle to cancel the notification
280 */
281 public Cancelable notifyTransmitReady(int priority, RelativeTime maxdelay,
282 PeerIdentity target, int size, final MessageTransmitter transmitter) {
283 if (!connectedPeers.containsKey(target)) {
284 throw new AssertionError("notifyTransmitReady called for unconnected peer");
285 }
286 int id = connectedPeers.get(target);
287 connectedPeers.put(target, id+1);
288 NotifyTransmitReadyRequest notifyRequest = new NotifyTransmitReadyRequest(priority, size, target, maxdelay, transmitter);
289 notifyRequest.smrId = id;
290 RequestIdentification rid = new RequestIdentification(notifyRequest.smrId, target);
291 return ntr_requests.addRequest(rid, notifyRequest);
292 }
293
294 /**
295 * Observe outgoing message headers from core.
296 * @param h callback
297 */
298 public void observeOutboundHeaders(HeaderNotify h) {
299 this.notifyOutboundHeaders = h;
300 }
301
302 public void observeInboundHeaders(HeaderNotify h) {
303 this.notifyInboundHeaders = h;
304 }
305
306 public void observeInboundMessages(MessageNotify h) {
307 this.notifyInboundMessages = h;
308 }
309
310 public void observeOutboundMessages(MessageNotify h) {
311 this.notifyOutboundMessages = h;
312 }
313
314 public void observeConnect(ConnectHandler connectHandler) {
315 this.connectHandler = connectHandler;
316 }
317
318 public void observeDisconnect(DisconnectHandler disconnectHandler) {
319 this.disconnectHandler = disconnectHandler;
320 }
321
322 /**
323 * Handle all incoming messages with the specified runabout.
324 * Has to be called before init, as the service has to know which messages we
325 * are interested in.
326 */
327 public void setMessageHandler(Runabout runabout) {
328 if (messageHandler != null) {
329 throw new AssertionError("Core can have only on message handler");
330 }
331 if (client.isConnected()) {
332 // todo: shouldn't we just reconnect?
333 throw new AssertionError("can set message handler only if not yet connected");
334 }
335 messageHandler = runabout;
336 interested = RunaboutUtil.getRunaboutMessageTypes(runabout);
337 }
338
339 /**
340 * Disconnect from the core service. This function can only
341 * be called *after* all pending notifyTransmitReady
342 * requests have been explicitly cancelled.
343 */
344 public void disconnect() {
345 client.disconnect();
346 }
347}
diff --git a/src/main/java/org/gnunet/core/DisconnectHandler.java b/src/main/java/org/gnunet/core/DisconnectHandler.java
new file mode 100644
index 0000000..c7ca407
--- /dev/null
+++ b/src/main/java/org/gnunet/core/DisconnectHandler.java
@@ -0,0 +1,30 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.core;
22
23import org.gnunet.util.PeerIdentity;
24
25/**
26 * Called when a peer disconnects from the core.
27 */
28public interface DisconnectHandler {
29 void onDisconnect(PeerIdentity peerIdentity);
30}
diff --git a/src/main/java/org/gnunet/core/DisconnectNotifyMessage.java b/src/main/java/org/gnunet/core/DisconnectNotifyMessage.java
new file mode 100644
index 0000000..e4c3209
--- /dev/null
+++ b/src/main/java/org/gnunet/core/DisconnectNotifyMessage.java
@@ -0,0 +1,46 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.core;
22
23import org.gnunet.construct.NestedMessage;
24import org.gnunet.construct.UInt32;
25import org.gnunet.construct.UnionCase;
26import org.gnunet.util.GnunetMessage;
27import org.gnunet.util.PeerIdentity;
28
29/**
30 * Message sent by the service to clients to notify them
31 * about a peer disconnecting.
32 */
33@UnionCase(68)
34public class DisconnectNotifyMessage implements GnunetMessage.Body {
35 /**
36 * Always zero.
37 */
38 @UInt32
39 public int reserved;
40
41 /**
42 * Identity of the connecting peer.
43 */
44 @NestedMessage
45 public PeerIdentity peer;
46}
diff --git a/src/main/java/org/gnunet/core/HeaderNotify.java b/src/main/java/org/gnunet/core/HeaderNotify.java
new file mode 100644
index 0000000..4f536e3
--- /dev/null
+++ b/src/main/java/org/gnunet/core/HeaderNotify.java
@@ -0,0 +1,30 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.core;
22
23import org.gnunet.util.GnunetMessage;
24
25/**
26 *
27 */
28public interface HeaderNotify {
29 void notify(GnunetMessage.Header header);
30}
diff --git a/src/main/java/org/gnunet/core/InitCallback.java b/src/main/java/org/gnunet/core/InitCallback.java
new file mode 100644
index 0000000..889f8cf
--- /dev/null
+++ b/src/main/java/org/gnunet/core/InitCallback.java
@@ -0,0 +1,30 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.core;
22
23import org.gnunet.util.PeerIdentity;
24
25/**
26 * Called once the handshake with core was successful.
27 */
28public interface InitCallback {
29 void onInit(PeerIdentity myIdentity);
30}
diff --git a/src/main/java/org/gnunet/core/InitMessage.java b/src/main/java/org/gnunet/core/InitMessage.java
new file mode 100644
index 0000000..5546088
--- /dev/null
+++ b/src/main/java/org/gnunet/core/InitMessage.java
@@ -0,0 +1,45 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.core;
22
23import org.gnunet.construct.IntegerFill;
24import org.gnunet.construct.UInt32;
25import org.gnunet.construct.UnionCase;
26import org.gnunet.util.GnunetMessage;
27
28
29@UnionCase(64)
30public class InitMessage implements GnunetMessage.Body {
31 /*
32 * Options used to tell core what kind of traffic notify messages we are interested in.
33 */
34 private final static int
35 OPTION_FULL_INBOUND = 8,
36 OPTION_HDR_INBOUND = 16,
37 OPTION_FULL_OUTBOUND = 32,
38 OPTION_HDR_OUTBOUND = 64;
39
40 @UInt32
41 public long options;
42
43 @IntegerFill(signed = false, bitSize = 16)
44 public int[] interested;
45}
diff --git a/src/main/java/org/gnunet/core/InitReplyMessage.java b/src/main/java/org/gnunet/core/InitReplyMessage.java
new file mode 100644
index 0000000..02e8eef
--- /dev/null
+++ b/src/main/java/org/gnunet/core/InitReplyMessage.java
@@ -0,0 +1,39 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.core;
22
23import org.gnunet.construct.NestedMessage;
24import org.gnunet.construct.UInt32;
25import org.gnunet.construct.UnionCase;
26import org.gnunet.util.GnunetMessage;
27import org.gnunet.util.PeerIdentity;
28
29
30@UnionCase(65)
31public class InitReplyMessage implements GnunetMessage.Body {
32 @UInt32
33 public int reserved = 0;
34 /**
35 * pubkey of the local peer
36 */
37 @NestedMessage
38 public PeerIdentity myIdentity;
39}
diff --git a/src/main/java/org/gnunet/core/MessageNotify.java b/src/main/java/org/gnunet/core/MessageNotify.java
new file mode 100644
index 0000000..b14ce29
--- /dev/null
+++ b/src/main/java/org/gnunet/core/MessageNotify.java
@@ -0,0 +1,28 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.core;
22
23import org.gnunet.util.GnunetMessage;
24
25
26public interface MessageNotify {
27 void notify(GnunetMessage messageBody);
28}
diff --git a/src/main/java/org/gnunet/core/NotifyInboundTrafficMessage.java b/src/main/java/org/gnunet/core/NotifyInboundTrafficMessage.java
new file mode 100644
index 0000000..2bdd428
--- /dev/null
+++ b/src/main/java/org/gnunet/core/NotifyInboundTrafficMessage.java
@@ -0,0 +1,47 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.core;
22
23import org.gnunet.construct.*;
24import org.gnunet.util.ATSInformation;
25import org.gnunet.util.GnunetMessage;
26import org.gnunet.util.PeerIdentity;
27
28
29@UnionCase(70)
30public class NotifyInboundTrafficMessage implements GnunetMessage.Body {
31 /**
32 * Identity of the receiver or sender.
33 */
34 @NestedMessage
35 public PeerIdentity peer;
36
37 @NestedMessage(newFrame = true)
38 public GnunetMessage.Header payloadHeader;
39
40 /**
41 * The (optional) message body corresponding to payloadHeader.
42 * Not typed as GnunetMessage.Body because the message type may not be known by this
43 * peer.
44 */
45 @FillWith @UInt8
46 public byte[] payloadBody;
47}
diff --git a/src/main/java/org/gnunet/core/NotifyOutboundTrafficMessage.java b/src/main/java/org/gnunet/core/NotifyOutboundTrafficMessage.java
new file mode 100644
index 0000000..900f8be
--- /dev/null
+++ b/src/main/java/org/gnunet/core/NotifyOutboundTrafficMessage.java
@@ -0,0 +1,58 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.core;
22
23import org.gnunet.construct.*;
24import org.gnunet.util.ATSInformation;
25import org.gnunet.util.GnunetMessage;
26import org.gnunet.util.PeerIdentity;
27
28
29@UnionCase(71)
30public class NotifyOutboundTrafficMessage implements GnunetMessage.Body {
31 /**
32 * Number of ATS key-value pairs that follow this struct
33 * (excluding the 0-terminator).
34 */
35 @UInt32
36 public long ats_count;
37
38 /**
39 * Identity of the receiver or sender.
40 */
41 @NestedMessage
42 public PeerIdentity peer;
43
44 @VariableSizeArray(lengthField = "ats_count")
45 public ATSInformation[] atsRest;
46
47 @NestedMessage(newFrame = true)
48 public GnunetMessage.Header payloadHeader;
49
50 /**
51 * The (optional) message body corresponding to payloadHeader.
52 * Not typed as GnunetMessage.Body because the message type may not be known by this
53 * peer.
54 */
55 @FillWith @UInt8
56 public byte[] payloadBody;
57
58}
diff --git a/src/main/java/org/gnunet/core/RequestIdentification.java b/src/main/java/org/gnunet/core/RequestIdentification.java
new file mode 100644
index 0000000..4f6a734
--- /dev/null
+++ b/src/main/java/org/gnunet/core/RequestIdentification.java
@@ -0,0 +1,35 @@
1package org.gnunet.core;
2
3import org.gnunet.peerinfo.PeerInfo;
4import org.gnunet.util.PeerIdentity;
5
6
7final class RequestIdentification {
8 public final int requestIdentifier;
9 public final PeerIdentity peerIdentity;
10
11 public RequestIdentification(int requestIdentifier, PeerIdentity peerIdentity) {
12 this.requestIdentifier = requestIdentifier;
13 this.peerIdentity = peerIdentity;
14 }
15
16 @Override
17 public boolean equals(Object o) {
18 if (this == o) return true;
19 if (o == null || getClass() != o.getClass()) return false;
20
21 RequestIdentification that = (RequestIdentification) o;
22
23 if (requestIdentifier != that.requestIdentifier) return false;
24 if (!peerIdentity.equals(that.peerIdentity)) return false;
25
26 return true;
27 }
28
29 @Override
30 public int hashCode() {
31 int result = requestIdentifier;
32 result = 31 * result + peerIdentity.hashCode();
33 return result;
34 }
35}
diff --git a/src/main/java/org/gnunet/core/SendMessage.java b/src/main/java/org/gnunet/core/SendMessage.java
new file mode 100644
index 0000000..e4c6215
--- /dev/null
+++ b/src/main/java/org/gnunet/core/SendMessage.java
@@ -0,0 +1,71 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.core;
22
23import org.gnunet.construct.NestedMessage;
24import org.gnunet.construct.UInt32;
25import org.gnunet.construct.UInt64;
26import org.gnunet.construct.UnionCase;
27import org.gnunet.util.AbsoluteTimeMessage;
28import org.gnunet.util.GnunetMessage;
29import org.gnunet.util.PeerIdentity;
30
31/**
32 * Client asking core to transmit a particular message to a particular
33 * target (response to GNUNET_MESSAGE_TYPE_CORE_SEND_READY).
34 */
35@UnionCase(76)
36public class SendMessage implements GnunetMessage.Body {
37 /**
38 * How important is this message?
39 */
40 @UInt32
41 public long priority;
42
43 /**
44 * By what time would the sender really like to see this
45 * message transmitted?
46 */
47 @NestedMessage
48 public AbsoluteTimeMessage deadline;
49
50 /**
51 * Identity of the intended receiver.
52 */
53 @NestedMessage
54 public PeerIdentity peer;
55
56 /**
57 * GNUNET_YES if corking is allowed, GNUNET_NO if not.
58 */
59 @UInt32
60 public int cork;
61
62 /**
63 * Always 0.
64 */
65 @UInt64
66 public int reserved;
67
68 @NestedMessage(newFrame = true)
69 public GnunetMessage payloadMessage;
70
71}
diff --git a/src/main/java/org/gnunet/core/SendMessageReady.java b/src/main/java/org/gnunet/core/SendMessageReady.java
new file mode 100644
index 0000000..aa5bf44
--- /dev/null
+++ b/src/main/java/org/gnunet/core/SendMessageReady.java
@@ -0,0 +1,56 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.core;
22
23import org.gnunet.construct.NestedMessage;
24import org.gnunet.construct.UInt16;
25import org.gnunet.construct.UnionCase;
26import org.gnunet.util.GnunetMessage;
27import org.gnunet.util.PeerIdentity;
28
29/**
30 * Core notifying client that it is allowed to now
31 * transmit a message to the given target
32 * (response to GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST).
33 */
34@UnionCase(75)
35public class SendMessageReady implements GnunetMessage.Body {
36 /**
37 * How many bytes are allowed for transmission?
38 * Guaranteed to be at least as big as the requested size,
39 * or ZERO if the request is rejected (will timeout,
40 * peer disconnected, queue full, etc.).
41 */
42 @UInt16
43 public int size;
44
45 /**
46 * smrId from the request.
47 */
48 @UInt16
49 public int smrId;
50
51 /**
52 * Identity of the intended target.
53 */
54 @NestedMessage
55 public PeerIdentity peer;
56}
diff --git a/src/main/java/org/gnunet/core/SendMessageRequest.java b/src/main/java/org/gnunet/core/SendMessageRequest.java
new file mode 100644
index 0000000..7a95127
--- /dev/null
+++ b/src/main/java/org/gnunet/core/SendMessageRequest.java
@@ -0,0 +1,73 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.core;
22
23import org.gnunet.construct.NestedMessage;
24import org.gnunet.construct.UInt16;
25import org.gnunet.construct.UInt32;
26import org.gnunet.construct.UnionCase;
27import org.gnunet.util.AbsoluteTimeMessage;
28import org.gnunet.util.GnunetMessage;
29import org.gnunet.util.PeerIdentity;
30
31/**
32 * Client notifying core about the maximum-priority
33 * message it has in the queue for a particular target.
34 */
35@UnionCase(74)
36public class SendMessageRequest implements GnunetMessage.Body {
37 /**
38 * How important is this message?
39 */
40 @UInt32
41 public long priority;
42
43 /**
44 * By what time would the sender really like to see this
45 * message transmitted?
46 */
47 @NestedMessage
48 public AbsoluteTimeMessage deadline;
49
50 /**
51 * Identity of the intended target.
52 */
53 @NestedMessage
54 public PeerIdentity peer;
55
56 /**
57 * How large is the client's message queue for this peer?
58 */
59 @UInt32
60 public byte reserved;
61
62 /**
63 * How large is the message?
64 */
65 @UInt16
66 public int size;
67
68 /**
69 * Counter for this peer to match SMRs to replies.
70 */
71 @UInt16
72 public int smrId;
73}
diff --git a/src/main/java/org/gnunet/core/package-info.java b/src/main/java/org/gnunet/core/package-info.java
new file mode 100644
index 0000000..64e5d59
--- /dev/null
+++ b/src/main/java/org/gnunet/core/package-info.java
@@ -0,0 +1,24 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21/**
22 * API for the gnunet core service.
23 */
24package org.gnunet.core;
diff --git a/src/main/java/org/gnunet/dht/BlockType.java b/src/main/java/org/gnunet/dht/BlockType.java
new file mode 100644
index 0000000..cf00d38
--- /dev/null
+++ b/src/main/java/org/gnunet/dht/BlockType.java
@@ -0,0 +1,81 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.dht;
22
23/**
24 * Information on how to interpret a block of data.
25 */
26public enum BlockType {
27 /**
28 * Any type of block, used as a wildcard when searching. Should
29 * never be attached to a specific block.
30 */
31 ANY(0),
32 /**
33 * Data block (leaf) in the CHK tree.
34 */
35 DBLOCK(1),
36 /**
37 * Inner block in the CHK tree.
38 */
39 IBLOCK(2),
40 /**
41 * Type of a block representing a keyword search result. Note that
42 * the values for KBLOCK, SBLOCK and NBLOCK must be consecutive.
43 */
44 KBLOCK(3),
45 /**
46 * Type of a block that is used to advertise content in a namespace.
47 */
48 SBLOCK(4),
49 /**
50 * Type of a block that is used to advertise a namespace.
51 */
52 NBLOCK(5),
53 /**
54 * Type of a block representing a block to be encoded on demand from disk.
55 * Should never appear on the network directly.
56 */
57 FS_ONDEMAND(6),
58 /**
59 * Type of a block that contains a HELLO for a peer (for
60 * DHT find-peer operations).
61 */
62 DHT_HELLO(7),
63 /**
64 * Block for testing.
65 */
66 TEST(8),
67 /**
68 * Block for storing .gnunet-domains
69 */
70 DNS(10),
71 /**
72 * Block for storing record data
73 */
74 NAMERECORD(11);
75
76 public final int val;
77
78 BlockType(int val) {
79 this.val = val;
80 }
81}
diff --git a/src/main/java/org/gnunet/dht/ClientGetMessage.java b/src/main/java/org/gnunet/dht/ClientGetMessage.java
new file mode 100644
index 0000000..cd317fb
--- /dev/null
+++ b/src/main/java/org/gnunet/dht/ClientGetMessage.java
@@ -0,0 +1,51 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.dht;
22
23import org.gnunet.construct.*;
24import org.gnunet.util.GnunetMessage;
25import org.gnunet.util.HashCode;
26
27/**
28* Created with IntelliJ IDEA.
29* User: dold
30* Date: 5/2/12
31* Time: 7:05 PM
32* To change this template use File | Settings | File Templates.
33*/
34@UnionCase(143)
35public class ClientGetMessage implements GnunetMessage.Body {
36 /**
37 * Combination of RouteOption.*
38 */
39 @UInt32
40 public int options;
41 @UInt32
42 public int desiredReplicationLevel;
43 @UInt32
44 public int type;
45 @NestedMessage
46 public HashCode key;
47 @UInt64
48 public long uniqueId;
49 @FillWith @UInt8
50 public byte[] xquery;
51}
diff --git a/src/main/java/org/gnunet/dht/ClientGetStopMessage.java b/src/main/java/org/gnunet/dht/ClientGetStopMessage.java
new file mode 100644
index 0000000..4cdee12
--- /dev/null
+++ b/src/main/java/org/gnunet/dht/ClientGetStopMessage.java
@@ -0,0 +1,45 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.dht;
22
23import org.gnunet.construct.NestedMessage;
24import org.gnunet.construct.UInt32;
25import org.gnunet.construct.UInt64;
26import org.gnunet.construct.UnionCase;
27import org.gnunet.util.GnunetMessage;
28import org.gnunet.util.HashCode;
29
30/**
31* Created with IntelliJ IDEA.
32* User: dold
33* Date: 5/2/12
34* Time: 7:05 PM
35* To change this template use File | Settings | File Templates.
36*/
37@UnionCase(144)
38public class ClientGetStopMessage implements GnunetMessage.Body {
39 @UInt32
40 public int reserved = 0;
41 @UInt64
42 public long unique_id;
43 @NestedMessage
44 public HashCode key;
45}
diff --git a/src/main/java/org/gnunet/dht/ClientPutConfirmationMessage.java b/src/main/java/org/gnunet/dht/ClientPutConfirmationMessage.java
new file mode 100644
index 0000000..45bbe60
--- /dev/null
+++ b/src/main/java/org/gnunet/dht/ClientPutConfirmationMessage.java
@@ -0,0 +1,38 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.dht;
22
23import org.gnunet.construct.UInt32;
24import org.gnunet.construct.UInt64;
25import org.gnunet.construct.UnionCase;
26import org.gnunet.util.GnunetMessage;
27
28
29@UnionCase(155)
30public class ClientPutConfirmationMessage implements GnunetMessage.Body {
31 @UInt32
32 public int reserved;
33 /**
34 * UID used to identify request with the response
35 */
36 @UInt64
37 public long uid;
38}
diff --git a/src/main/java/org/gnunet/dht/ClientPutMessage.java b/src/main/java/org/gnunet/dht/ClientPutMessage.java
new file mode 100644
index 0000000..4b63e92
--- /dev/null
+++ b/src/main/java/org/gnunet/dht/ClientPutMessage.java
@@ -0,0 +1,54 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.dht;
22
23import org.gnunet.construct.*;
24import org.gnunet.util.AbsoluteTimeMessage;
25import org.gnunet.util.GnunetMessage;
26import org.gnunet.util.HashCode;
27
28
29@UnionCase(142)
30public class ClientPutMessage implements GnunetMessage.Body {
31 /**
32 * Type of data to insert, one of BlockType.*
33 */
34 @UInt32
35 public int type;
36 /**
37 * Combination of RouteOption.*
38 */
39 @UInt32
40 public int options;
41 @UInt32
42 public int desiredReplicationLevel;
43 /**
44 * UID used to identify request with the response
45 */
46 @UInt64
47 public long uid;
48 @NestedMessage
49 public AbsoluteTimeMessage expiration;
50 @NestedMessage
51 public HashCode hash;
52 @FillWith @UInt8
53 public byte[] data;
54}
diff --git a/src/main/java/org/gnunet/dht/ClientResultMessage.java b/src/main/java/org/gnunet/dht/ClientResultMessage.java
new file mode 100644
index 0000000..fab614f
--- /dev/null
+++ b/src/main/java/org/gnunet/dht/ClientResultMessage.java
@@ -0,0 +1,56 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.dht;
22
23import org.gnunet.construct.*;
24import org.gnunet.util.AbsoluteTimeMessage;
25import org.gnunet.util.GnunetMessage;
26import org.gnunet.util.HashCode;
27import org.gnunet.util.PeerIdentity;
28
29/**
30* Created with IntelliJ IDEA.
31* User: dold
32* Date: 5/2/12
33* Time: 7:06 PM
34* To change this template use File | Settings | File Templates.
35*/
36@UnionCase(145)
37public class ClientResultMessage implements GnunetMessage.Body {
38 @UInt32
39 public int type;
40 @UInt32
41 public int putPathLength;
42 @UInt32
43 public int getPathLength;
44 @UInt64
45 public long uid;
46 @NestedMessage
47 public AbsoluteTimeMessage expiration;
48 @NestedMessage
49 public HashCode key;
50 @VariableSizeArray(lengthField = "putPathLength")
51 public PeerIdentity[] putPath;
52 @VariableSizeArray(lengthField = "getPathLength")
53 public PeerIdentity[] getPath;
54 @FillWith @UInt8
55 public byte[] data;
56}
diff --git a/src/main/java/org/gnunet/dht/DistributedHashTable.java b/src/main/java/org/gnunet/dht/DistributedHashTable.java
new file mode 100644
index 0000000..0a561bf
--- /dev/null
+++ b/src/main/java/org/gnunet/dht/DistributedHashTable.java
@@ -0,0 +1,449 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.dht;
22
23import com.google.common.base.Charsets;
24import org.gnunet.mq.Envelope;
25import org.gnunet.requests.MatchingRequestContainer;
26import org.gnunet.requests.RequestContainer;
27import org.gnunet.requests.SequentialRequestContainer;
28import org.gnunet.util.*;
29import org.gnunet.util.getopt.Argument;
30import org.gnunet.util.getopt.ArgumentAction;
31import org.slf4j.Logger;
32import org.slf4j.LoggerFactory;
33
34import java.util.*;
35
36/**
37 * API for the gnunet dht service.
38 * <p/>
39 * Stores data under a key, distributed across the network.
40 * <p/>
41 */
42public class DistributedHashTable {
43 private static final Logger logger = LoggerFactory
44 .getLogger(DistributedHashTable.class);
45
46 private Client client;
47
48 /**
49 * next UID used on get/monitor requests, incremented after each use.
50 */
51 private long nextUID = 1;
52
53 private MatchingRequestContainer<Long, PutRequest> putRequests;
54 private MatchingRequestContainer<Long, GetRequest> getRequests;
55 private SequentialRequestContainer<MonitorRequest> monitorRequests;
56
57 private class PutRequest extends RequestContainer.Request {
58 public byte[] data;
59 public HashCode key;
60 public int replicationLevel;
61 public AbsoluteTime expiration;
62 public int type;
63 public Continuation cont;
64 public long uid;
65
66 public PutRequest() {
67 this.uid = nextUID++;
68 }
69
70 @Override
71 public Envelope assembleRequest() {
72 final ClientPutMessage cpm = new ClientPutMessage();
73 cpm.data = data;
74 cpm.hash = key;
75 cpm.desiredReplicationLevel = replicationLevel;
76 cpm.expiration = expiration.asMessage();
77 cpm.type = type;
78 cpm.uid = uid;
79 return new Envelope(cpm);
80 }
81
82 public void cancel() {
83 }
84 }
85
86
87 private class GetRequest extends RequestContainer.Request {
88 public long uid;
89 public HashCode key;
90 public ResultCallback cb;
91 public int type;
92 public int replication;
93 public byte[] xquery;
94
95 public GetRequest() {
96 uid = DistributedHashTable.this.nextUID++;
97 }
98
99 @Override
100 public Envelope assembleRequest() {
101 ClientGetMessage gm = new ClientGetMessage();
102 gm.desiredReplicationLevel = replication;
103 gm.type = type;
104 gm.xquery = xquery == null ? new byte[0] : xquery;
105 gm.key = key;
106 gm.uniqueId = uid;
107 return new Envelope(gm);
108 }
109 public void cancel() {
110
111 }
112 }
113
114 private class MonitorRequest extends RequestContainer.Request {
115 public int blockType;
116 public HashCode key;
117 public MonitorGetHandler getHandler;
118 public MonitorGetResponseHandler getResponseHandler;
119 public MonitorPutHandler putHandler;
120
121 @Override
122 public Envelope assembleRequest() {
123 MonitorStartStop mss = new MonitorStartStop();
124 if (key != null) {
125 mss.filter_key = 1;
126 mss.key = key;
127 } else {
128 mss.key = new HashCode();
129 }
130 if (getHandler != null) {
131 mss.get = 1;
132 }
133 if (getResponseHandler != null) {
134 mss.getResp = 1;
135 }
136 if (putHandler != null) {
137 mss.put = 1;
138 }
139 mss.type = blockType;
140 return new Envelope(mss);
141 }
142
143 public void cancel() {
144 // todo: use priority requests
145 MonitorRequest cancelRequest = new MonitorRequest();
146 cancelRequest.getHandler = null;
147 cancelRequest.getResponseHandler = null;
148 cancelRequest.putHandler = null;
149 monitorRequests.addRequest(cancelRequest);
150
151 monitorRequests.addRequest(cancelRequest);
152 }
153 }
154
155 private class DHTMessageReceiver extends RunaboutMessageReceiver {
156 public void visit(ClientPutConfirmationMessage pcm) {
157 PutRequest thePutRequest = putRequests.getRequest(pcm.uid);
158 if (thePutRequest == null) {
159 logger.warn("request UID not found");
160 return;
161 }
162 if (thePutRequest.cont != null) {
163 thePutRequest.cont.cont(true);
164 }
165 }
166
167 public void visit(ClientResultMessage rm) {
168 GetRequest theGetRequest = getRequests.getRequest(rm.uid);
169 if (theGetRequest == null) {
170 logger.warn("request UID not found");
171 return;
172 }
173 theGetRequest.cb.handleResult(AbsoluteTime.fromNetwork(rm.expiration), rm.key, null, null,
174 BlockType.TEST,
175 rm.data);
176 }
177
178 public void visit(MonitorGetMessage monitorGetMessage) {
179 for (MonitorRequest monitorRequest : monitorRequests.iter()) {
180 boolean type_ok = (monitorGetMessage.type == BlockType.ANY.val)
181 || (monitorGetMessage.type == monitorRequest.blockType);
182 boolean key_ok = monitorGetMessage.key.isAllZero()
183 || monitorGetMessage.key.equals(monitorRequest.key);
184
185 if (key_ok && type_ok && monitorRequest.getHandler != null) {
186 monitorRequest.getHandler.onGet(monitorGetMessage.options, monitorGetMessage.type,
187 monitorGetMessage.hop_count, monitorGetMessage.desired_replication_level, monitorGetMessage.getPath,
188 monitorGetMessage.key);
189 }
190 }
191 }
192
193 public void visit(MonitorGetRespMessage monitorGetRespMessage) {
194 for (MonitorRequest monitorRequest : monitorRequests.iter()) {
195 boolean type_ok = (monitorGetRespMessage.type == BlockType.ANY.val)
196 || (monitorGetRespMessage.type == monitorRequest.blockType);
197 boolean key_ok = monitorGetRespMessage.key.isAllZero()
198 || monitorGetRespMessage.key.equals(monitorRequest.key);
199
200 if (key_ok && type_ok && monitorRequest.getResponseHandler != null) {
201 monitorRequest.getResponseHandler.onGetResponse(
202 monitorGetRespMessage.type,
203 monitorGetRespMessage.getPath,
204 monitorGetRespMessage.putPath,
205 monitorGetRespMessage.expiration,
206 monitorGetRespMessage.key,
207 monitorGetRespMessage.data);
208 }
209 }
210
211 }
212
213 public void visit(MonitorPutMessage monitorPutMessage) {
214 for (MonitorRequest monitorRequest : monitorRequests.iter()) {
215 boolean type_ok = (monitorPutMessage.type == BlockType.ANY.val)
216 || (monitorPutMessage.type == monitorRequest.blockType);
217 boolean key_ok = monitorPutMessage.key.isAllZero()
218 || monitorPutMessage.key.equals(monitorRequest.key);
219
220 if (key_ok && type_ok && monitorRequest.putHandler != null) {
221 monitorRequest.putHandler.onPut(monitorPutMessage.options, monitorPutMessage.type,
222 monitorPutMessage.hop_count, monitorPutMessage.expirationTime,
223 monitorPutMessage.putPath, monitorPutMessage.key, monitorPutMessage.data);
224 }
225 }
226 }
227
228 @Override
229 public void handleError() {
230 }
231 }
232
233
234 /**
235 * Create a connection with the DHT service.
236 *
237 * @param cfg the configuration to use
238 */
239 public DistributedHashTable(Configuration cfg) {
240 client = new Client("dht", cfg);
241 client.installReceiver(new DHTMessageReceiver());
242 putRequests = new MatchingRequestContainer<Long, PutRequest>(client);
243 getRequests = new MatchingRequestContainer<Long, GetRequest>(client);
244 monitorRequests = new SequentialRequestContainer<MonitorRequest>(client);
245 }
246
247 /**
248 * Put data into the dht.
249 *
250 * @param key key key to store the data under
251 * @param data data data to store
252 * @param replicationLevel how many peers should store this value
253 * @param routeOptions additional options
254 * @param type type of the data to store
255 * @param expiration how long should the value be stored? TODO: what is the maximum?
256 * @param timeout how long after we give up on storing the value?
257 * @param cont called after the put operation failed or succeeded
258 */
259 public void put(HashCode key, byte[] data, int replicationLevel, Set<RouteOption> routeOptions,
260 int type, AbsoluteTime expiration,
261 RelativeTime timeout, final Continuation cont) {
262 PutRequest pr = new PutRequest();
263 pr.key = key;
264 pr.data = data;
265 pr.replicationLevel = replicationLevel;
266 pr.expiration = expiration;
267 pr.type = type;
268 pr.cont = cont;
269
270 putRequests.addRequest(pr.uid, pr);
271 }
272
273
274 /**
275 * Request results from the DHT.
276 *
277 * @param timeout timeout for the request
278 * @param type which type of data do we want to query for? (the DHT does not support TYPE_ANY)
279 * @param key the key we want to query
280 * @param replication how many peers do we want to ask?
281 * @param routeOptions extra routing options, null for default
282 * @param xquery extra query parameters, defaults to null
283 * @param cb the callback object for results or failure indication
284 * @return a handle to cancel the request
285 */
286 public Cancelable startGet(RelativeTime timeout, int type, HashCode key,
287 int replication, EnumSet<RouteOption> routeOptions,
288 byte[] xquery, ResultCallback cb) {
289
290 final GetRequest getRequest = new GetRequest();
291 getRequest.key = key;
292 getRequest.cb = cb;
293 getRequest.type = type;
294 getRequest.replication = type;
295 getRequest.xquery = xquery;
296
297 return getRequests.addRequest(getRequest.uid, getRequest);
298 }
299
300 public Cancelable startMonitor(int blockType, HashCode key, MonitorGetHandler getHandler,
301 MonitorGetResponseHandler getResponseHandler,
302 MonitorPutHandler putHandler) {
303 MonitorRequest monitorRequest = new MonitorRequest();
304 monitorRequest.blockType = blockType;
305 monitorRequest.key = key;
306 monitorRequest.getHandler = getHandler;
307 monitorRequest.getResponseHandler = getResponseHandler;
308 monitorRequest.putHandler = putHandler;
309
310 return monitorRequests.addRequest(monitorRequest);
311 }
312
313
314 /**
315 * Destroy the connection to the service.
316 */
317 public void destroy() {
318 // there's nothing to sync, just destroy!
319 client.disconnect();
320 }
321
322 public static void main(String[] args) {
323 new Program(args) {
324 @Argument(action = ArgumentAction.SET,
325 shortname = "p",
326 longname = "put",
327 description = "set a value in the DHT; default is get")
328 boolean modePut = false;
329
330 @Argument(action = ArgumentAction.SET,
331 shortname = "m",
332 longname = "monitor",
333 description = "monitor requests going to the local DHT")
334 boolean monitor = false;
335
336
337 @Argument(action = ArgumentAction.STORE_STRING,
338 shortname = "d",
339 longname = "data",
340 description = "data (only used with --put)")
341 String data = null;
342
343 @Argument(action = ArgumentAction.STORE_STRING,
344 shortname = "k",
345 longname = "key",
346 description = "key used for the operation")
347 String key = null;
348
349
350 // todo: implement the following options
351 /*
352 @Argument(action = ArgumentAction.STORE_STRING,
353 shortname = "t",
354 longname = "type",
355 description = "type of data used in this operation")
356 String type = null;
357
358 @Argument(action = ArgumentAction.STORE_STRING,
359 shortname = "e",
360 longname = "expire",
361 description = "expiration (ony use with --put)")
362 String expiration = null;
363 */
364
365
366 @Argument(action = ArgumentAction.STORE_NUMBER,
367 shortname = "r",
368 longname = "replication",
369 description = "desired replication (only used with --put)")
370 int replication = 5;
371
372
373 public void run() {
374 if (modePut) {
375
376 if (key == null) {
377 System.out.println("key required");
378 return;
379 }
380
381 if (data == null) {
382 System.out.println("data required on put");
383 return;
384 }
385 final DistributedHashTable dht = new DistributedHashTable(cfg);
386
387 dht.put(new HashCode(key), data.getBytes(), replication, EnumSet.of(RouteOption.NONE),
388 BlockType.TEST.val, AbsoluteTime.now().add(RelativeTime.HOUR),
389 RelativeTime.SECOND, new Continuation() {
390 @Override
391 public void cont(boolean success) {
392 if (success) {
393 System.out.println("put request sent");
394 } else {
395 System.out.println("error");
396 }
397 dht.destroy();
398 }
399 });
400 } else if (monitor) {
401 final DistributedHashTable dht = new DistributedHashTable(cfg);
402 dht.startMonitor(BlockType.TEST.val, null,
403 new MonitorGetHandler() {
404 @Override
405 public void onGet(int options, int type, int hop_count,
406 int desired_replication_level, PeerIdentity[] getPath, HashCode key) {
407 System.out.println("get monitored");
408 }
409 },
410 new MonitorGetResponseHandler() {
411 @Override
412 public void onGetResponse(int type, PeerIdentity[] getPath, PeerIdentity[] putPath,
413 AbsoluteTimeMessage expiration, HashCode key, byte[] data) {
414 System.out.println("get response monitored");
415 }
416 },
417 new MonitorPutHandler() {
418 @Override
419 public void onPut(int options, int type, int hop_count, AbsoluteTimeMessage
420 expirationTime, PeerIdentity[] putPath, HashCode key, byte[] data) {
421 System.out.println("put monitored");
422 }
423 });
424 } else { // get
425 if (key == null) {
426 System.out.println("key required");
427 return;
428 }
429 if (data != null) {
430 System.out.println("get does not take data as an option");
431 return;
432 }
433
434 final DistributedHashTable dht = new DistributedHashTable(cfg);
435
436 dht.startGet(RelativeTime.SECOND, BlockType.TEST.val, new HashCode(key), replication, null,
437 new byte[0], new ResultCallback() {
438 @Override
439 public void handleResult(AbsoluteTime expiration, HashCode key, List<PeerIdentity>
440 getPath, List<PeerIdentity> putPath, BlockType type, byte[] data) {
441 System.out.println("got result:");
442 System.out.println(new String(data, Charsets.UTF_8));
443 }
444 });
445 }
446 }
447 }.start();
448 }
449}
diff --git a/src/main/java/org/gnunet/dht/MonitorGetHandler.java b/src/main/java/org/gnunet/dht/MonitorGetHandler.java
new file mode 100644
index 0000000..c7dad7c
--- /dev/null
+++ b/src/main/java/org/gnunet/dht/MonitorGetHandler.java
@@ -0,0 +1,30 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.dht;
22
23import org.gnunet.util.HashCode;
24import org.gnunet.util.PeerIdentity;
25
26
27public interface MonitorGetHandler {
28 void onGet(int options, int type, int hop_count, int desired_replication_level, PeerIdentity[] getPath,
29 HashCode key);
30}
diff --git a/src/main/java/org/gnunet/dht/MonitorGetMessage.java b/src/main/java/org/gnunet/dht/MonitorGetMessage.java
new file mode 100644
index 0000000..e96ec96
--- /dev/null
+++ b/src/main/java/org/gnunet/dht/MonitorGetMessage.java
@@ -0,0 +1,75 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.dht;
22
23import org.gnunet.construct.NestedMessage;
24import org.gnunet.construct.UInt32;
25import org.gnunet.construct.UnionCase;
26import org.gnunet.construct.VariableSizeArray;
27import org.gnunet.util.GnunetMessage;
28import org.gnunet.util.HashCode;
29import org.gnunet.util.PeerIdentity;
30
31/**
32 * Message to monitor get requests going through peer, DHT service -> clients.
33 */
34@UnionCase(149)
35public class MonitorGetMessage implements GnunetMessage.Body {
36 /**
37 * Message options, actually an 'enum GNUNET_DHT_RouteOption' value.
38 */
39 @UInt32
40 public int options;
41
42 /**
43 * The type of data in the request.
44 */
45 @UInt32
46 public int type;
47
48 /**
49 * Hop count
50 */
51 @UInt32
52 public int hop_count;
53
54 /**
55 * Replication level for this message
56 */
57 @UInt32
58 public int desired_replication_level;
59
60 /**
61 * Number of peers recorded in the outgoing path from source to the
62 * storage location of this message.
63 */
64 @UInt32
65 public int get_path_length;
66
67 /**
68 * The key to store the value under.
69 */
70 @NestedMessage
71 public HashCode key;
72
73 @VariableSizeArray(lengthField = "get_path_length")
74 public PeerIdentity[] getPath;
75}
diff --git a/src/main/java/org/gnunet/dht/MonitorGetRespMessage.java b/src/main/java/org/gnunet/dht/MonitorGetRespMessage.java
new file mode 100644
index 0000000..3bf145b
--- /dev/null
+++ b/src/main/java/org/gnunet/dht/MonitorGetRespMessage.java
@@ -0,0 +1,72 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.dht;
22
23import org.gnunet.construct.*;
24import org.gnunet.util.AbsoluteTimeMessage;
25import org.gnunet.util.GnunetMessage;
26import org.gnunet.util.HashCode;
27import org.gnunet.util.PeerIdentity;
28
29/**
30 * Message to monitor get results going through peer, DHT service --> clients.
31 */
32@UnionCase(150)
33public class MonitorGetRespMessage implements GnunetMessage.Body {
34 /**
35 * Content type.
36 */
37 @UInt32
38 int type;
39
40 /**
41 * Length of the PUT path that follows (if tracked).
42 */
43 @UInt32
44 int put_path_length;
45
46 /**
47 * Length of the GET path that follows (if tracked).
48 */
49 @UInt32
50 int get_path_length;
51
52 /**
53 * When does the content expire?
54 */
55 @NestedMessage
56 public AbsoluteTimeMessage expiration;
57
58 /**
59 * The key of the corresponding GET request.
60 */
61 @NestedMessage
62 public HashCode key;
63
64 @VariableSizeArray(lengthField = "put_path_length")
65 public PeerIdentity[] putPath;
66
67 @VariableSizeArray(lengthField = "get_path_length")
68 public PeerIdentity[] getPath;
69
70 @FillWith @UInt8
71 public byte[] data;
72}
diff --git a/src/main/java/org/gnunet/dht/MonitorGetResponseHandler.java b/src/main/java/org/gnunet/dht/MonitorGetResponseHandler.java
new file mode 100644
index 0000000..ff03cce
--- /dev/null
+++ b/src/main/java/org/gnunet/dht/MonitorGetResponseHandler.java
@@ -0,0 +1,31 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.dht;
22
23import org.gnunet.util.AbsoluteTimeMessage;
24import org.gnunet.util.HashCode;
25import org.gnunet.util.PeerIdentity;
26
27
28public interface MonitorGetResponseHandler {
29 void onGetResponse(int type, PeerIdentity[] getPath, PeerIdentity[] putPath, AbsoluteTimeMessage expiration,
30 HashCode key, byte[] data);
31}
diff --git a/src/main/java/org/gnunet/dht/MonitorPutHandler.java b/src/main/java/org/gnunet/dht/MonitorPutHandler.java
new file mode 100644
index 0000000..0abb79b
--- /dev/null
+++ b/src/main/java/org/gnunet/dht/MonitorPutHandler.java
@@ -0,0 +1,31 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.dht;
22
23import org.gnunet.util.AbsoluteTimeMessage;
24import org.gnunet.util.HashCode;
25import org.gnunet.util.PeerIdentity;
26
27
28public interface MonitorPutHandler {
29 void onPut(int options, int type, int hop_count, AbsoluteTimeMessage expirationTime, PeerIdentity[] putPath,
30 HashCode key, byte[] data);
31}
diff --git a/src/main/java/org/gnunet/dht/MonitorPutMessage.java b/src/main/java/org/gnunet/dht/MonitorPutMessage.java
new file mode 100644
index 0000000..103c05b
--- /dev/null
+++ b/src/main/java/org/gnunet/dht/MonitorPutMessage.java
@@ -0,0 +1,82 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.dht;
22
23import org.gnunet.construct.*;
24import org.gnunet.util.AbsoluteTimeMessage;
25import org.gnunet.util.GnunetMessage;
26import org.gnunet.util.HashCode;
27import org.gnunet.util.PeerIdentity;
28
29/**
30 * Message to monitor put requests going through peer, DHT service --> clients.
31 */
32@UnionCase(151)
33public class MonitorPutMessage implements GnunetMessage.Body {
34 /**
35 * Message options, actually an 'enum GNUNET_DHT_RouteOption' value.
36 */
37 @UInt32
38 public int options;
39
40 /**
41 * The type of data in the request.
42 */
43 @UInt32
44 public int type;
45
46 /**
47 * Hop count so far.
48 */
49 @UInt32
50 public int hop_count;
51
52 /**
53 * Replication level for this message
54 */
55 @UInt32
56 public int desired_replication_level;
57
58 /**
59 * Number of peers recorded in the outgoing path from source to the
60 * storage location of this message.
61 */
62 @UInt32
63 public int put_path_length;
64
65 /**
66 * How long should this data persist?
67 */
68 @NestedMessage
69 public AbsoluteTimeMessage expirationTime;
70
71 /**
72 * The key to store the value under.
73 */
74 @NestedMessage
75 public HashCode key;
76
77 @VariableSizeArray(lengthField = "put_path_length")
78 public PeerIdentity[] putPath;
79
80 @FillWith @UInt8
81 public byte[] data;
82}
diff --git a/src/main/java/org/gnunet/dht/MonitorStartStop.java b/src/main/java/org/gnunet/dht/MonitorStartStop.java
new file mode 100644
index 0000000..b3f3268
--- /dev/null
+++ b/src/main/java/org/gnunet/dht/MonitorStartStop.java
@@ -0,0 +1,70 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.dht;
22
23import org.gnunet.construct.NestedMessage;
24import org.gnunet.construct.UInt16;
25import org.gnunet.construct.UInt32;
26import org.gnunet.construct.UnionCase;
27import org.gnunet.util.GnunetMessage;
28import org.gnunet.util.HashCode;
29
30/**
31 * Message to request monitoring messages, clients --> DHT service.
32 */
33@UnionCase(153)
34public class MonitorStartStop implements GnunetMessage.Body {
35 /**
36 * The type of data desired, GNUNET_BLOCK_TYPE_ANY for all.
37 */
38 @UInt32
39 public int type;
40
41 /**
42 * Flag whether to notify about GET messages.
43 */
44 @UInt16
45 public int get;
46
47 /**
48 * Flag whether to notify about GET_REPONSE messages.
49 */
50 @UInt16
51 public int getResp;
52
53 /**
54 * Flag whether to notify about PUT messages.
55 */
56 @UInt16
57 public int put;
58
59 /**
60 * Flag whether to use the provided key to filter messages.
61 */
62 @UInt16
63 public int filter_key;
64
65 /*
66 The key to filter messages by.
67 */
68 @NestedMessage
69 public HashCode key;
70}
diff --git a/src/main/java/org/gnunet/dht/ResultCallback.java b/src/main/java/org/gnunet/dht/ResultCallback.java
new file mode 100644
index 0000000..fa5c7ef
--- /dev/null
+++ b/src/main/java/org/gnunet/dht/ResultCallback.java
@@ -0,0 +1,46 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.dht;
22
23import org.gnunet.util.AbsoluteTime;
24import org.gnunet.util.HashCode;
25import org.gnunet.util.PeerIdentity;
26
27import java.util.List;
28
29/**
30 * Callback object for requests to the dht
31 */
32public interface ResultCallback {
33 /**
34 * Called when the dht returns a result
35 *
36 * @param expiration expiration of the returned entry
37 * @param key key of the returned entry
38 * @param getPath put path of the returned entry
39 * @param putPath put path of the returned entry
40 * @param type type of data in the entry
41 * @param data data of the returned entry
42 */
43 public void handleResult(AbsoluteTime expiration, HashCode key,
44 List<PeerIdentity> getPath, List<PeerIdentity> putPath,
45 BlockType type, byte[] data);
46}
diff --git a/src/main/java/org/gnunet/dht/RouteOption.java b/src/main/java/org/gnunet/dht/RouteOption.java
new file mode 100644
index 0000000..b74b528
--- /dev/null
+++ b/src/main/java/org/gnunet/dht/RouteOption.java
@@ -0,0 +1,55 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.dht;
22
23/**
24 * Options passed to the dht service for routing requests.
25 */
26enum RouteOption {
27 /**
28 * Default. Do nothing special.
29 */
30 NONE(0),
31 /**
32 * Each peer along the way should look at 'enc' (otherwise
33 * only the k-peers closest to the key should look at it).
34 */
35 DEMULTIPLEX_EVERYWHERE(1),
36 /**
37 * We should keep track of the route that the message
38 * took in the P2P network.
39 */
40 RECORD_ROUTE(2),
41 /**
42 * This is a 'FIND-PEER' request, so approximate results are fine.
43 */
44 FIND_PEER(4),
45 /**
46 * Possible message option for query key randomization.
47 */
48 BART(8);
49
50 private int val;
51
52 RouteOption(int val) {
53 this.val = val;
54 }
55}
diff --git a/src/main/java/org/gnunet/dht/package-info.java b/src/main/java/org/gnunet/dht/package-info.java
new file mode 100644
index 0000000..3d8c6ed
--- /dev/null
+++ b/src/main/java/org/gnunet/dht/package-info.java
@@ -0,0 +1,24 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21/**
22 * API for the gnunet dht service.
23 */
24package org.gnunet.dht;
diff --git a/src/main/java/org/gnunet/hello/HelloMessage.java b/src/main/java/org/gnunet/hello/HelloMessage.java
new file mode 100644
index 0000000..0d90912
--- /dev/null
+++ b/src/main/java/org/gnunet/hello/HelloMessage.java
@@ -0,0 +1,50 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.hello;
22
23import org.gnunet.construct.*;
24import org.gnunet.peerinfo.RsaPublicKeyBinaryEncoded;
25
26/**
27 * A HELLO message is used to exchange information about
28 * transports with other peers. This struct is always
29 * followed by the actual network addresses which have
30 * the format:
31 *
32 * 1) transport-name (0-terminated)
33 * 2) address-length (uint16_t, network byte order; possibly
34 * unaligned!)
35 * 3) address expiration (GNUNET_TIME_AbsoluteNBO); possibly
36 * unaligned!)
37 * 4) address (address-length bytes; possibly unaligned!)
38 *
39 * @author Florian Dold
40 */
41public class HelloMessage implements Message {
42 @UInt32
43 public int reserved;
44
45 @NestedMessage
46 public RsaPublicKeyBinaryEncoded publicKey;
47
48 @FillWith @UInt8
49 public byte[] addresses;
50}
diff --git a/src/main/java/org/gnunet/hello/package-info.java b/src/main/java/org/gnunet/hello/package-info.java
new file mode 100644
index 0000000..78a5193
--- /dev/null
+++ b/src/main/java/org/gnunet/hello/package-info.java
@@ -0,0 +1,25 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21
22/**
23 * Management of hello-messages from peers.
24 */
25package org.gnunet.hello;
diff --git a/src/main/java/org/gnunet/mesh/ClientConnectMessage.java b/src/main/java/org/gnunet/mesh/ClientConnectMessage.java
new file mode 100644
index 0000000..1a56ebb
--- /dev/null
+++ b/src/main/java/org/gnunet/mesh/ClientConnectMessage.java
@@ -0,0 +1,17 @@
1package org.gnunet.mesh;
2
3import org.gnunet.construct.*;
4import org.gnunet.util.GnunetMessage;
5
6/**
7 * Allows a client to register with the service.
8 *
9 * Direction: client -> service
10 *
11 * @author Florian Dold
12 */
13@UnionCase(272)
14public class ClientConnectMessage implements GnunetMessage.Body {
15 @IntegerFill(signed = false, bitSize = 32)
16 public int[] apps_list;
17}
diff --git a/src/main/java/org/gnunet/mesh/ConnectHandler.java b/src/main/java/org/gnunet/mesh/ConnectHandler.java
new file mode 100644
index 0000000..021e8f0
--- /dev/null
+++ b/src/main/java/org/gnunet/mesh/ConnectHandler.java
@@ -0,0 +1,13 @@
1package org.gnunet.mesh;
2
3import org.gnunet.peerinfo.PeerInfo;
4import org.gnunet.util.PeerIdentity;
5
6/**
7 * ...
8 *
9 * @author Florian Dold
10 */
11public interface ConnectHandler {
12 public void onConnect(Mesh.Tunnel tunnel, PeerIdentity peer);
13}
diff --git a/src/main/java/org/gnunet/mesh/DataMessage.java b/src/main/java/org/gnunet/mesh/DataMessage.java
new file mode 100644
index 0000000..92546c8
--- /dev/null
+++ b/src/main/java/org/gnunet/mesh/DataMessage.java
@@ -0,0 +1,19 @@
1package org.gnunet.mesh;
2
3import org.gnunet.construct.*;
4import org.gnunet.util.GnunetMessage;
5import org.gnunet.util.PeerIdentity;
6
7/**
8 * ...
9 *
10 * @author Florian Dold
11 */
12@UnionCase(260)
13public class DataMessage implements GnunetMessage.Body {
14 @UInt32
15 public int tid;
16 @FillWith
17 @UInt8
18 public byte[] payload;
19}
diff --git a/src/main/java/org/gnunet/mesh/DisconnectHandler.java b/src/main/java/org/gnunet/mesh/DisconnectHandler.java
new file mode 100644
index 0000000..8fd6428
--- /dev/null
+++ b/src/main/java/org/gnunet/mesh/DisconnectHandler.java
@@ -0,0 +1,12 @@
1package org.gnunet.mesh;
2
3import org.gnunet.util.PeerIdentity;
4
5/**
6 * ...
7 *
8 * @author Florian Dold
9 */
10public interface DisconnectHandler {
11 void onDisconnect(PeerIdentity peer);
12}
diff --git a/src/main/java/org/gnunet/mesh/InboundTunnelHandler.java b/src/main/java/org/gnunet/mesh/InboundTunnelHandler.java
new file mode 100644
index 0000000..ebcf225
--- /dev/null
+++ b/src/main/java/org/gnunet/mesh/InboundTunnelHandler.java
@@ -0,0 +1,12 @@
1package org.gnunet.mesh;
2
3import org.gnunet.util.PeerIdentity;
4
5/**
6 * ...
7 *
8 * @author Florian Dold
9 */
10public interface InboundTunnelHandler {
11 void onInboundTunnel(Mesh.Tunnel tunnel, PeerIdentity initiator);
12}
diff --git a/src/main/java/org/gnunet/mesh/LocalAckMessage.java b/src/main/java/org/gnunet/mesh/LocalAckMessage.java
new file mode 100644
index 0000000..6a09411
--- /dev/null
+++ b/src/main/java/org/gnunet/mesh/LocalAckMessage.java
@@ -0,0 +1,18 @@
1package org.gnunet.mesh;
2
3import org.gnunet.construct.UInt32;
4import org.gnunet.construct.UnionCase;
5import org.gnunet.util.GnunetMessage;
6
7/**
8 * ...
9 *
10 * @author Florian Dold
11 */
12@UnionCase(286)
13public class LocalAckMessage implements GnunetMessage.Body {
14 @UInt32
15 public int tid;
16 @UInt32
17 public int maxPid;
18}
diff --git a/src/main/java/org/gnunet/mesh/Mesh.java b/src/main/java/org/gnunet/mesh/Mesh.java
new file mode 100644
index 0000000..a6bf602
--- /dev/null
+++ b/src/main/java/org/gnunet/mesh/Mesh.java
@@ -0,0 +1,309 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.mesh;
22
23import org.gnunet.construct.Construct;
24import org.gnunet.mq.Envelope;
25import org.gnunet.mq.MessageQueue;
26import org.gnunet.mq.NotifySentHandler;
27import org.gnunet.util.*;
28import org.slf4j.Logger;
29import org.slf4j.LoggerFactory;
30
31import java.util.HashMap;
32import java.util.Map;
33
34/**
35 * Mesh API
36 *
37 * @author Florian Dold
38 */
39public class Mesh {
40 /**
41 * Class logger.
42 */
43 private static final Logger logger = LoggerFactory
44 .getLogger(Mesh.class);
45
46 /**
47 * For tunnels created by the client, the bit in this
48 * mask is always set.
49 */
50 private static final int TUNNEL_ID_CLI = 0x80000000;
51
52 /**
53 * For tunnels created by the server, the bit in this
54 * mask is always set.
55 */
56 private static final int TUNNEL_ID_SERV = 0xB0000000;
57
58 /**
59 * Disable buffering on intermediate nodes (for minimum latency).
60 * Yes/No.
61 */
62 private static final int OPTION_NOBUFFER = 1;
63
64 /**
65 * Enable tunnel reliability, lost messages will be retransmitted.
66 * Yes/No.
67 */
68 private static final int OPTION_RELIABLE = 2;
69
70 /**
71 * Client connected to the mesh service
72 */
73 private final Client client;
74
75 /**
76 * Called whenever a tunnel was destroyed.
77 */
78 private TunnelEndHandler tunnelEndHandler;
79
80 /**
81 * Message handler for messages received through
82 * a tunnel.
83 */
84 private MeshRunabout messageReceiver;
85
86 /**
87 * Ports that we listen on.
88 */
89 private int[] ports;
90
91 /**
92 * Handler for inbound tunnels.
93 */
94 private InboundTunnelHandler inboundTunnelHandler;
95
96 /**
97 * Mapping from the tunnel's ID to the tunnel object.
98 */
99 private Map<Integer,Tunnel> tunnelMap = new HashMap<Integer,Tunnel>();
100
101 /**
102 * Counter for generating fresh tunnel ID's
103 * when creating new tunnels.
104 */
105 private int next_tid = 1;
106
107 /**
108 * A tunnel to a remote peer.
109 * @param <T> type of context data for the tunnel
110 */
111 public class Tunnel<T> extends MessageQueue {
112 private T context;
113 private final int opt;
114 public final PeerIdentity peer;
115 public final int port;
116 protected int tunnelId;
117 private boolean receive_done_expected = false;
118 int ack_count = 1;
119
120 /**
121 * Canceler for the currently submitted envelope.
122 */
123 public Cancelable envelopeCanceler;
124
125 /**
126 * Create a new tunnel (we're initiator and will be allowed to add/remove peers
127 * and to broadcast).
128 *
129 * @param context tunnel context
130 * @param peer peer identity the tunnel should go to
131 * @param port Port number.
132 * @param nobuffer Flag for disabling buffering on relay nodes.
133 * @param reliable Flag for end-to-end reliability.
134 */
135 public Tunnel(PeerIdentity peer, int port, boolean nobuffer, boolean reliable, T context)
136 {
137 this(peer, 0, port, nobuffer, reliable);
138 TunnelCreateMessage tcm = new TunnelCreateMessage();
139 tcm.otherEnd = peer;
140 tcm.opt = opt;
141 tcm.port = port;
142 tcm.tunnel_id = tunnelId;
143 client.send(tcm);
144 }
145
146 /**
147 * Private tunnel constructor, for creating tunnel objects for
148 * incoming tunnels.
149 *
150 * @param peer
151 * @param tunnelId
152 * @param port
153 * @param nobuffer
154 * @param reliable
155 */
156 public Tunnel(PeerIdentity peer, int tunnelId, int port, boolean nobuffer, boolean reliable) {
157 int my_opt = 0;
158 if (reliable)
159 my_opt |= OPTION_RELIABLE;
160 if (nobuffer)
161 my_opt |= OPTION_NOBUFFER;
162 if (0 == tunnelId)
163 this.tunnelId = ((next_tid++) | TUNNEL_ID_CLI) & ~TUNNEL_ID_SERV;
164 else
165 this.tunnelId = tunnelId;
166 this.peer = peer;
167 this.port = port;
168 this.opt = my_opt;
169 }
170
171 public void receiveDone() {
172 if (!receive_done_expected)
173 throw new AssertionError("unexpected call to receiveDone");
174 LocalAckMessage am = new LocalAckMessage();
175 am.tid = tunnelId;
176 client.send(am);
177 receive_done_expected = false;
178 }
179
180 public void destroy() {
181 TunnelDestroyMessage m = new TunnelDestroyMessage();
182 m.tunnel_id = tunnelId;
183 client.send(m);
184 }
185
186 @Override
187 protected void submit(Envelope ev) {
188 if (ack_count <= 0)
189 throw new AssertionError();
190 DataMessage m = new DataMessage();
191 m.payload = Construct.toBinary(GnunetMessage.fromBody(ev.message));
192 Envelope mesh_ev = new Envelope(m);
193 mesh_ev.notifySent(new NotifySentHandler() {
194 @Override
195 public void onSent() {
196 envelopeCanceler = null;
197 }
198 });
199 client.send(mesh_ev);
200 envelopeCanceler = mesh_ev;
201 ack_count -= 1;
202 }
203
204 @Override
205 protected void retract() {
206 if (envelopeCanceler == null)
207 throw new AssertionError();
208 envelopeCanceler.cancel();
209 envelopeCanceler = null;
210 }
211
212 public T getContext() {
213 return context;
214 }
215
216 public void setContext(T newContext) {
217 context = newContext;
218 }
219
220 }
221
222 private class MeshMessageReceiver extends RunaboutMessageReceiver {
223 public void visit(TunnelCreateMessage m) {
224 Tunnel t = new Tunnel(m.otherEnd, m.tunnel_id, m.port,
225 (m.opt & OPTION_NOBUFFER) != 0, (m.opt & OPTION_NOBUFFER) != 0);
226 if (inboundTunnelHandler != null) {
227 inboundTunnelHandler.onInboundTunnel(t, m.otherEnd);
228 }
229 }
230
231 public void visit(DataMessage m) {
232 Tunnel t = tunnelMap.get(m.tid);
233 if (t != null)
234 {
235 if (t.receive_done_expected)
236 logger.warn("got unexpected message from service");
237 t.receive_done_expected = true;
238 messageReceiver.visitAppropriate(Construct.parseAs(m.payload, GnunetMessage.class).body);
239 }
240 }
241
242 public void visit(LocalAckMessage m) {
243 Tunnel t = tunnelMap.get(m.tid);
244 if (t != null)
245 t.ack_count += 1;
246 }
247
248 public void visit(TunnelDestroyMessage m) {
249 Tunnel t = tunnelMap.get(m.tunnel_id);
250 if (t == null) {
251 logger.warn("server got confused with tunnel IDs on destroy, ignoring message");
252 return;
253 }
254 t.destroy();
255 tunnelEndHandler.onTunnelEnd(t);
256 }
257
258 @Override
259 public void handleError() {
260 if (tunnelEndHandler != null) {
261 for (Tunnel t : tunnelMap.values()) {
262 tunnelEndHandler.onTunnelEnd(t);
263 }
264 }
265 tunnelMap.clear();
266 client.reconnect();
267 ClientConnectMessage ccm = new ClientConnectMessage();
268 ccm.apps_list = ports;
269 client.send(ccm);
270 }
271 }
272
273 /**
274 * Connect to the mesh service.
275 *
276 * @param cfg configuration to use
277 * @param inboundTunnelHandler function called when an *inbound* tunnel is created
278 * @param tunnelEndHandler function called when an *inbound* tunnel is destroyed by the
279 * remote peer, it is *not* called if Tunnel.destroy
280 * is called on the tunnel
281 */
282 public Mesh(Configuration cfg, InboundTunnelHandler inboundTunnelHandler,
283 TunnelEndHandler tunnelEndHandler, MeshRunabout messageReceiver, int... ports) {
284 this.tunnelEndHandler = tunnelEndHandler;
285 this.messageReceiver = messageReceiver;
286 this.ports = ports;
287 this.inboundTunnelHandler = inboundTunnelHandler;
288
289 client = new Client("mesh", cfg);
290 client.installReceiver(new MeshMessageReceiver());
291 ClientConnectMessage ccm = new ClientConnectMessage();
292 ccm.apps_list = ports;
293 client.send(ccm);
294 }
295
296 public <T> Tunnel<T> createTunnel(PeerIdentity peer, int port, boolean nobuffer, boolean reliable, T initialContext) {
297 return new Tunnel<T>(peer, port, nobuffer, reliable, initialContext);
298 }
299
300 /**
301 * Disconnect from the mesh service.
302 * All tunnels will be destroyed.
303 * All tunnel disconnect callbacks will be called on any still connected peers, notifying
304 * about their disconnection.
305 */
306 public void destroy() {
307 client.disconnect();
308 }
309}
diff --git a/src/main/java/org/gnunet/mesh/MeshRunabout.java b/src/main/java/org/gnunet/mesh/MeshRunabout.java
new file mode 100644
index 0000000..ea4248c
--- /dev/null
+++ b/src/main/java/org/gnunet/mesh/MeshRunabout.java
@@ -0,0 +1,19 @@
1package org.gnunet.mesh;
2
3import org.gnunet.util.PeerIdentity;
4import org.grothoff.Runabout;
5
6/**
7 * ...
8 *
9 * @author Florian Dold
10 */
11public class MeshRunabout extends Runabout {
12 private PeerIdentity sender;
13 /* package private */ void setSender(PeerIdentity sender) {
14 this.sender = sender;
15 }
16 public PeerIdentity getSender() {
17 return sender;
18 }
19}
diff --git a/src/main/java/org/gnunet/mesh/TunnelCreateMessage.java b/src/main/java/org/gnunet/mesh/TunnelCreateMessage.java
new file mode 100644
index 0000000..eaa4d6c
--- /dev/null
+++ b/src/main/java/org/gnunet/mesh/TunnelCreateMessage.java
@@ -0,0 +1,27 @@
1package org.gnunet.mesh;
2
3import org.gnunet.construct.NestedMessage;
4import org.gnunet.construct.UInt32;
5import org.gnunet.construct.UnionCase;
6import org.gnunet.util.GnunetMessage;
7import org.gnunet.util.PeerIdentity;
8
9/**
10 * FIXME
11 *
12 * @author Florian Dold
13 */
14@UnionCase(273)
15public class TunnelCreateMessage implements GnunetMessage.Body {
16 @UInt32
17 public int tunnel_id;
18
19 @NestedMessage(optional = false)
20 public PeerIdentity otherEnd;
21
22 @UInt32
23 public int port;
24
25 @UInt32
26 public int opt;
27}
diff --git a/src/main/java/org/gnunet/mesh/TunnelDestroyMessage.java b/src/main/java/org/gnunet/mesh/TunnelDestroyMessage.java
new file mode 100644
index 0000000..bce60bb
--- /dev/null
+++ b/src/main/java/org/gnunet/mesh/TunnelDestroyMessage.java
@@ -0,0 +1,16 @@
1package org.gnunet.mesh;
2
3import org.gnunet.construct.UInt32;
4import org.gnunet.construct.UnionCase;
5import org.gnunet.util.GnunetMessage;
6
7/**
8 * ...
9 *
10 * @author Florian Dold
11 */
12@UnionCase(274)
13public class TunnelDestroyMessage implements GnunetMessage.Body {
14 @UInt32
15 public int tunnel_id;
16}
diff --git a/src/main/java/org/gnunet/mesh/TunnelEndHandler.java b/src/main/java/org/gnunet/mesh/TunnelEndHandler.java
new file mode 100644
index 0000000..e56fdd4
--- /dev/null
+++ b/src/main/java/org/gnunet/mesh/TunnelEndHandler.java
@@ -0,0 +1,10 @@
1package org.gnunet.mesh;
2
3/**
4 * ...
5 *
6 * @author Florian Dold
7 */
8public interface TunnelEndHandler {
9 void onTunnelEnd(Mesh.Tunnel tunnel);
10}
diff --git a/src/main/java/org/gnunet/mesh/TunnelNotificationMessage.java b/src/main/java/org/gnunet/mesh/TunnelNotificationMessage.java
new file mode 100644
index 0000000..8846088
--- /dev/null
+++ b/src/main/java/org/gnunet/mesh/TunnelNotificationMessage.java
@@ -0,0 +1,22 @@
1package org.gnunet.mesh;
2
3import org.gnunet.construct.NestedMessage;
4import org.gnunet.construct.UInt32;
5import org.gnunet.util.GnunetMessage;
6import org.gnunet.util.PeerIdentity;
7
8/**
9 * ...
10 *
11 * @author Florian Dold
12 */
13public class TunnelNotificationMessage implements GnunetMessage.Body {
14 @UInt32
15 public int tunnel_id;
16 /**
17 * Peer at the other end, if any
18 * TODO: ask bart what 'if any' means here
19 */
20 @NestedMessage
21 public PeerIdentity peer;
22}
diff --git a/src/main/java/org/gnunet/mesh/package-info.java b/src/main/java/org/gnunet/mesh/package-info.java
new file mode 100644
index 0000000..fb5a8a4
--- /dev/null
+++ b/src/main/java/org/gnunet/mesh/package-info.java
@@ -0,0 +1,25 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21
22/**
23 * Create tunnels for packet-based communication to distant peers.
24 */
25package org.gnunet.mesh;
diff --git a/src/main/java/org/gnunet/mq/Envelope.java b/src/main/java/org/gnunet/mq/Envelope.java
new file mode 100644
index 0000000..fa19225
--- /dev/null
+++ b/src/main/java/org/gnunet/mq/Envelope.java
@@ -0,0 +1,36 @@
1package org.gnunet.mq;
2
3import org.gnunet.util.Cancelable;
4import org.gnunet.util.GnunetMessage;
5
6/**
7 * Container for a message to be sent by a message queue.
8 */
9public class Envelope implements Cancelable {
10 public final GnunetMessage.Body message;
11 private MessageQueue parent_queue;
12 private NotifySentHandler notify_sent_handler;
13
14 public Envelope(GnunetMessage.Body message) {
15 this.message = message;
16 }
17
18 public void notifySent(NotifySentHandler h) {
19 this.notify_sent_handler = h;
20 }
21
22 public void injectSent() {
23 if (notify_sent_handler != null)
24 notify_sent_handler.onSent();
25 }
26
27 public void cancel() {
28 if (parent_queue == null)
29 throw new AssertionError("can not cancel an unqueued message");
30 }
31
32 /* pkg-private */ void invokeSentNotification() {
33 if (null != notify_sent_handler)
34 notify_sent_handler.onSent();
35 }
36}
diff --git a/src/main/java/org/gnunet/mq/MessageQueue.java b/src/main/java/org/gnunet/mq/MessageQueue.java
new file mode 100644
index 0000000..4df3ae4
--- /dev/null
+++ b/src/main/java/org/gnunet/mq/MessageQueue.java
@@ -0,0 +1,84 @@
1package org.gnunet.mq;
2
3
4import org.gnunet.util.GnunetMessage;
5
6import java.util.LinkedList;
7
8/**
9 * General-purpose message queue
10 */
11public abstract class MessageQueue {
12 private LinkedList<Envelope> queued_envelopes = new LinkedList<Envelope>();
13 private LinkedList<Envelope> prefered_queued_envelopes = new LinkedList<Envelope>();
14 protected Envelope current_envelope;
15
16 protected abstract void submit(Envelope ev);
17
18 protected abstract void retract();
19
20 public void send(GnunetMessage.Body body) {
21 send(new Envelope(body));
22 }
23
24 public void sendPrefered(GnunetMessage.Body body) {
25 sendPrefered(new Envelope(body));
26 }
27
28 private Envelope pollNextEnvelope() {
29 if (!prefered_queued_envelopes.isEmpty())
30 return prefered_queued_envelopes.removeFirst();
31 if (!queued_envelopes.isEmpty())
32 return queued_envelopes.removeFirst();
33 return null;
34 }
35
36 public void send(Envelope ev) {
37 if (null == current_envelope) {
38 current_envelope = ev;
39 submit(current_envelope);
40 } else {
41 queued_envelopes.addLast(ev);
42 }
43 }
44
45 public void sendPrefered(Envelope ev) {
46 if (null == current_envelope) {
47 current_envelope = ev;
48 submit(current_envelope);
49 } else {
50 prefered_queued_envelopes.addLast(ev);
51 }
52 }
53
54 protected void reportMessageSent() {
55 if (null == current_envelope)
56 throw new AssertionError();
57 current_envelope.invokeSentNotification();
58 next();
59 }
60
61 private void next() {
62 current_envelope = pollNextEnvelope();
63 if (current_envelope == null)
64 return;
65 submit(current_envelope);
66 }
67
68 /**
69 * Cancel sending an envelope. The envelope must be queued in this message queue.
70 *
71 * @param ev the envelope to cancel
72 */
73 /* pkg-private */ void cancelEnvelope(Envelope ev) {
74 if (null == current_envelope)
75 throw new AssertionError();
76 if (ev == current_envelope) {
77 retract();
78 next();
79 } else {
80 queued_envelopes.remove(ev);
81 prefered_queued_envelopes.remove(ev);
82 }
83 }
84}
diff --git a/src/main/java/org/gnunet/mq/NotifySentHandler.java b/src/main/java/org/gnunet/mq/NotifySentHandler.java
new file mode 100644
index 0000000..7ec13b2
--- /dev/null
+++ b/src/main/java/org/gnunet/mq/NotifySentHandler.java
@@ -0,0 +1,6 @@
1package org.gnunet.mq;
2
3
4public interface NotifySentHandler {
5 void onSent();
6}
diff --git a/src/main/java/org/gnunet/nse/NetworkSizeEstimation.java b/src/main/java/org/gnunet/nse/NetworkSizeEstimation.java
new file mode 100644
index 0000000..5fc7453
--- /dev/null
+++ b/src/main/java/org/gnunet/nse/NetworkSizeEstimation.java
@@ -0,0 +1,167 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.nse;
22
23
24import org.gnunet.util.*;
25import org.gnunet.util.getopt.Argument;
26import org.gnunet.util.getopt.ArgumentAction;
27import org.slf4j.Logger;
28import org.slf4j.LoggerFactory;
29
30import java.util.Collection;
31import java.util.HashSet;
32
33
34/**
35 * An API for the network size estimation service.
36 *
37 * @author Florian Dold
38 */
39public class NetworkSizeEstimation {
40 private static final Logger logger = LoggerFactory
41 .getLogger(NetworkSizeEstimation.class);
42
43 private Collection<Subscriber> subscribers = new HashSet<Subscriber>(1);
44 private boolean disconnected = false;
45
46 private Client client;
47
48 private class NSE_Receiver extends RunaboutMessageReceiver {
49 public void visit(UpdateMessage uMsg) {
50 for (Subscriber s : subscribers) {
51 s.update(AbsoluteTime.fromNetwork(uMsg.timestamp), uMsg.sizeEstimate, uMsg.stdDeviation);
52 }
53
54 if (!disconnected) {
55 client.receiveOne(RelativeTime.FOREVER, this);
56 }
57 }
58
59 @Override
60 public void handleError() {
61 logger.warn("NSE connection lost - trying to reconnect");
62 client.reconnect();
63 requestUpdate();
64 }
65 }
66
67 private class NSE_Transmitter implements MessageTransmitter {
68 @Override
69 public void transmit(Connection.MessageSink sink) {
70 StartMessage m = new StartMessage();
71 sink.send(m);
72 client.receiveOne(RelativeTime.FOREVER, new NSE_Receiver());
73 }
74
75 @Override
76 public void handleError() {
77 logger.warn("NSE connection lost - trying to reconnect");
78 client.reconnect();
79 requestUpdate();
80
81 }
82 }
83
84
85 /**
86 * A handle for a subscription to the network size estimation service, may be used to cancel the
87 * subscription.
88 */
89 public class Subscription implements Cancelable {
90 private Subscriber sub;
91
92 private Subscription(Subscriber sub) {
93 this.sub = sub;
94 }
95
96 /**
97 * Cancel the subscription.
98 */
99 public void cancel() {
100 subscribers.remove(sub);
101 }
102 }
103
104 /**
105 * A NSE_Subscriber receives updates from the service.
106 */
107 public interface Subscriber {
108 public void update(AbsoluteTime timestamp, double estimate, double deviation);
109 }
110
111 /**
112 * Subscribe for updates from the service.
113 *
114 * @param s callback for updates
115 * @return a subscription handle that may be used to cancel the subscription
116 */
117 public Cancelable subscribe(Subscriber s) {
118 subscribers.add(s);
119 requestUpdate();
120 return new Subscription(s);
121 }
122
123 /**
124 * Create a connection to the network size estimation service.
125 *
126 * @param cfg the configuration to use for connecting with the service
127 */
128 public NetworkSizeEstimation(Configuration cfg) {
129 client = new Client("nse", cfg);
130 }
131
132 private void requestUpdate() {
133 client.notifyTransmitReady(RelativeTime.FOREVER, true, 0, new NSE_Transmitter());
134 }
135
136 /**
137 * Cancel all subscriptions and disconnect from the service.
138 */
139 public void disconnect() {
140 disconnected = true;
141 }
142
143 public static void main(String[] args) {
144 new Program(args) {
145 @Argument(action = ArgumentAction.SET,
146 shortname = "w",
147 longname = "watch",
148 description = "wait and watch for more NSE updates")
149 boolean cont = false;
150
151 public void run() {
152 final NetworkSizeEstimation svc = new NetworkSizeEstimation(cfg);
153
154 Subscriber subscriber = new Subscriber() {
155 @Override
156 public void update(AbsoluteTime timestamp, double estimate, double deviation) {
157 System.out.println("est: " + estimate + " dev: " + deviation + " t: " + timestamp.toDate());
158 if (!cont) {
159 svc.disconnect();
160 }
161 }
162 };
163 svc.subscribe(subscriber);
164 }
165 }.start();
166 }
167}
diff --git a/src/main/java/org/gnunet/nse/StartMessage.java b/src/main/java/org/gnunet/nse/StartMessage.java
new file mode 100644
index 0000000..9f5b79e
--- /dev/null
+++ b/src/main/java/org/gnunet/nse/StartMessage.java
@@ -0,0 +1,12 @@
1package org.gnunet.nse;
2
3import org.gnunet.construct.UnionCase;
4import org.gnunet.util.GnunetMessage;
5
6/**
7* ...
8*
9* @author Florian Dold
10*/
11@UnionCase(321)
12public class StartMessage implements GnunetMessage.Body {}
diff --git a/src/main/java/org/gnunet/nse/UpdateMessage.java b/src/main/java/org/gnunet/nse/UpdateMessage.java
new file mode 100644
index 0000000..5c64a54
--- /dev/null
+++ b/src/main/java/org/gnunet/nse/UpdateMessage.java
@@ -0,0 +1,26 @@
1package org.gnunet.nse;
2
3import org.gnunet.construct.*;
4import org.gnunet.construct.DoubleValue;
5import org.gnunet.util.AbsoluteTimeMessage;
6import org.gnunet.util.GnunetMessage;
7
8/**
9* ...
10*
11* @author Florian Dold
12*/
13@UnionCase(323)
14public class UpdateMessage implements GnunetMessage.Body {
15 @UInt32
16 public int reserved;
17
18 @NestedMessage
19 public AbsoluteTimeMessage timestamp;
20
21 @DoubleValue
22 public double sizeEstimate;
23
24 @DoubleValue
25 public double stdDeviation;
26}
diff --git a/src/main/java/org/gnunet/nse/package-info.java b/src/main/java/org/gnunet/nse/package-info.java
new file mode 100644
index 0000000..3bbc064
--- /dev/null
+++ b/src/main/java/org/gnunet/nse/package-info.java
@@ -0,0 +1,24 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21/**
22 * API for the gnunet nse service.
23 */
24package org.gnunet.nse;
diff --git a/src/main/java/org/gnunet/peerinfo/InfoEnd.java b/src/main/java/org/gnunet/peerinfo/InfoEnd.java
new file mode 100644
index 0000000..b59e194
--- /dev/null
+++ b/src/main/java/org/gnunet/peerinfo/InfoEnd.java
@@ -0,0 +1,14 @@
1package org.gnunet.peerinfo;
2
3import org.gnunet.construct.UnionCase;
4import org.gnunet.util.GnunetMessage;
5
6/**
7 * ...
8 *
9 * @author Florian Dold
10 */
11@UnionCase(333)
12public class InfoEnd implements GnunetMessage.Body {
13
14}
diff --git a/src/main/java/org/gnunet/peerinfo/InfoMessage.java b/src/main/java/org/gnunet/peerinfo/InfoMessage.java
new file mode 100644
index 0000000..d8d7daa
--- /dev/null
+++ b/src/main/java/org/gnunet/peerinfo/InfoMessage.java
@@ -0,0 +1,50 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.peerinfo;
22
23import org.gnunet.construct.NestedMessage;
24import org.gnunet.construct.UInt32;
25import org.gnunet.construct.UnionCase;
26import org.gnunet.hello.HelloMessage;
27import org.gnunet.util.GnunetMessage;
28import org.gnunet.util.PeerIdentity;
29
30/**
31 * @author Florian Dold
32 */
33@UnionCase(332)
34public class InfoMessage implements GnunetMessage.Body {
35 /**
36 * Always zero.
37 */
38 @UInt32
39 public int reserved;
40 /**
41 * About which peer are we talking here?
42 */
43 @NestedMessage
44 public PeerIdentity peerIdentity;
45 /**
46 * HELLO of the peer, null if no HELLO present.
47 */
48 @NestedMessage(optional = true)
49 public HelloMessage hello;
50}
diff --git a/src/main/java/org/gnunet/peerinfo/ListAllPeersMessage.java b/src/main/java/org/gnunet/peerinfo/ListAllPeersMessage.java
new file mode 100644
index 0000000..e384c93
--- /dev/null
+++ b/src/main/java/org/gnunet/peerinfo/ListAllPeersMessage.java
@@ -0,0 +1,39 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.peerinfo;
22
23import org.gnunet.construct.NestedMessage;
24import org.gnunet.construct.UInt32;
25import org.gnunet.construct.UnionCase;
26import org.gnunet.util.GnunetMessage;
27import org.gnunet.util.PeerIdentity;
28
29/**
30 * Message requesting a listing of all known peers,
31 * possibly restricted to the specified peer identity.
32 *
33 * @author Florian Dold
34 */
35@UnionCase(331)
36public class ListAllPeersMessage implements GnunetMessage.Body {
37 @UInt32
38 public int include_friend_only;
39}
diff --git a/src/main/java/org/gnunet/peerinfo/ListPeerMessage.java b/src/main/java/org/gnunet/peerinfo/ListPeerMessage.java
new file mode 100644
index 0000000..56e6b5d
--- /dev/null
+++ b/src/main/java/org/gnunet/peerinfo/ListPeerMessage.java
@@ -0,0 +1,39 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.peerinfo;
22
23import org.gnunet.construct.NestedMessage;
24import org.gnunet.construct.UInt32;
25import org.gnunet.util.GnunetMessage;
26import org.gnunet.util.PeerIdentity;
27
28/**
29 * Message requesting a listing of all known peers,
30 * possibly restricted to the specified peer identity.
31 *
32 * @author Florian Dold
33 */
34public class ListPeerMessage implements GnunetMessage.Body {
35 @UInt32
36 public int reserved;
37 @NestedMessage
38 public PeerIdentity peer;
39}
diff --git a/src/main/java/org/gnunet/peerinfo/PeerInfo.java b/src/main/java/org/gnunet/peerinfo/PeerInfo.java
new file mode 100644
index 0000000..46d35bf
--- /dev/null
+++ b/src/main/java/org/gnunet/peerinfo/PeerInfo.java
@@ -0,0 +1,159 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.peerinfo;
22
23import org.gnunet.hello.HelloMessage;
24import org.gnunet.mq.Envelope;
25import org.gnunet.mq.MessageQueue;
26import org.gnunet.requests.RequestContainer;
27import org.gnunet.requests.SequentialRequestContainer;
28import org.gnunet.util.*;
29import org.slf4j.Logger;
30import org.slf4j.LoggerFactory;
31
32/**
33 * Interface to the service that maintains all known hosts.
34 *
35 * @author Florian Dold
36 */
37public class PeerInfo {
38 private static final Logger logger = LoggerFactory
39 .getLogger(AbsoluteTime.class);
40
41 /**
42 * Client that connects to the peerinfo service.
43 */
44 private final Client client;
45
46 /**
47 * All currently active iterate requests.
48 */
49 private SequentialRequestContainer<PeerIterateRequest> iterate_requests;
50
51 private class PeerIterateRequest extends RequestContainer.Request {
52 public PeerIdentity peer;
53 public PeerProcessor peerProcessor;
54 public boolean friend_only;
55 public boolean canceled;
56
57 public PeerIterateRequest(PeerIdentity peer, boolean friend_only, PeerProcessor peerProcessor) {
58 this.peer = peer;
59 this.peerProcessor = peerProcessor;
60 this.friend_only = friend_only;
61 }
62
63 @Override
64 public Envelope assembleRequest() {
65 if (peer == null) {
66 ListAllPeersMessage m = new ListAllPeersMessage();
67 m.include_friend_only = friend_only ? 1 : 0;
68 return new Envelope(m);
69 } else {
70 ListPeerMessage m = new ListPeerMessage();
71 m.peer = peer;
72 return new Envelope(m);
73 }
74 }
75
76 public void cancel() {
77 canceled = true;
78 }
79 }
80
81 private class PeerInfoMessageReceiver extends RunaboutMessageReceiver {
82 public void visit(InfoEnd infoEnd) {
83 PeerIterateRequest r = iterate_requests.getRequest();
84 if (!r.canceled)
85 r.peerProcessor.onEnd();
86 iterate_requests.next();
87 }
88 public void visit(InfoMessage infoMessage) {
89 PeerIterateRequest r = iterate_requests.getRequest();
90 if (!r.canceled)
91 r.peerProcessor.onPeer(infoMessage.peerIdentity, infoMessage.hello);
92 }
93
94 @Override
95 public void handleError() {
96 client.reconnect();
97 iterate_requests.restart();
98 }
99 }
100
101
102 public PeerInfo(Configuration cfg) {
103 client = new Client("peerinfo", cfg);
104 client.installReceiver(new PeerInfoMessageReceiver());
105 iterate_requests = new SequentialRequestContainer<PeerIterateRequest>(client);
106 // Make sure that new requests are only sent once the old request has finished.
107 // Otherwise, the peerinfo service would send the answers interleaved.
108 iterate_requests.setOverlap(false);
109 }
110
111 /**
112 * Iterates over the HELLOs of all peers.
113 *
114 * @param timeout
115 * @param processor
116 * @return
117 */
118 public Cancelable iterate(RelativeTime timeout, boolean friend_only, PeerProcessor processor) {
119 return iterate(timeout, null, friend_only, processor);
120 }
121
122 /**
123 * Iterates over the HELLOs of the given peer.
124 * Can be called with peer=null to iterate over all peers.
125 * @param timeout
126 * @param peer
127 * @param processor
128 * @return
129 */
130 public Cancelable iterate(RelativeTime timeout, PeerIdentity peer, boolean friend_only, PeerProcessor processor) {
131 PeerIterateRequest r = new PeerIterateRequest(peer, friend_only, processor);
132 return iterate_requests.addRequest(r);
133 }
134
135 public void disconnect() {
136 client.disconnect();
137 }
138
139 public static void main(String... args) {
140 new Program(args) {
141 @Override
142 public void run() {
143 final PeerInfo peerInfo = new PeerInfo(getConfiguration());
144 peerInfo.iterate(RelativeTime.FOREVER, false, new PeerProcessor() {
145 @Override
146 public void onPeer(PeerIdentity peerIdentity, HelloMessage hello) {
147 System.out.println("peer " + peerIdentity.toString());
148 }
149
150 @Override
151 public void onEnd() {
152 System.out.println("got peer end");
153 peerInfo.disconnect();
154 }
155 });
156 }
157 }.start();
158 }
159}
diff --git a/src/main/java/org/gnunet/peerinfo/PeerProcessor.java b/src/main/java/org/gnunet/peerinfo/PeerProcessor.java
new file mode 100644
index 0000000..b096c37
--- /dev/null
+++ b/src/main/java/org/gnunet/peerinfo/PeerProcessor.java
@@ -0,0 +1,12 @@
1package org.gnunet.peerinfo;
2
3import org.gnunet.hello.HelloMessage;
4import org.gnunet.util.PeerIdentity;
5
6/**
7 * Callback class to receive known peers and their HELLOs.
8 */
9public interface PeerProcessor {
10 public void onPeer(PeerIdentity peerIdentity, HelloMessage hello);
11 public void onEnd();
12}
diff --git a/src/main/java/org/gnunet/peerinfo/RsaPublicKeyBinaryEncoded.java b/src/main/java/org/gnunet/peerinfo/RsaPublicKeyBinaryEncoded.java
new file mode 100644
index 0000000..86985f3
--- /dev/null
+++ b/src/main/java/org/gnunet/peerinfo/RsaPublicKeyBinaryEncoded.java
@@ -0,0 +1,56 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.peerinfo;
22
23import org.gnunet.construct.FixedSizeIntegerArray;
24import org.gnunet.construct.Message;
25import org.gnunet.construct.UInt16;
26import org.gnunet.construct.UInt8;
27
28/**
29 * @author Florian Dold
30 */
31public class RsaPublicKeyBinaryEncoded implements Message {
32 public static final int GNUNET_CRYPTO_RSA_KEY_LENGTH = 258;
33
34 /**
35 * In big-endian, must be GNUNET_CRYPTO_RSA_KEY_LENGTH+4
36 */
37 @UInt16
38 public int len;
39 /**
40 * Size of n in key; in big-endian!
41 */
42 @UInt16
43 public int sizen;
44
45 /**
46 * The key itself, contains n followed by e.
47 */
48 @FixedSizeIntegerArray(length = RsaPublicKeyBinaryEncoded.GNUNET_CRYPTO_RSA_KEY_LENGTH, signed = false, bitSize = 8)
49 public byte[] key;
50
51 /**
52 * Padding.
53 */
54 @UInt8
55 public byte reserved;
56}
diff --git a/src/main/java/org/gnunet/peerinfo/package-info.java b/src/main/java/org/gnunet/peerinfo/package-info.java
new file mode 100644
index 0000000..19cebdb
--- /dev/null
+++ b/src/main/java/org/gnunet/peerinfo/package-info.java
@@ -0,0 +1,25 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21
22/**
23 * Access to information about known hosts.
24 */
25package org.gnunet.peerinfo;
diff --git a/src/main/java/org/gnunet/requests/MatchingRequestContainer.java b/src/main/java/org/gnunet/requests/MatchingRequestContainer.java
new file mode 100644
index 0000000..656d162
--- /dev/null
+++ b/src/main/java/org/gnunet/requests/MatchingRequestContainer.java
@@ -0,0 +1,51 @@
1package org.gnunet.requests;
2
3import com.google.common.collect.Maps;
4import org.gnunet.mq.MessageQueue;
5import org.gnunet.util.Cancelable;
6
7import java.util.Map;
8
9
10public class MatchingRequestContainer<K, T extends RequestContainer.Request> extends RequestContainer {
11 private Map<K,T> requests = Maps.newHashMap();
12 private final MessageQueue mq;
13
14 public MatchingRequestContainer(MessageQueue mq) {
15 this.mq = mq;
16 }
17
18 public Cancelable addRequest(K key, final T request) {
19 if (requests.containsKey(key))
20 throw new AssertionError("key already present in request container");
21 requests.put(key, request);
22 mq.send(request.assembleRequest());
23 return new Cancelable() {
24 @Override
25 public void cancel() {
26 setRequestCancelled(request);
27 if (isRequestTransmitting(request)) {
28 cancelRequestTransmission(request);
29 } else {
30 request.cancel();
31 }
32 }
33 };
34 }
35
36 @Override
37 public void restart() {
38 Map<K,T> requestsOld = requests;
39 requests = Maps.newHashMap();
40 for (Map.Entry<K,T> e : requestsOld.entrySet()) {
41 if (!isRequestCancelled(e.getValue())) {
42 setRequestTransmitting(e.getValue(), false);
43 addRequest(e.getKey(), e.getValue());
44 }
45 }
46 }
47
48 public T getRequest(K key) {
49 return requests.get(key);
50 }
51}
diff --git a/src/main/java/org/gnunet/requests/RequestContainer.java b/src/main/java/org/gnunet/requests/RequestContainer.java
new file mode 100644
index 0000000..c7627f0
--- /dev/null
+++ b/src/main/java/org/gnunet/requests/RequestContainer.java
@@ -0,0 +1,59 @@
1package org.gnunet.requests;
2
3
4import org.gnunet.mq.Envelope;
5import org.gnunet.util.Cancelable;
6
7public abstract class RequestContainer {
8 protected boolean overlap = true;
9
10 public abstract static class Request {
11 private boolean transmitting;
12 private boolean canceled;
13 private Cancelable cancelRequest;
14 public abstract Envelope assembleRequest();
15 public void cancel() {
16 throw new AssertionError("request of type " + this.getClass() + " can not be canceled (not implemented)");
17 }
18 }
19
20 /**
21 * Re-send all requests in the queue that have not been canceled.
22 */
23 public abstract void restart();
24
25 /**
26 * Allow or disallow requests to be send while other requests in the queue have not been completed.
27 *
28 * @param overlap true to allow overlapped requests, false to disallow them
29 */
30 public void setOverlap(boolean overlap) {
31 this.overlap = overlap;
32 }
33
34 protected boolean isRequestTransmitting(Request r) {
35 return r.transmitting;
36 }
37
38 protected void setRequestTransmitting(Request r, boolean transmitting) {
39 r.transmitting = transmitting;
40 }
41
42 protected void setRequestTransmissionCancel(Request request, Cancelable cancel) {
43 request.cancelRequest = cancel;
44 }
45
46 protected void cancelRequestTransmission(Request r) {
47 r.cancelRequest.cancel();
48 r.cancelRequest = null;
49 }
50
51 protected void setRequestCancelled(Request r) {
52 r.canceled = true;
53 }
54
55 protected boolean isRequestCancelled(Request r) {
56 return r.canceled;
57 }
58
59}
diff --git a/src/main/java/org/gnunet/requests/SequentialRequestContainer.java b/src/main/java/org/gnunet/requests/SequentialRequestContainer.java
new file mode 100644
index 0000000..037055f
--- /dev/null
+++ b/src/main/java/org/gnunet/requests/SequentialRequestContainer.java
@@ -0,0 +1,86 @@
1package org.gnunet.requests;
2
3import org.gnunet.mq.Envelope;
4import org.gnunet.mq.MessageQueue;
5import org.gnunet.mq.NotifySentHandler;
6import org.gnunet.util.Cancelable;
7
8import java.util.LinkedList;
9
10/**
11 * Container for requests that are responded to in sequential order.
12 */
13public class SequentialRequestContainer<T extends RequestContainer.Request> extends RequestContainer {
14 private LinkedList<T> requests = new LinkedList<T>();
15 private MessageQueue mq;
16
17 int requestsActive = 0;
18
19 public SequentialRequestContainer(MessageQueue mq) {
20 this.mq = mq;
21 }
22
23 public T getRequest() {
24 return requests.getFirst();
25 }
26
27 public void next() {
28 if (requestsActive == 0 || requests.isEmpty())
29 throw new AssertionError();
30 requestsActive--;
31 requests.removeFirst();
32 if (requestsActive == 0 && !requests.isEmpty()) {
33 Request r = requests.getFirst();
34 setRequestTransmitting(r, true);
35 Envelope ev = r.assembleRequest();
36 setRequestTransmissionCancel(r, ev);
37 mq.send(r.assembleRequest());
38 requestsActive++;
39 }
40 }
41
42 public Cancelable addRequest(final T request) {
43 requests.addLast(request);
44 if (overlap || requestsActive == 0) {
45 requestsActive++;
46 setRequestTransmitting(request, true);
47 Envelope ev = request.assembleRequest();
48 ev.notifySent(new NotifySentHandler() {
49 @Override
50 public void onSent() {
51 setRequestTransmitting(request, false);
52 }
53 });
54 setRequestTransmissionCancel(request, ev);
55 mq.send(request.assembleRequest());
56 }
57 return new Cancelable() {
58 @Override
59 public void cancel() {
60 setRequestCancelled(request);
61 if (isRequestTransmitting(request)) {
62 cancelRequestTransmission(request);
63 } else {
64 request.cancel();
65 }
66 }
67 };
68 }
69
70 @Override
71 public void restart() {
72 LinkedList<T> requestsOld = requests;
73 requests = new LinkedList<T>();
74 for (T r : requestsOld) {
75 if (!isRequestCancelled(r)) {
76 setRequestTransmitting(r, false);
77 addRequest(r);
78 }
79 }
80 }
81
82
83 public Iterable<T> iter() {
84 return requests;
85 }
86}
diff --git a/src/main/java/org/gnunet/requests/package-info.java b/src/main/java/org/gnunet/requests/package-info.java
new file mode 100644
index 0000000..892a606
--- /dev/null
+++ b/src/main/java/org/gnunet/requests/package-info.java
@@ -0,0 +1,25 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21
22/**
23 * General mechanism for queueing requests to a service.
24 */
25package org.gnunet.requests;
diff --git a/src/main/java/org/gnunet/statistics/GetMessage.java b/src/main/java/org/gnunet/statistics/GetMessage.java
new file mode 100644
index 0000000..eb44fbd
--- /dev/null
+++ b/src/main/java/org/gnunet/statistics/GetMessage.java
@@ -0,0 +1,43 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.statistics;
22
23import org.gnunet.construct.UnionCase;
24import org.gnunet.construct.ZeroTerminatedString;
25import org.gnunet.util.GnunetMessage;
26
27/**
28 * Client --> Service
29 *
30 */
31@UnionCase(169)
32public class GetMessage implements GnunetMessage.Body {
33 /**
34 * Subsystem of interest, empty string for all subsystems.
35 */
36 @ZeroTerminatedString
37 public String subsystemName;
38 /**
39 * Statistics value name of interest, empty string for all values.
40 */
41 @ZeroTerminatedString
42 public String statisticsName;
43}
diff --git a/src/main/java/org/gnunet/statistics/GetRequest.java b/src/main/java/org/gnunet/statistics/GetRequest.java
new file mode 100644
index 0000000..8f421b1
--- /dev/null
+++ b/src/main/java/org/gnunet/statistics/GetRequest.java
@@ -0,0 +1,31 @@
1package org.gnunet.statistics;
2
3import org.gnunet.mq.Envelope;
4import org.gnunet.mq.MessageQueue;
5import org.gnunet.requests.RequestContainer;
6
7/**
8 */
9public class GetRequest extends RequestContainer.Request {
10 private final String subsystem;
11 private final String name;
12 public final StatisticsReceiver receiver;
13 private RequestContainer parent;
14
15 public GetRequest(String subsystem, String name, StatisticsReceiver receiver) {
16 this.subsystem = subsystem;
17 this.name = name;
18 this.receiver = receiver;
19 }
20
21 @Override
22 public Envelope assembleRequest() {
23 GetMessage m = new GetMessage();
24 m.subsystemName = subsystem;
25 m.statisticsName = name;
26 return new Envelope(m);
27 }
28
29 public void cancel() {
30 }
31}
diff --git a/src/main/java/org/gnunet/statistics/GetResponseEndMessage.java b/src/main/java/org/gnunet/statistics/GetResponseEndMessage.java
new file mode 100644
index 0000000..eebe16e
--- /dev/null
+++ b/src/main/java/org/gnunet/statistics/GetResponseEndMessage.java
@@ -0,0 +1,32 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.statistics;
22
23import org.gnunet.construct.UnionCase;
24import org.gnunet.util.GnunetMessage;
25
26/**
27 * ...
28*/
29@UnionCase(171)
30public class GetResponseEndMessage implements GnunetMessage.Body {
31 // empty
32}
diff --git a/src/main/java/org/gnunet/statistics/GetResponseMessage.java b/src/main/java/org/gnunet/statistics/GetResponseMessage.java
new file mode 100644
index 0000000..2a722e6
--- /dev/null
+++ b/src/main/java/org/gnunet/statistics/GetResponseMessage.java
@@ -0,0 +1,49 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.statistics;
22
23import org.gnunet.construct.UInt32;
24import org.gnunet.construct.UInt64;
25import org.gnunet.construct.UnionCase;
26import org.gnunet.construct.ZeroTerminatedString;
27import org.gnunet.util.GnunetMessage;
28
29/**
30 * service --> client
31 *
32 *
33 */
34@UnionCase(170)
35public class GetResponseMessage implements GnunetMessage.Body {
36 /**
37 * Unique numerical identifier for the value (will
38 * not change during the same client-session). Highest
39 * bit will be set for persistent values.
40 */
41 @UInt32
42 public long uid;
43 @UInt64
44 public long value;
45 @ZeroTerminatedString
46 public String subsystemName;
47 @ZeroTerminatedString
48 public String statisticName;
49}
diff --git a/src/main/java/org/gnunet/statistics/SetMessage.java b/src/main/java/org/gnunet/statistics/SetMessage.java
new file mode 100644
index 0000000..d75e3bf
--- /dev/null
+++ b/src/main/java/org/gnunet/statistics/SetMessage.java
@@ -0,0 +1,43 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.statistics;
22
23import org.gnunet.construct.UInt32;
24import org.gnunet.construct.UInt64;
25import org.gnunet.construct.UnionCase;
26import org.gnunet.construct.ZeroTerminatedString;
27import org.gnunet.util.GnunetMessage;
28
29
30/**
31 * Sent to the service by the client to set a statistics value.
32 */
33@UnionCase(168)
34public class SetMessage implements GnunetMessage.Body {
35 @UInt32
36 public int flags;
37 @UInt64
38 public long value;
39 @ZeroTerminatedString
40 public String subsystemName;
41 @ZeroTerminatedString
42 public String statisticName;
43}
diff --git a/src/main/java/org/gnunet/statistics/SetRequest.java b/src/main/java/org/gnunet/statistics/SetRequest.java
new file mode 100644
index 0000000..4aa4a82
--- /dev/null
+++ b/src/main/java/org/gnunet/statistics/SetRequest.java
@@ -0,0 +1,48 @@
1package org.gnunet.statistics;
2
3import org.gnunet.mq.Envelope;
4import org.gnunet.mq.MessageQueue;
5import org.gnunet.requests.RequestContainer;
6import org.gnunet.util.RelativeTime;
7
8
9public class SetRequest extends RequestContainer.Request {
10 /**
11 * Time after we give up on setting values in statistics
12 */
13 private static final RelativeTime SET_TIMEOUT = RelativeTime.SECOND.multiply(10);
14
15 private final static int SETFLAG_RELATIVE = 1;
16 private final static int SETFLAG_PERSIST = 2;
17 private final String subsystem;
18 private final String name;
19 private final boolean persist;
20 private final long value;
21 private final boolean relative;
22
23 public SetRequest(String subsystem, String name, long value, boolean relative, boolean persist) {
24 this.subsystem = subsystem;
25 this.name = name;
26 this.persist = persist;
27 this.value = value;
28 this.relative = relative;
29
30 }
31
32 @Override
33 public Envelope assembleRequest() {
34 SetMessage m = new SetMessage();
35 m.statisticName = name;
36 m.subsystemName = subsystem;
37 m.value = value;
38 if (relative)
39 m.flags |= SETFLAG_RELATIVE;
40 if (persist)
41 m.flags |= SETFLAG_PERSIST;
42 return new Envelope(m);
43 }
44
45 public void cancel() {
46 //To change body of implemented methods use File | Settings | File Templates.
47 }
48}
diff --git a/src/main/java/org/gnunet/statistics/Statistics.java b/src/main/java/org/gnunet/statistics/Statistics.java
new file mode 100644
index 0000000..416db0c
--- /dev/null
+++ b/src/main/java/org/gnunet/statistics/Statistics.java
@@ -0,0 +1,310 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21/*
22 * The stuff below does nothing whatsoever, first milestone of
23 * this project is to implement the StatisticsService api
24 *
25 */
26
27package org.gnunet.statistics;
28
29import org.gnunet.requests.MatchingRequestContainer;
30import org.gnunet.requests.SequentialRequestContainer;
31import org.gnunet.util.*;
32import org.gnunet.util.getopt.Argument;
33import org.gnunet.util.getopt.ArgumentAction;
34import org.slf4j.Logger;
35import org.slf4j.LoggerFactory;
36
37/**
38 * API for the GNUnet statistics service.
39 * <p/>
40 * Set, get and monitor statistics values, represented as unsigned 64bit integer.
41 * Note that {@literal long}, java's largest primitive type, can only store signed 64bit integers.
42 * With absolute operation, its negative values are interpreted as large numbers by the statistics api.
43 */
44public class Statistics {
45 private static final Logger logger = LoggerFactory
46 .getLogger(Statistics.class);
47
48 /**
49 * Client connecting us to the statistics service.
50 */
51 private final Client client;
52
53 SequentialRequestContainer<SetRequest> set_requests;
54 SequentialRequestContainer<GetRequest> get_requests;
55 MatchingRequestContainer<Long,WatchRequest> watch_requests;
56
57 private boolean destroy_requested;
58
59 private long wid = 0;
60
61 public class StatisticsMessageReceiver extends RunaboutMessageReceiver {
62 public void visit(GetResponseMessage m) {
63 GetRequest r = get_requests.getRequest();
64 if (r != null)
65 r.receiver.onReceive(m.subsystemName, m.statisticName, m.value);
66 }
67
68 public void visit(GetResponseEndMessage m) {
69 GetRequest r = get_requests.getRequest();
70 if (r != null)
71 r.receiver.onDone();
72 //noinspection ConstantConditions
73 if (get_requests != null)
74 get_requests.next();
75 }
76
77 public void visit(TestMessage m) {
78 System.out.println("got back TEST message");
79 client.disconnect();
80 }
81
82 public void visit(WatchResponseMessage wrm) {
83 WatchRequest r = watch_requests.getRequest((long) wrm.wid);
84 if (r != null) {
85 r.watcher.onReceive(r.subsystem, r.name, wrm.value);
86 }
87 }
88
89 @Override
90 public void handleError() {
91 if (!destroy_requested) {
92 client.reconnect();
93 get_requests.restart();
94 set_requests.restart();
95 watch_requests.restart();
96 }
97 }
98 }
99
100 public Statistics(Configuration cfg) {
101 client = new Client("statistics", cfg);
102 client.installReceiver(new StatisticsMessageReceiver());
103 get_requests = new SequentialRequestContainer<GetRequest>(client);
104 set_requests = new SequentialRequestContainer<SetRequest>(client);
105 watch_requests = new MatchingRequestContainer<Long, WatchRequest>(client);
106 }
107
108 /**
109 * Retrieve values from statistics.
110 * Only one instance of this request may be active simultaneously.
111 * Upon cancellation
112 *
113 * @param timeout time after we give up and call receiver.onTimeout
114 * @param subsystem the subsystem of interest
115 * @param name name of the statistics value belongs to
116 * @param receiver callback
117 * @return handle to cancel the request
118 */
119 public Cancelable get(RelativeTime timeout, final String subsystem, final String name,
120 final StatisticsReceiver receiver) {
121 if (destroy_requested)
122 throw new AssertionError("already destroyed");
123 return get_requests.addRequest(new GetRequest(subsystem, name, receiver));
124 }
125
126 /**
127 * Sets a statistics value asynchronously.
128 *
129 * @param name name of the entry
130 * @param value desired value
131 * @param persist keep value even if the statistics service restarts
132 * @return a handle to cancel the request
133 */
134 public Cancelable set(final String subsystem, final String name, final long value, boolean persist) {
135 if (destroy_requested)
136 throw new AssertionError("already destroyed");
137 return set_requests.addRequest(new SetRequest(subsystem, name, value, false, persist));
138 }
139
140 /**
141 * Changes a statistics value asynchronously.
142 *
143 * @param name name of the entry
144 * @param delta relative difference to the old value
145 * @param persist keep value even if the statistics service restarts
146 * @return a handle to cancel the request
147 */
148 public Cancelable update(final String subsystem, final String name, final long delta, boolean persist) {
149 if (destroy_requested)
150 throw new AssertionError("already destroyed");
151 return set_requests.addRequest(new SetRequest(subsystem, name, delta, true, persist));
152 }
153
154 /**
155 * Receive updates about changing statistics values.
156 *
157 * @param subsystem the subsystem to watch
158 * @param name the value to watch
159 * @param watcher the object that receives the updates
160 * @return a handle to cancel the request
161 */
162 public Cancelable watch(final String subsystem, final String name, StatisticsWatcher watcher) {
163 if (destroy_requested)
164 throw new AssertionError("already destroyed");
165 WatchRequest r = new WatchRequest(subsystem, name, watcher);
166 return watch_requests.addRequest(wid++, r);
167 }
168
169 /**
170 * Destroy handle to the statistics service. Always finishes writing pending values.
171 */
172 public void destroy() {
173 destroy_requested = true;
174 client.send(new TestMessage());
175 // wait until the service responds
176 // TODO: or timeout
177 System.out.println("destroying statistics");
178 }
179
180
181 /**
182 * Statistics command line utility entry point
183 *
184 * @param args command line arguments
185 */
186 public static void main(String[] args) {
187 new Program(args) {
188 @Argument(
189 shortname = "x",
190 longname = "set",
191 action = ArgumentAction.SET,
192 description = "watch a value")
193 boolean set;
194 @Argument(
195 shortname = "w",
196 longname = "watch",
197 action = ArgumentAction.SET,
198 description = "set a value")
199 boolean watch;
200 @Argument(
201 shortname = "n",
202 longname = "name",
203 action = ArgumentAction.STORE_STRING,
204 argumentName = "NAME",
205 description = "statistics name")
206 String statisticsName = "";
207 @Argument(
208 shortname = "s",
209 longname = "subsystem",
210 argumentName = "SUBSYS",
211 action = ArgumentAction.STORE_STRING,
212 description = "subsystem name")
213 String subsystemName = "";
214 @Argument(
215 shortname = "p",
216 longname = "persistent",
217 action = ArgumentAction.SET,
218 description = "set value persistently (used with -x)")
219 boolean persistent = false;
220 @Argument(
221 shortname = "r",
222 longname = "relative",
223 action = ArgumentAction.SET,
224 description = "set value relative to old value (used with -x)")
225 boolean relative = false;
226
227 @Override
228 protected String makeHelpText() {
229 return "Get, set and watch GNUnet's statistics.";
230 }
231
232 public void run() {
233 final Statistics statistics = new Statistics(cfg);
234
235 if (set && watch) {
236 System.err.println("--watch/-w and --set/-s cannot be used together");
237 return;
238 }
239
240 if (set) {
241 if (subsystemName.isEmpty() || statisticsName.isEmpty()) {
242 System.err.println("both subsystem and name must be given for --set/-x");
243 return;
244 }
245 if (unprocessedArgs.length != 1) {
246 System.err.println("must specify exactly one value to set");
247 return;
248 }
249 long value;
250 try {
251 value = Long.parseLong(unprocessedArgs[0]);
252 } catch (NumberFormatException e) {
253 System.err.println("invalid value (not a long)");
254 return;
255 }
256 if (relative)
257 statistics.update(subsystemName, statisticsName, value, persistent);
258 else
259 statistics.set(subsystemName, statisticsName, value, persistent);
260 statistics.destroy();
261 return;
262 }
263
264 if (unprocessedArgs.length != 0) {
265 System.err.println("dumping statistics does not take any positional parameters");
266 return;
267 }
268
269 if (watch) {
270 if (subsystemName.isEmpty() || statisticsName.isEmpty()) {
271 System.err.println("both subsystem and name must be given for --watch/-w");
272 return;
273 }
274 statistics.watch(subsystemName, statisticsName,
275 new StatisticsWatcher() {
276 @Override
277 public void onReceive(String subsystem, String name, long value) {
278 System.out.println(subsystem + "(" + name + ") = " + value);
279 }
280
281 @Override
282 public void onTimeout() {
283 System.err.println("timeout");
284 }
285 }
286 );
287 } else {
288 statistics.get(RelativeTime.SECOND, subsystemName, statisticsName,
289 new StatisticsReceiver() {
290 @Override
291 public void onReceive(String subsystem, String name, long value) {
292 System.out.println(subsystem + "(" + name + ") = " + value);
293 }
294
295 @Override
296 public void onTimeout() {
297 System.err.println("timeout");
298 }
299
300 @Override
301 public void onDone() {
302 statistics.destroy();
303 }
304 }
305 );
306 }
307 }
308 }.start();
309 }
310}
diff --git a/src/main/java/org/gnunet/statistics/StatisticsReceiver.java b/src/main/java/org/gnunet/statistics/StatisticsReceiver.java
new file mode 100644
index 0000000..0c25acc
--- /dev/null
+++ b/src/main/java/org/gnunet/statistics/StatisticsReceiver.java
@@ -0,0 +1,29 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.statistics;
22
23
24
25public interface StatisticsReceiver {
26 public void onReceive(String subsystem, String name, long value);
27 public void onTimeout();
28 public void onDone();
29}
diff --git a/src/main/java/org/gnunet/statistics/StatisticsWatcher.java b/src/main/java/org/gnunet/statistics/StatisticsWatcher.java
new file mode 100644
index 0000000..d3fb645
--- /dev/null
+++ b/src/main/java/org/gnunet/statistics/StatisticsWatcher.java
@@ -0,0 +1,13 @@
1package org.gnunet.statistics;
2
3/**
4 * Created with IntelliJ IDEA.
5 * User: dold
6 * Date: 8/24/13
7 * Time: 5:56 PM
8 * To change this template use File | Settings | File Templates.
9 */
10public interface StatisticsWatcher {
11 public void onReceive(String subsystem, String name, long value);
12 public void onTimeout();
13}
diff --git a/src/main/java/org/gnunet/statistics/WatchMessage.java b/src/main/java/org/gnunet/statistics/WatchMessage.java
new file mode 100644
index 0000000..814f263
--- /dev/null
+++ b/src/main/java/org/gnunet/statistics/WatchMessage.java
@@ -0,0 +1,40 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.statistics;
22
23
24import org.gnunet.construct.UnionCase;
25import org.gnunet.construct.ZeroTerminatedString;
26import org.gnunet.util.GnunetMessage;
27
28@UnionCase(172)
29public class WatchMessage implements GnunetMessage.Body {
30 /**
31 * Subsystem of interest, may not be empty.
32 */
33 @ZeroTerminatedString
34 public String subsystemName;
35 /**
36 * Statistics value name of interest, may not be empty.
37 */
38 @ZeroTerminatedString
39 public String statisticsName;
40}
diff --git a/src/main/java/org/gnunet/statistics/WatchRequest.java b/src/main/java/org/gnunet/statistics/WatchRequest.java
new file mode 100644
index 0000000..49e1615
--- /dev/null
+++ b/src/main/java/org/gnunet/statistics/WatchRequest.java
@@ -0,0 +1,31 @@
1package org.gnunet.statistics;
2
3import org.gnunet.mq.Envelope;
4import org.gnunet.mq.MessageQueue;
5import org.gnunet.requests.RequestContainer;
6
7/**
8 */
9public class WatchRequest extends RequestContainer.Request {
10 public String subsystem;
11 public String name;
12 public StatisticsWatcher watcher;
13
14 public WatchRequest(String subsystem, String name, StatisticsWatcher watcher) {
15 this.subsystem = subsystem;
16 this.name = name;
17 this.watcher = watcher;
18 }
19
20 @Override
21 public Envelope assembleRequest() {
22 WatchMessage m = new WatchMessage();
23 m.statisticsName = name;
24 m.subsystemName = subsystem;
25 return new Envelope(m);
26 }
27
28 public void cancel() {
29
30 }
31}
diff --git a/src/main/java/org/gnunet/statistics/WatchResponseMessage.java b/src/main/java/org/gnunet/statistics/WatchResponseMessage.java
new file mode 100644
index 0000000..19c56b5
--- /dev/null
+++ b/src/main/java/org/gnunet/statistics/WatchResponseMessage.java
@@ -0,0 +1,39 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.statistics;
22
23
24import org.gnunet.construct.UInt32;
25import org.gnunet.construct.UInt64;
26import org.gnunet.construct.UnionCase;
27import org.gnunet.util.GnunetMessage;
28
29@UnionCase(173)
30public class WatchResponseMessage implements GnunetMessage.Body {
31 @UInt32
32 public int flags;
33 @UInt32
34 public int wid;
35 @UInt32
36 public int reserved ;
37 @UInt64
38 public long value;
39}
diff --git a/src/main/java/org/gnunet/statistics/package-info.java b/src/main/java/org/gnunet/statistics/package-info.java
new file mode 100644
index 0000000..fce59e7
--- /dev/null
+++ b/src/main/java/org/gnunet/statistics/package-info.java
@@ -0,0 +1,24 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21/**
22 * API for the gnunet statistics service.
23 */
24package org.gnunet.statistics;
diff --git a/src/main/java/org/gnunet/testbed/Controller.java b/src/main/java/org/gnunet/testbed/Controller.java
new file mode 100644
index 0000000..7372986
--- /dev/null
+++ b/src/main/java/org/gnunet/testbed/Controller.java
@@ -0,0 +1,84 @@
1package org.gnunet.testbed;
2
3import org.gnunet.util.Client;
4import org.gnunet.util.Configuration;
5
6/**
7 * Handle to interact with a GNUnet testbed controller. Each
8 * controller has at least one master handle which is created when the
9 * controller is created; this master handle interacts with the
10 * controller process, destroying it destroys the controller (by
11 * closing stdin of the controller process). Additionally,
12 * controllers can interact with each other (in a P2P fashion); those
13 * links are established via TCP/IP on the controller's service port.
14 */
15public class Controller {
16
17 /**
18 * Client connecting to the testbed service.
19 */
20 Client client;
21
22 /**
23 * Connect to a controller process. The configuration to use for the connection
24 * is retreived from the given host where a controller is started using
25 * GNUNET_TESTBED_controller_start().
26 *
27 * @param host host to run the controller on; This should be the same host if
28 * the controller was previously started with
29 * GNUNET_TESTBED_controller_start()
30 * @param event_mask bit mask with set of events to call 'cc' for;
31 * or-ed values of "1LL" shifted by the
32 * respective 'enum GNUNET_TESTBED_EventType'
33 * (i.e. "(1LL << GNUNET_TESTBED_ET_CONNECT) | ...")
34 * @param cb controller callback to invoke on events
35 */
36 public Controller(Host host, long event_mask, ControllerEventCallback cb) {
37 client = new Client("testbed", host.cfg);
38 }
39
40
41 /**
42 * Create the given peer at the specified host using the given
43 * controller. If the given controller is not running on the target
44 * host, it should find or create a controller at the target host and
45 * delegate creating the peer. Explicit delegation paths can be setup
46 * using 'GNUNET_TESTBED_controller_link'. If no explicit delegation
47 * path exists, a direct link with a subordinate controller is setup
48 * for the first delegated peer to a particular host; the subordinate
49 * controller is then destroyed once the last peer that was delegated
50 * to the remote host is stopped.
51 *
52 * Creating the peer only creates the handle to manipulate and further
53 * configure the peer; use "GNUNET_TESTBED_peer_start" and
54 * "GNUNET_TESTBED_peer_stop" to actually start/stop the peer's
55 * processes.
56 *
57 * Note that the given configuration will be adjusted by the
58 * controller to avoid port/path conflicts with other peers.
59 * The "final" configuration can be obtained using
60 * 'GNUNET_TESTBED_peer_get_information'.
61 *
62 * @param host host to run the peer on; cannot be NULL
63 * @param cfg Template configuration to use for the peer. Should exist until
64 * operation is cancelled or GNUNET_TESTBED_operation_done() is called
65 * @param cb the callback to call when the peer has been created
66 * @return the operation handle
67 */
68
69 public Operation createPeer(Host host, Configuration cfg, PeerCreateCallback cb) {
70 return null;
71 }
72
73
74 /**
75 * Stop the given controller (also will terminate all peers and
76 * controllers dependent on this controller). This function
77 * blocks until the testbed has been fully terminated (!).
78 */
79 public void disconnect () {
80
81 }
82
83
84}
diff --git a/src/main/java/org/gnunet/testbed/ControllerEventCallback.java b/src/main/java/org/gnunet/testbed/ControllerEventCallback.java
new file mode 100644
index 0000000..099977f
--- /dev/null
+++ b/src/main/java/org/gnunet/testbed/ControllerEventCallback.java
@@ -0,0 +1,21 @@
1package org.gnunet.testbed;
2
3
4
5public abstract class ControllerEventCallback {
6 void onPeerStart() {
7 throw new AssertionError("event not handled");
8 }
9 void onPeerStop() {
10 throw new AssertionError("event not handled");
11 }
12 void onPeerConnect() {
13 throw new AssertionError("event not handled");
14 }
15 void onPeerDisconnect() {
16 throw new AssertionError("event not handled");
17 }
18 void onOperationFinished() {
19 throw new AssertionError("event not handled");
20 }
21}
diff --git a/src/main/java/org/gnunet/testbed/ControllerInitMessage.java b/src/main/java/org/gnunet/testbed/ControllerInitMessage.java
new file mode 100644
index 0000000..95114d7
--- /dev/null
+++ b/src/main/java/org/gnunet/testbed/ControllerInitMessage.java
@@ -0,0 +1,30 @@
1package org.gnunet.testbed;
2
3
4import org.gnunet.construct.UInt32;
5import org.gnunet.construct.UInt64;
6import org.gnunet.construct.UnionCase;
7import org.gnunet.construct.ZeroTerminatedString;
8import org.gnunet.util.GnunetMessage;
9
10@UnionCase(460)
11public class ControllerInitMessage implements GnunetMessage.Body {
12 /**
13 * Host ID that the controller is either given (if this is the
14 * dominating client) or assumed to have (for peer-connections
15 * between controllers). A controller must check that all
16 * connections make consistent claims...
17 */
18 @UInt32
19 public int host_id;
20
21 /**
22 * Event mask that specifies which events this client is interested in.
23 */
24 @UInt64
25 public long event_mask;
26
27 @ZeroTerminatedString
28 public String controler_hostname;
29
30}
diff --git a/src/main/java/org/gnunet/testbed/ControllerProc.java b/src/main/java/org/gnunet/testbed/ControllerProc.java
new file mode 100644
index 0000000..9eaca2c
--- /dev/null
+++ b/src/main/java/org/gnunet/testbed/ControllerProc.java
@@ -0,0 +1,89 @@
1package org.gnunet.testbed;
2
3import com.google.common.base.Charsets;
4import org.gnunet.util.Helper;
5import org.gnunet.util.RunaboutMessageReceiver;
6
7import java.io.ByteArrayOutputStream;
8import java.util.zip.Deflater;
9
10/**
11 * A controller process.
12 * The controller process is either a local helper process, or an ssh process that starts and controls
13 * the testbed helper on a remote machine.
14 */
15public class ControllerProc {
16 final Helper helper;
17
18
19 public class ControllerProcReceiver extends RunaboutMessageReceiver {
20 public void visit(HelperReplyMessage m) {
21
22 }
23 @Override
24 public void handleError() {
25
26 }
27 }
28
29 /**
30 * Starts a controller process at the given host. The given host's configration
31 * is used as a Template configuration to use for the remote controller; the
32 * remote controller will be started with a slightly modified configuration
33 * (port numbers, unix domain sockets and service home values are changed as per
34 * TESTING library on the remote host). The modified configuration replaces the
35 * host's existing configuration before signalling success through the
36 * GNUNET_TESTBED_ControllerStatusCallback()
37 *
38 * @param trustedIP the ip address of the controller which will be set as TRUSTED
39 * HOST(all connections form this ip are permitted by the testbed) when
40 * starting testbed controller at host. This can either be a single ip
41 * address or a network address in CIDR notation.
42 * @param host the host where the controller has to be started. CANNOT be NULL.
43 * @param cb function called when the controller is successfully started or
44 * dies unexpectedly; GNUNET_TESTBED_controller_stop shouldn't be
45 * called if cb is called with GNUNET_SYSERR as status. Will never be
46 * called in the same task as 'GNUNET_TESTBED_controller_start'
47 * (synchronous errors will be signalled by returning NULL). This
48 * parameter cannot be NULL.
49 */
50 public ControllerProc(String trustedIP, Host host, ControllerStatusCallback cb) {
51 if (host.isLocal()) {
52 helper = new Helper(false, "gnunet-testbed-helper", null, new ControllerProcReceiver());
53 } else {
54 throw new AssertionError("not implemented yet");
55 }
56 }
57
58
59 private HelperInitMessage makeHelperInitMessage(String trustedIP, Host host) {
60 HelperInitMessage m = new HelperInitMessage();
61 if (host.hostname == null) {
62 m.hostname = null;
63 m.hostname_size = 0;
64 } else {
65 m.hostname_size = host.hostname.length();
66 m.hostname = host.hostname.getBytes(Charsets.UTF_8);
67 }
68 m.trusted_ip_size = trustedIP.length();
69 m.trusted_ip = trustedIP;
70
71 byte[] serialized_config = host.cfg.serialize().getBytes();
72
73 Deflater compresser = new Deflater();
74 compresser.setInput(serialized_config);
75 compresser.finish();
76
77 ByteArrayOutputStream s = new ByteArrayOutputStream();
78 byte[] buf = new byte[1024];
79 while (!compresser.finished()) {
80 int n = compresser.deflate(buf);
81 s.write(buf, 0, n);
82 }
83
84 m.compressed_config = s.toByteArray();
85 m.config_size = serialized_config.length;
86
87 return m;
88 }
89}
diff --git a/src/main/java/org/gnunet/testbed/ControllerStatusCallback.java b/src/main/java/org/gnunet/testbed/ControllerStatusCallback.java
new file mode 100644
index 0000000..ba9e56f
--- /dev/null
+++ b/src/main/java/org/gnunet/testbed/ControllerStatusCallback.java
@@ -0,0 +1,9 @@
1package org.gnunet.testbed;
2
3
4import org.gnunet.util.Configuration;
5
6public interface ControllerStatusCallback {
7 void onStartupSuccess(Configuration cfg);
8 void onStartupFailure();
9}
diff --git a/src/main/java/org/gnunet/testbed/HelperInitMessage.java b/src/main/java/org/gnunet/testbed/HelperInitMessage.java
new file mode 100644
index 0000000..29d8bb6
--- /dev/null
+++ b/src/main/java/org/gnunet/testbed/HelperInitMessage.java
@@ -0,0 +1,37 @@
1package org.gnunet.testbed;
2
3import org.gnunet.construct.*;
4import org.gnunet.util.GnunetMessage;
5
6/**
7 * Initialization message for gnunet-helper-testbed to start testbed service
8 */
9@UnionCase(495)
10public class HelperInitMessage implements GnunetMessage.Body {
11 /**
12 * The controller hostname size excluding the NULL termination character -
13 * strlen (hostname); cannot be zero
14 */
15 @UInt16
16 int trusted_ip_size;
17 /**
18 * The hostname size excluding the NULL termination character - strlen
19 * (hostname); cannot be zero
20 */
21 @UInt16
22 int hostname_size;
23 /**
24 * The size of the uncompressed configuration
25 */
26 @UInt16
27 public int config_size;
28
29 @ZeroTerminatedString(optional = true)
30 public String trusted_ip;
31
32 @VariableSizeIntegerArray(signed = true, bitSize = 8, lengthField = "hostname_size")
33 public byte[] hostname;
34
35 @FillWith @UInt8
36 public byte[] compressed_config;
37}
diff --git a/src/main/java/org/gnunet/testbed/HelperReplyMessage.java b/src/main/java/org/gnunet/testbed/HelperReplyMessage.java
new file mode 100644
index 0000000..bb50860
--- /dev/null
+++ b/src/main/java/org/gnunet/testbed/HelperReplyMessage.java
@@ -0,0 +1,17 @@
1package org.gnunet.testbed;
2
3
4import org.gnunet.construct.FillWith;
5import org.gnunet.construct.UInt16;
6import org.gnunet.construct.UInt8;
7import org.gnunet.construct.UnionCase;
8import org.gnunet.util.GnunetMessage;
9
10@UnionCase(496)
11public class HelperReplyMessage implements GnunetMessage.Body {
12 @UInt16
13 int uncompressed_config_size;
14
15 @FillWith @UInt8
16 byte[] compressed_config;
17}
diff --git a/src/main/java/org/gnunet/testbed/Host.java b/src/main/java/org/gnunet/testbed/Host.java
new file mode 100644
index 0000000..fb89028
--- /dev/null
+++ b/src/main/java/org/gnunet/testbed/Host.java
@@ -0,0 +1,51 @@
1package org.gnunet.testbed;
2
3import org.gnunet.util.Configuration;
4
5/**
6 * Opaque handle to a host running experiments managed by the testing framework.
7 * The master process must be able to SSH to this host without password (via
8 * ssh-agent).
9 */
10public class Host {
11 static int nextUID = 1;
12 final Configuration cfg;
13 final String hostname;
14 final String username;
15 final int port;
16
17 private boolean controllerStarted;
18
19 /**
20 * Create a host to run peers and controllers on.
21 *
22 * @param hostname name of the host, use "NULL" for localhost
23 * @param username username to use for the login; may be NULL
24 * @param cfg the configuration to use as a template while starting a controller
25 * on this host. Operation queue sizes specific to a host are also
26 * read from this configuration handle
27 * @param port port number to use for ssh; use 0 to let ssh decide
28 */
29 public Host(String hostname, String username, Configuration cfg, int port) {
30 this.port = (port == 0) ? 22 : port;
31 this.hostname = hostname;
32 this.username = username;
33 this.cfg = cfg;
34 }
35
36 /**
37 * Create a host handle for the local machine.
38 *
39 * @param cfg the configuration to use as a template while starting a controller
40 * on this host. Operation queue sizes specific to a host are also
41 * read from this configuration handle
42 * @param port port number to use for ssh; use 0 to let ssh decide
43 */
44 public Host(Configuration cfg, int port) {
45 this(null, null, cfg, port);
46 }
47
48 public boolean isLocal() {
49 return hostname == null;
50 }
51}
diff --git a/src/main/java/org/gnunet/testbed/Operation.java b/src/main/java/org/gnunet/testbed/Operation.java
new file mode 100644
index 0000000..4403540
--- /dev/null
+++ b/src/main/java/org/gnunet/testbed/Operation.java
@@ -0,0 +1,11 @@
1package org.gnunet.testbed;
2
3/**
4 * Created with IntelliJ IDEA.
5 * User: dold
6 * Date: 8/25/13
7 * Time: 1:26 PM
8 * To change this template use File | Settings | File Templates.
9 */
10public interface Operation {
11}
diff --git a/src/main/java/org/gnunet/testbed/OperationCompletionCallback.java b/src/main/java/org/gnunet/testbed/OperationCompletionCallback.java
new file mode 100644
index 0000000..413be06
--- /dev/null
+++ b/src/main/java/org/gnunet/testbed/OperationCompletionCallback.java
@@ -0,0 +1,11 @@
1package org.gnunet.testbed;
2
3/**
4 * Created with IntelliJ IDEA.
5 * User: dold
6 * Date: 8/25/13
7 * Time: 1:35 PM
8 * To change this template use File | Settings | File Templates.
9 */
10public interface OperationCompletionCallback {
11}
diff --git a/src/main/java/org/gnunet/testbed/Peer.java b/src/main/java/org/gnunet/testbed/Peer.java
new file mode 100644
index 0000000..f9cfc3e
--- /dev/null
+++ b/src/main/java/org/gnunet/testbed/Peer.java
@@ -0,0 +1,57 @@
1package org.gnunet.testbed;
2
3import org.gnunet.util.Configuration;
4
5/**
6 * Opaque handle to a peer controlled by the testbed framework. A peer runs
7 * at a particular host.
8 */
9public class Peer {
10 public Operation start(PeerChurnCallback peerChurnCallback) {
11 return null;
12 }
13
14 public Operation stop(PeerChurnCallback peerChurnCallback) {
15 return null;
16 }
17
18 public Operation getInformation() {
19 return null;
20 }
21
22 /*
23 * Change peer configuration. Must only be called while the
24 * peer is stopped. Ports and paths cannot be changed this
25 * way.
26 */
27 public Operation updateConfiguration(Configuration cfg) {
28 return null;
29 }
30
31 /*
32 * Change peer configuration. Must only be called while the
33 * peer is stopped. Ports and paths cannot be changed this
34 * way.
35 */
36 public Operation destroy() {
37 return null;
38 }
39
40 public Operation manageService(String serviceName, boolean start) {
41 return null;
42 }
43
44 /**
45 * Both peers must have been started before calling this function.
46 * This function then obtains a HELLO from this peer, gives it to 'otherPeer'
47 * and asks 'otherPeer' to connect to this peer..
48 */
49 public Operation connectOverlay(OperationCompletionCallback cb, Peer otherPeer) {
50 return null;
51 }
52
53 public Operation getServiceConnection(String serviceName /*,... */) {
54 return null;
55 }
56
57}
diff --git a/src/main/java/org/gnunet/testbed/PeerChurnCallback.java b/src/main/java/org/gnunet/testbed/PeerChurnCallback.java
new file mode 100644
index 0000000..a5dacbd
--- /dev/null
+++ b/src/main/java/org/gnunet/testbed/PeerChurnCallback.java
@@ -0,0 +1,7 @@
1package org.gnunet.testbed;
2
3
4public interface PeerChurnCallback {
5 void onChurnSuccess();
6 void onChurnError(String emsg);
7}
diff --git a/src/main/java/org/gnunet/testbed/PeerCreateCallback.java b/src/main/java/org/gnunet/testbed/PeerCreateCallback.java
new file mode 100644
index 0000000..b6100f6
--- /dev/null
+++ b/src/main/java/org/gnunet/testbed/PeerCreateCallback.java
@@ -0,0 +1,7 @@
1package org.gnunet.testbed;
2
3
4public interface PeerCreateCallback {
5 void onPeerCreated(Peer peer);
6 void onError(String errorMessage);
7}
diff --git a/src/main/java/org/gnunet/testing/TestingFixture.java b/src/main/java/org/gnunet/testing/TestingFixture.java
new file mode 100644
index 0000000..4303512
--- /dev/null
+++ b/src/main/java/org/gnunet/testing/TestingFixture.java
@@ -0,0 +1,21 @@
1package org.gnunet.testing;
2
3import org.gnunet.util.Scheduler;
4import org.junit.After;
5import org.junit.Before;
6
7/**
8 * Default JUnit4 fixture methods for gnunet-java tests.
9 * Resets the scheduler properly.
10 */
11public class TestingFixture {
12 @Before
13 public void beginGNJTest() {
14 Scheduler.forceReset();
15 }
16
17 @After
18 public void endGNJTest() {
19 Scheduler.forceReset();
20 }
21}
diff --git a/src/main/java/org/gnunet/testing/TestingServer.java b/src/main/java/org/gnunet/testing/TestingServer.java
new file mode 100644
index 0000000..97b6bc0
--- /dev/null
+++ b/src/main/java/org/gnunet/testing/TestingServer.java
@@ -0,0 +1,65 @@
1package org.gnunet.testing;
2
3import org.gnunet.util.Client;
4import org.gnunet.util.RelativeTime;
5import org.gnunet.util.Server;
6
7import java.io.IOException;
8import java.net.InetAddress;
9import java.net.InetSocketAddress;
10import java.net.SocketAddress;
11import java.nio.channels.ServerSocketChannel;
12
13/**
14 * Server with an ephemeral port.
15 * Can spawn clients connected to the server for testing.
16 *
17 * @author Florian Dold
18 */
19public class TestingServer {
20 public final Server server;
21 private final ServerSocketChannel srvChan;
22
23 public TestingServer() {
24 this(RelativeTime.FOREVER, true);
25 }
26
27 public TestingServer(RelativeTime idleTimeout, boolean requireFound) {
28 try {
29 srvChan = ServerSocketChannel.open();
30 srvChan.configureBlocking(false);
31
32 // bind to ephemeral port
33 srvChan.socket().bind(null);
34 } catch (IOException e) {
35 throw new RuntimeException("TestingServer creation failed");
36 }
37
38 server = new Server(idleTimeout, requireFound);
39 server.addAcceptSocket(srvChan);
40
41 }
42
43 /**
44 * Create a client connected to this server.
45 *
46 * @return a client connected to this server
47 */
48 public Client createClient() {
49 SocketAddress socketAddress = srvChan.socket().getLocalSocketAddress();
50
51 if (!(socketAddress instanceof InetSocketAddress)) {
52 throw new RuntimeException("unknown type of socket address");
53 }
54 InetSocketAddress saddr = (InetSocketAddress) socketAddress;
55
56 String hostname = saddr.getHostName();
57
58 if (hostname == null) {
59 throw new RuntimeException("localhost SocketAddress has no hostname");
60 }
61
62 return new Client(hostname, srvChan.socket().getLocalPort());
63 }
64
65}
diff --git a/src/main/java/org/gnunet/testing/TestingSetup.java b/src/main/java/org/gnunet/testing/TestingSetup.java
new file mode 100644
index 0000000..6f8d578
--- /dev/null
+++ b/src/main/java/org/gnunet/testing/TestingSetup.java
@@ -0,0 +1,56 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.testing;
22
23import org.gnunet.util.Program;
24import org.gnunet.util.RelativeTime;
25
26/**
27 * A testing setup is responsible for configuring the loggers during testing, and can
28 * start gnunet subsystems (like statistics, core, etc.).
29 *
30 * @author Florian Dold
31 */
32public final class TestingSetup {
33
34 private TestingSetup() {
35
36 }
37
38 public static class SetupException extends RuntimeException {
39 public SetupException(Exception e) {
40 super(e);
41 }
42 public SetupException(String msg) {
43 super(msg);
44 }
45
46 }
47
48 public static void setup() {
49 String log = System.getenv("GNJ_LOGLEVEL");
50 if (log != null) {
51 Program.configureLogging(log, null);
52 } else {
53 Program.configureLogging("WARN", null);
54 }
55 }
56}
diff --git a/src/main/java/org/gnunet/testing/TestingSubsystem.java b/src/main/java/org/gnunet/testing/TestingSubsystem.java
new file mode 100644
index 0000000..da7f2e4
--- /dev/null
+++ b/src/main/java/org/gnunet/testing/TestingSubsystem.java
@@ -0,0 +1,133 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.testing;
22
23import com.google.common.base.Charsets;
24import org.gnunet.util.Configuration;
25import org.slf4j.Logger;
26import org.slf4j.LoggerFactory;
27
28import java.io.BufferedReader;
29import java.io.IOException;
30import java.io.InputStreamReader;
31import java.io.OutputStreamWriter;
32
33/**
34 * Handle to a GNUnet subsystem that has been started for testing purposes.
35 *
36 * @author Florian Dold
37*/
38public class TestingSubsystem {
39 private static final Logger logger = LoggerFactory
40 .getLogger(TestingSubsystem.class);
41
42
43 private Process p;
44 private BufferedReader reader;
45 private OutputStreamWriter writer;
46 private Configuration cfg;
47
48 public Configuration getConfiguration() {
49 return cfg;
50 }
51
52 public TestingSubsystem(String service) {
53 try {
54 p = Runtime.getRuntime().exec(new String[]{"gnunet-testing", "-r", service});
55 } catch (IOException e) {
56 throw new TestingSetup.SetupException(e);
57 }
58
59 reader = new BufferedReader(new InputStreamReader(p.getInputStream(), Charsets.UTF_8));
60
61 writer = new OutputStreamWriter(p.getOutputStream(), Charsets.UTF_8);
62
63 String started;
64 try {
65 started = reader.readLine();
66 } catch (IOException e) {
67 throw new TestingSetup.SetupException(e);
68 }
69
70 if (started == null || !started.equals("ok")) {
71 throw new TestingSetup.SetupException("could not start service ('" + started + "')");
72 }
73
74
75 String cfgFileName;
76 try {
77 cfgFileName = reader.readLine();
78 } catch (IOException e) {
79 throw new TestingSetup.SetupException(e);
80 }
81
82 if (cfgFileName == null) {
83 throw new TestingSetup.SetupException("could not start subsystem for testing: no config file received from helper");
84 }
85
86 cfg = new Configuration();
87 cfg.parse(cfgFileName);
88
89 try {
90 if (p.getErrorStream().available() != 0) {
91 throw new TestingSetup.SetupException("error starting service");
92 }
93 } catch (IOException e) {
94 throw new TestingSetup.SetupException(e);
95 }
96 }
97 public void destroy() {
98 try {
99 writer.write("q\n");
100 writer.flush();
101 } catch (IOException e) {
102 throw new TestingSetup.SetupException(e);
103 }
104 try {
105 p.waitFor();
106 } catch (InterruptedException e) {
107 throw new TestingSetup.SetupException(e);
108 }
109 if (p.exitValue() != 0) {
110 throw new TestingSetup.SetupException("gnunet-testing exit value unsuccessful");
111 }
112 }
113
114 public void restart() {
115 try {
116 writer.write("r\n");
117 writer.flush();
118 } catch (IOException e) {
119 throw new TestingSetup.SetupException(e);
120 }
121 String response;
122 logger.debug("waiting for gnunet-testing to respond to restart");
123 try {
124 response = reader.readLine();
125 } catch (IOException e) {
126 throw new TestingSetup.SetupException(e);
127 }
128 if (response == null || !response.equals("restarted")) {
129 throw new TestingSetup.SetupException("wrapper did not cooperate");
130 }
131 logger.debug("restart successful");
132 }
133}
diff --git a/src/main/java/org/gnunet/testing/package-info.java b/src/main/java/org/gnunet/testing/package-info.java
new file mode 100644
index 0000000..1a91586
--- /dev/null
+++ b/src/main/java/org/gnunet/testing/package-info.java
@@ -0,0 +1,27 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21
22/**
23 * Various utilities for testing.
24 *
25 * This is in the main source code location, as the testing package itself has unit tests.
26 */
27package org.gnunet.testing;
diff --git a/src/main/java/org/gnunet/transport/AddressIterateMessage.java b/src/main/java/org/gnunet/transport/AddressIterateMessage.java
new file mode 100644
index 0000000..59ea6a8
--- /dev/null
+++ b/src/main/java/org/gnunet/transport/AddressIterateMessage.java
@@ -0,0 +1,34 @@
1package org.gnunet.transport;
2
3
4import org.gnunet.construct.NestedMessage;
5import org.gnunet.construct.UInt32;
6import org.gnunet.construct.UnionCase;
7import org.gnunet.util.AbsoluteTime;
8import org.gnunet.util.PeerIdentity;
9
10
11/**
12 * Message from the client to the transport service
13 * asking for binary addresses known for a peer.
14 */
15@UnionCase(380)
16public class AddressIterateMessage {
17 /**
18 * One shot call or continous replies?
19 */
20 @UInt32
21 public int one_shot;
22
23 /**
24 * FIXME: This field seems to be deprecated in the C API?
25 */
26 @NestedMessage
27 public AbsoluteTime timeout;
28
29 /**
30 * The identity of the peer to look up.
31 */
32 @NestedMessage
33 public PeerIdentity peer;
34}
diff --git a/src/main/java/org/gnunet/transport/BlacklistCallback.java b/src/main/java/org/gnunet/transport/BlacklistCallback.java
new file mode 100644
index 0000000..b763dec
--- /dev/null
+++ b/src/main/java/org/gnunet/transport/BlacklistCallback.java
@@ -0,0 +1,12 @@
1package org.gnunet.transport;
2
3import org.gnunet.util.PeerIdentity;
4
5/**
6 * ...
7 *
8 * @author Florian Dold
9 */
10public interface BlacklistCallback {
11 boolean isAllowed(PeerIdentity peerIdentity);
12}
diff --git a/src/main/java/org/gnunet/transport/BlacklistInitMessage.java b/src/main/java/org/gnunet/transport/BlacklistInitMessage.java
new file mode 100644
index 0000000..be3209e
--- /dev/null
+++ b/src/main/java/org/gnunet/transport/BlacklistInitMessage.java
@@ -0,0 +1,9 @@
1package org.gnunet.transport;
2
3import org.gnunet.construct.UnionCase;
4import org.gnunet.util.GnunetMessage;
5
6@UnionCase(369)
7public class BlacklistInitMessage implements GnunetMessage.Body {
8 // message body is empty
9}
diff --git a/src/main/java/org/gnunet/transport/HelloUpdateCallback.java b/src/main/java/org/gnunet/transport/HelloUpdateCallback.java
new file mode 100644
index 0000000..d5253b7
--- /dev/null
+++ b/src/main/java/org/gnunet/transport/HelloUpdateCallback.java
@@ -0,0 +1,9 @@
1package org.gnunet.transport;
2
3/**
4 * ...
5 *
6 * @author Florian Dold
7 */
8public class HelloUpdateCallback {
9}
diff --git a/src/main/java/org/gnunet/transport/PeerIterateCallback.java b/src/main/java/org/gnunet/transport/PeerIterateCallback.java
new file mode 100644
index 0000000..25bea5a
--- /dev/null
+++ b/src/main/java/org/gnunet/transport/PeerIterateCallback.java
@@ -0,0 +1,12 @@
1package org.gnunet.transport;
2
3import org.gnunet.util.PeerIdentity;
4
5/**
6 * ...
7 *
8 * @author Florian Dold
9 */
10public interface PeerIterateCallback {
11 void processPeerAddress(PeerIdentity peer, Object hello);
12}
diff --git a/src/main/java/org/gnunet/transport/RequestConnectMessage.java b/src/main/java/org/gnunet/transport/RequestConnectMessage.java
new file mode 100644
index 0000000..63bbc39
--- /dev/null
+++ b/src/main/java/org/gnunet/transport/RequestConnectMessage.java
@@ -0,0 +1,24 @@
1package org.gnunet.transport;
2
3import org.gnunet.construct.NestedMessage;
4import org.gnunet.construct.UInt32;
5import org.gnunet.construct.UnionCase;
6import org.gnunet.util.GnunetMessage;
7import org.gnunet.util.PeerIdentity;
8
9/**
10 * ...
11 *
12 * @author Florian Dold
13 */
14@UnionCase(374)
15public class RequestConnectMessage implements GnunetMessage.Body {
16 @UInt32
17 public int reserved;
18
19 /**
20 * Identity of the peer we would like to connect to.
21 */
22 @NestedMessage
23 public PeerIdentity peer;
24}
diff --git a/src/main/java/org/gnunet/transport/StartMessage.java b/src/main/java/org/gnunet/transport/StartMessage.java
new file mode 100644
index 0000000..2169565
--- /dev/null
+++ b/src/main/java/org/gnunet/transport/StartMessage.java
@@ -0,0 +1,32 @@
1package org.gnunet.transport;
2
3import org.gnunet.construct.NestedMessage;
4import org.gnunet.construct.UInt32;
5import org.gnunet.construct.UnionCase;
6import org.gnunet.util.GnunetMessage;
7import org.gnunet.util.PeerIdentity;
8
9/**
10 * ...
11 *
12 * @author Florian Dold
13 */
14@UnionCase(360)
15public class StartMessage implements GnunetMessage.Body {
16 /**
17 * 0: no options
18 * 1: The 'self' field should be checked
19 * 2: this client is interested in payload traffic
20 */
21 @UInt32
22 public int options;
23
24 /**
25 * Identity we think we have. If it does not match, the
26 * receiver should print out an error message and disconnect.
27 */
28 @NestedMessage
29 public PeerIdentity self;
30
31
32}
diff --git a/src/main/java/org/gnunet/transport/Transport.java b/src/main/java/org/gnunet/transport/Transport.java
new file mode 100644
index 0000000..b9881b6
--- /dev/null
+++ b/src/main/java/org/gnunet/transport/Transport.java
@@ -0,0 +1,156 @@
1package org.gnunet.transport;
2
3import org.gnunet.hello.HelloMessage;
4import org.gnunet.mq.Envelope;
5import org.gnunet.mq.NotifySentHandler;
6import org.gnunet.util.*;
7
8/**
9 * ...
10 *
11 * @author Florian Dold
12 */
13public class Transport {
14
15 /**
16 * Client that connects to the transport service,
17 */
18 private final Client client;
19
20 boolean init_requested;
21
22 /**
23 * Blacklist callback, null if there is no active blacklist
24 * for this handle.
25 */
26 BlacklistCallback blacklistCallback;
27
28 private final class TransportReceiver extends RunaboutMessageReceiver {
29 @Override
30 public void handleError() {
31 client.reconnect();
32 // FIXME: complete
33 }
34 }
35
36 /**
37 * Create a handle to the transport service.
38 *
39 * @param cfg configuration to use for connecting
40 */
41 public Transport(Configuration cfg) {
42 client = new Client("transport", cfg);
43 client.installReceiver(new TransportReceiver());
44 }
45
46 /**
47 * Ask the transport service to establish a connection to
48 * the given peer.
49 *
50 * @param target who we should try to connect to
51 * @param cb callback to be called when request was transmitted to transport
52 * service
53 * @return a handle to cancel the operation
54 */
55 Cancelable tryConnect(PeerIdentity target, final TryConnectCallback cb) {
56 RequestConnectMessage m = new RequestConnectMessage();
57 m.peer = target;
58 m.reserved = 0;
59 final Envelope ev = new Envelope(m);
60 ev.notifySent(new NotifySentHandler() {
61 @Override
62 public void onSent() {
63 cb.onDone();
64 }
65 });
66 client.send(ev);
67
68 return new Cancelable() {
69 @Override
70 public void cancel() {
71 ev.cancel();
72 }
73 };
74 }
75
76 /**
77 * ... (discuss first)
78 */
79 public void init(Object receiveCallback, Object notifyConnect, Object notifyDisconnect) {
80
81 }
82
83
84 /**
85 * Obtain the HELLO message for this peer.
86 *
87 * @param rec function to call with the HELLO, sender will be our peer
88 * identity; message and sender will be NULL on timeout
89 * (handshake with transport service pending/failed).
90 * cost estimate will be 0.
91 * @return handle to cancel the operation
92 */
93 Cancelable getHello(HelloUpdateCallback rec) {
94 throw new UnsupportedOperationException();
95 }
96
97 /**
98 * Offer the transport service the HELLO of another peer. Note that
99 * the transport service may just ignore this message if the HELLO is
100 * malformed or useless due to our local configuration.
101 *
102 * @param hello the hello message
103 * @param cont continuation to call when HELLO has been sent,
104 * tc reason GNUNET_SCHEDULER_REASON_TIMEOUT for fail
105 * tc reasong GNUNET_SCHEDULER_REASON_READ_READY for success
106 * @return a GNUNET_TRANSPORT_OfferHelloHandle handle or NULL on failure,
107 * in case of failure cont will not be called
108 */
109
110 Cancelable offerHello(HelloMessage hello,
111 Scheduler.Task cont) {
112 throw new UnsupportedOperationException();
113 }
114
115 /**
116 * Install a blacklist callback. The service will be queried for all
117 * existing connections as well as any fresh connections to check if
118 * they are permitted.
119 * The blacklist is active until the Transport handle is destroyed.
120 * When the transport handle that installed the blacklist is destroyed,
121 * all hosts that were denied in the past will automatically be
122 * whitelisted again. This is the only way to re-enable
123 * connections from peers that were previously blacklisted.
124 *
125 * @param blacklistCallback callback to invoke to check if connections are allowed
126 */
127 public void blacklist(BlacklistCallback blacklistCallback) {
128 if (this.blacklistCallback != null)
129 throw new AssertionError("there is already a blacklist");
130 if (blacklistCallback == null)
131 throw new AssertionError("blacklist callback may not be null");
132 this.blacklistCallback = blacklistCallback;
133 client.send(new BlacklistInitMessage());
134 }
135
136 /**
137 * Return all the known addresses for a specific peer or all peers.
138 * Returns continuously all address if one_shot is set to false
139 * <p/>
140 * Returns the address(es) that we are currently using for this
141 * peer. Upon completion, the 'AddressLookUpCallback' is called one more
142 * time with 'NULL' for the address and the peer. After this, the operation must no
143 * longer be explicitly canceled.
144 *
145 * @param peer peer identity to look up the addresses of, CHANGE: allow NULL for all (connected) peers
146 * @param one_shot GNUNET_YES to return the current state and then end (with NULL+NULL),
147 * GNUNET_NO to monitor the set of addresses used (continuously, must be explicitly canceled)
148 * @param timeout how long is the lookup allowed to take at most (irrelevant if one_shot is set to GNUNET_NO)
149 * @param peer_address_callback function to call with the results
150 */
151 Cancelable getActiveAddresses(PeerIdentity peer, int one_shot,
152 RelativeTime timeout, PeerIterateCallback peer_address_callback) {
153 throw new UnsupportedOperationException();
154 }
155}
156
diff --git a/src/main/java/org/gnunet/transport/TryConnectCallback.java b/src/main/java/org/gnunet/transport/TryConnectCallback.java
new file mode 100644
index 0000000..41e743b
--- /dev/null
+++ b/src/main/java/org/gnunet/transport/TryConnectCallback.java
@@ -0,0 +1,10 @@
1package org.gnunet.transport;
2
3/**
4 * ...
5 *
6 * @author Florian Dold
7 */
8public interface TryConnectCallback {
9 void onDone();
10}
diff --git a/src/main/java/org/gnunet/util/ATSInformation.java b/src/main/java/org/gnunet/util/ATSInformation.java
new file mode 100644
index 0000000..b93931b
--- /dev/null
+++ b/src/main/java/org/gnunet/util/ATSInformation.java
@@ -0,0 +1,37 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23
24import org.gnunet.construct.Message;
25import org.gnunet.construct.UInt32;
26
27
28/**
29 * Information related to Automatic Transport Selection.
30 */
31public class ATSInformation implements Message {
32 @UInt32
33 public long type;
34
35 @UInt32
36 public long value;
37}
diff --git a/src/main/java/org/gnunet/util/AbsoluteTime.java b/src/main/java/org/gnunet/util/AbsoluteTime.java
new file mode 100644
index 0000000..9d22d36
--- /dev/null
+++ b/src/main/java/org/gnunet/util/AbsoluteTime.java
@@ -0,0 +1,269 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23import org.slf4j.Logger;
24import org.slf4j.LoggerFactory;
25
26import java.util.Date;
27
28/**
29 * A specific point in time.
30 *
31 * @author Florian Dold
32 */
33public class AbsoluteTime implements Comparable<AbsoluteTime> {
34 private static final Logger logger = LoggerFactory
35 .getLogger(AbsoluteTime.class);
36
37 /**
38 * Constant for 'the beginning of time' in our frame.
39 */
40 public final static AbsoluteTime ZERO = new AbsoluteTime(0);
41 public final static AbsoluteTime FOREVER = new AbsoluteTime(Long.MAX_VALUE);
42
43 /**
44 * Absolute time value in microseconds.
45 */
46 private final long abs_value_us;
47
48 /**
49 * Gets the current time.
50 *
51 * @return the current time
52 */
53 public static AbsoluteTime now() {
54 return new AbsoluteTime(System.currentTimeMillis() * 1000);
55 }
56
57 public AbsoluteTime(final long abs_value_us) {
58 this.abs_value_us = abs_value_us;
59 }
60
61 /**
62 * Adds a relative time value to an absolute time.
63 *
64 * @param duration duration to add to {@literal this}
65 * @return {@literal this + duration}
66 */
67 public AbsoluteTime add(RelativeTime duration) {
68 if (abs_value_us == Long.MAX_VALUE
69 || duration.isForever()) {
70 return AbsoluteTime.FOREVER;
71 }
72 if (abs_value_us + duration.getMicroseconds() < abs_value_us) {
73 return AbsoluteTime.FOREVER;
74 }
75 return new AbsoluteTime(abs_value_us + duration.getMicroseconds());
76 }
77
78 /**
79 * Calculates the estimate time of arrival/completion for an operation.
80 *
81 * @param start
82 * when did the operation start?
83 * @param finished
84 * how much has been done?
85 * @param total
86 * how much must be done overall (same unit as for "finished")
87 * @return remaining duration for the operation, assuming it continues at
88 * the same speed
89 */
90 public static RelativeTime calculateETA(final AbsoluteTime start,
91 final long finished, final long total) {
92 if (finished >= total) {
93 return RelativeTime.ZERO;
94 }
95 if (finished == 0) {
96 return RelativeTime.FOREVER;
97 }
98 final RelativeTime dur = start.getDuration();
99 final double exp = dur.getMicroseconds() * total
100 / (double) finished;
101 return new RelativeTime((long) exp);
102 }
103
104
105 /**
106 * {@inheritDoc}
107 */
108 @Override
109 public boolean equals(Object other) {
110 return other instanceof AbsoluteTime && compareTo((AbsoluteTime) other) == 0;
111 }
112
113 /**
114 * {@inheritDoc}
115 */
116 @Override
117 public int hashCode() {
118 return (int) this.abs_value_us;
119 }
120
121 /**
122 * {@inheritDoc}
123 */
124 @Override
125 public int compareTo(AbsoluteTime other) {
126 if (this.abs_value_us < other.abs_value_us) {
127 return -1;
128 }
129 if (this.abs_value_us > other.abs_value_us) {
130 return 1;
131 }
132 return 0;
133 }
134
135 /**
136 * {@inheritDoc}
137 */
138 @Override
139 public String toString() {
140 if (this.isForever()) {
141 return "AbsoluteTime(FOREVER)";
142 }
143 return "AbsoluteTime("+this.abs_value_us +")";
144 }
145
146
147 /**
148 * Check if a deadline is due.
149 * @return true if NOW is greater than the given time, false otherwise
150 */
151 public boolean isDue() {
152 return this.abs_value_us < now().abs_value_us;
153 }
154
155 /**
156 * Does this AbsoluteTime value represent forever?
157 *
158 * @return this==FOREVER
159 */
160 public boolean isForever() {
161 return this.abs_value_us == Long.MAX_VALUE;
162 }
163
164 /**
165 * Calculates the difference between two absolute times.
166 *
167 * @param other ...
168 * @return this - other
169 */
170 public RelativeTime getDifference(final AbsoluteTime other) {
171 if (other.abs_value_us == Long.MAX_VALUE) {
172 return RelativeTime.FOREVER;
173 }
174 return new RelativeTime(abs_value_us - other.abs_value_us);
175 }
176
177 /**
178 * Gets the duration of an operation as the difference of the current time
179 * and {@literal this}.
180 *
181 * @return this - now
182 */
183 public RelativeTime getDuration() {
184 assert abs_value_us != Long.MAX_VALUE;
185 return getDifference(AbsoluteTime.now());
186 }
187
188 /**
189 * Returns the milliseconds since some fixed point of reference.
190 *
191 * @return the absolute time in milliseconds
192 */
193 public long getMicroseconds() {
194 return abs_value_us;
195 }
196
197 /**
198 * Calculates the remaining time relative to now.
199 *
200 * @return this - now
201 */
202 public RelativeTime getRemaining() {
203 if (abs_value_us == Long.MAX_VALUE) {
204 return RelativeTime.FOREVER;
205 }
206 return getDifference(AbsoluteTime.now());
207 }
208
209 /**
210 * Returns the maximum of two time values.
211 *
212 * @param other ...
213 * @return max(this,other)
214 */
215 public AbsoluteTime max(final AbsoluteTime other) {
216 return abs_value_us >= other.abs_value_us ? this : other;
217
218 }
219
220 /**
221 * Returns the minimum of two time values.
222 *
223 * @param other ...
224 * @return min(this,other)
225 */
226 public AbsoluteTime min(final AbsoluteTime other) {
227 return abs_value_us <= other.abs_value_us ? this : other;
228 }
229
230 /**
231 * Subtracts a relative time value to an absolute time
232 *
233 * @param duration ...
234 * @return this - duration
235 */
236 public AbsoluteTime subtract(final RelativeTime duration) {
237 if (abs_value_us <= duration.getMicroseconds()) {
238 return AbsoluteTime.ZERO;
239 }
240 if (abs_value_us == Long.MAX_VALUE) {
241 return this;
242 }
243 return new AbsoluteTime(abs_value_us - duration.getMicroseconds());
244 }
245
246 /**
247 * Get a serializable message corresponding to this AbsoluteTime.
248 *
249 * @return a serializable message corresponding to this AbsoluteTime
250 */
251 public AbsoluteTimeMessage asMessage() {
252 return new AbsoluteTimeMessage(this);
253 }
254
255 /**
256 * Get the AbsoluteTime from a AbsoluteTimeMessage.
257 *
258 * @param m serializable representation of an AbsoluteTime
259 *
260 * @return the real AbsoluteTime associated with m
261 */
262 public static AbsoluteTime fromNetwork(AbsoluteTimeMessage m) {
263 return m.value__ < 0 ? AbsoluteTime.FOREVER : new AbsoluteTime(m.value__);
264 }
265
266 public Date toDate() {
267 return new Date(abs_value_us / 1000);
268 }
269}
diff --git a/src/main/java/org/gnunet/util/AbsoluteTimeMessage.java b/src/main/java/org/gnunet/util/AbsoluteTimeMessage.java
new file mode 100644
index 0000000..f46d577
--- /dev/null
+++ b/src/main/java/org/gnunet/util/AbsoluteTimeMessage.java
@@ -0,0 +1,47 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23import org.gnunet.construct.Message;
24import org.gnunet.construct.UInt64;
25
26
27
28/**
29 * Representation of an AbsoluteTime object, to be sent over the network.
30 */
31public class AbsoluteTimeMessage implements Message {
32 @UInt64
33 public long value__;
34
35 public AbsoluteTimeMessage() {
36
37 }
38
39
40 public AbsoluteTimeMessage(final AbsoluteTime t) {
41 if (t.equals(AbsoluteTime.FOREVER)) {
42 this.value__ = -1;
43 } else {
44 this.value__ = t.getMicroseconds();
45 }
46 }
47}
diff --git a/src/main/java/org/gnunet/util/Cancelable.java b/src/main/java/org/gnunet/util/Cancelable.java
new file mode 100644
index 0000000..173f4c0
--- /dev/null
+++ b/src/main/java/org/gnunet/util/Cancelable.java
@@ -0,0 +1,28 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23/**
24 * Any asynchronous operation that can be canceled should implement this interface.
25 */
26public interface Cancelable {
27 public void cancel();
28}
diff --git a/src/main/java/org/gnunet/util/Client.java b/src/main/java/org/gnunet/util/Client.java
new file mode 100644
index 0000000..18c206b
--- /dev/null
+++ b/src/main/java/org/gnunet/util/Client.java
@@ -0,0 +1,329 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20package org.gnunet.util;
21
22import com.google.common.base.Optional;
23import org.gnunet.mq.Envelope;
24import org.gnunet.mq.MessageQueue;
25import org.gnunet.statistics.Statistics;
26import org.slf4j.Logger;
27import org.slf4j.LoggerFactory;
28
29
30/**
31 * A connection to a GNUnet service.
32 *
33 * Wraps a Connection, and is responsible for waiting until the underlying connection has been made
34 * and allows reconnects.
35 */
36public class Client extends MessageQueue {
37 private static final Logger logger = LoggerFactory
38 .getLogger(Client.class);
39
40 /**
41 * Underlying connection to the service.
42 * May be NULL if the client is currently not connected.
43 */
44 private Connection connection;
45
46 /**
47 * Host this client should be connected to.
48 */
49 private final String hostname;
50
51 /**
52 * Port of the host the client should connect to.
53 */
54 private final int port;
55
56 /**
57 * Initial value for connectBackoff.
58 */
59 private static final RelativeTime INITAL_BACKOFF = RelativeTime.MILLISECOND.multiply(5);
60
61 /**
62 * Maximum value for connectBackoff.
63 */
64 private static final RelativeTime MAX_BACKOFF = RelativeTime.SECOND.multiply(5);
65
66 /**
67 * The time to wait after an error occured while connecting.
68 * Every time an error occurs while connecting, this value is doubled until its maximum
69 * value (MAX_BACKOFF) has been reached. This strategy is called exponential backoff.
70 */
71 private RelativeTime connectBackoff = INITAL_BACKOFF;
72
73 /**
74 * True if we are waiting for the client to connect before we can ask it to do
75 * notifyTransmitReady.
76 */
77 private boolean notifyTransmitReadyDelayed;
78
79 /**
80 * When notifyTransmitReadyDelayed is true, This can be used to cancel the task
81 * waiting for the connection to be established.
82 */
83 private Cancelable delayedNotifyTransmitHandle;
84
85 /**
86 * Currently installed persistent receiver.
87 * Will receive all messages sent to the client.
88 */
89 private RunaboutMessageReceiver receiver;
90
91 private boolean receiver_active;
92
93 /**
94 * Handle to cancel the message currently submitted in the queue,
95 */
96 private Cancelable currentSubmit;
97
98 /**
99 * Create a connection to a service.
100 *
101 * @param serviceName name of the service
102 * @param cfg configuration to use
103 */
104 public Client(String serviceName, Configuration cfg) {
105 if (cfg == null) {
106 throw new AssertionError("Configuration may not be null");
107 }
108 if (!cfg.haveValue(serviceName, "PORT")) {
109 throw new Configuration.ConfigurationException(String.format("PORT of service '%s' not specified", serviceName));
110 }
111 if (!cfg.haveValue(serviceName, "HOSTNAME")) {
112 throw new Configuration.ConfigurationException(String.format("HOSTNAME of service '%s' not specified", serviceName));
113 }
114
115 // get port of this service from the configuration
116 Optional<Long> portOption = cfg.getValueNumber(serviceName, "PORT");
117 port = portOption.get().intValue();
118 // get the hostname from the configuration
119 hostname = cfg.getValueString(serviceName, "HOSTNAME").get();
120 if (hostname == null || hostname.isEmpty()) {
121 throw new Configuration.ConfigurationException(String.format("hostname of service '%s' empty", serviceName));
122 }
123 reconnect();
124 }
125
126 /**
127 * Create a connection to a service with the specified hostname and port.
128 *
129 * @param hostname hostname of the service
130 * @param port port of the service
131 */
132 public Client(String hostname, int port) {
133 this.hostname = hostname;
134 this.port = port;
135 reconnect();
136 }
137
138
139 /**
140 * Receive one message from the service. Can only be called after sending a message to the server.
141 *
142 * @param timeout deadline after which MessageReceiver.deadline will be called
143 * @param receiver MessageReceiver that is responsible for the received message
144 */
145 public Cancelable receiveOne(RelativeTime timeout, MessageReceiver receiver) {
146 return connection.receive(timeout, receiver);
147 }
148
149 /**
150 * Ask the client to call us once it is able to send a message.
151 *
152 *
153 * @param timeout after how long should we give up (and call transmitter.transmit(null))
154 * @param autoRetry if the connection to the service dies, should we
155 * automatically reconnect and retry (within the deadline period)
156 * or should we immediately fail in this case? Pass true
157 * if the caller does not care about temporary connection errors,
158 * for example because the protocol is stateless
159 * @param size size of the message we want to transmit, can be an upper bound
160 * @param transmitter the MessageTransmitter object to call once the client is ready to transmit or
161 * 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
162 *
163 * @return a handle to cancel the notification
164 */
165 public Cancelable notifyTransmitReady(final RelativeTime timeout,
166 final boolean autoRetry, int size, final MessageTransmitter transmitter) {
167 if (notifyTransmitReadyDelayed) {
168 throw new AssertionError("notifyTransmitReady called twice!");
169 }
170 if (connection == null) {
171 throw new AssertionError("notifyTransmitReady called on disconnected client");
172 }
173 if (connection.isConnected()) {
174 return connection.notifyTransmitReady(0, timeout, transmitter);
175 } else {
176 notifyTransmitReadyDelayed = true;
177 final AbsoluteTime deadline = timeout.toAbsolute();
178 delayedNotifyTransmitHandle = connection.notifyConnected(connectBackoff, new Continuation() {
179 @Override
180 public void cont(boolean success) {
181 delayedNotifyTransmitHandle = null;
182 if (success) {
183 activateReceiver();
184 notifyTransmitReadyDelayed = false;
185 delayedNotifyTransmitHandle = connection.notifyTransmitReady(0, timeout, new MessageTransmitter() {
186 @Override
187 public void transmit(Connection.MessageSink sink) {
188 delayedNotifyTransmitHandle = null;
189 transmitter.transmit(sink);
190 }
191
192 @Override
193 public void handleError() {
194 delayedNotifyTransmitHandle = null;
195 transmitter.handleError();
196 }
197 });
198 } else {
199 logger.debug("connect timed out, trying again");
200 if (deadline.isDue()) {
201 transmitter.handleError();
202 } else {
203 RelativeTime timeout = deadline.getRemaining();
204 connectBackoff = RelativeTime.min(timeout, RelativeTime.min(connectBackoff.multiply(2), MAX_BACKOFF));
205 reconnect();
206 delayedNotifyTransmitHandle = connection.notifyConnected(connectBackoff, this);
207 }
208 }
209 }
210 });
211 return new Cancelable() {
212 @Override
213 public void cancel() {
214 if (delayedNotifyTransmitHandle != null) {
215 delayedNotifyTransmitHandle.cancel();
216 }
217 }
218 };
219 }
220 }
221
222 /**
223 * Convenience method for sending messages.
224 *
225 * @param timeout when should we give up sending the message, and call cont.cont(false)
226 * @param message the message to send
227 * @param cont called when the message has been sent successfully or on error
228 * @return a handle to cancel sending the message
229 */
230 public Cancelable transmitWhenReady(final RelativeTime timeout, final GnunetMessage.Body message, final Continuation cont) {
231 return notifyTransmitReady(timeout, false, 0, new MessageTransmitter() {
232 @Override
233 public void transmit(Connection.MessageSink sink) {
234 sink.send(message);
235 if (cont != null) {
236 cont.cont(true);
237 }
238 }
239
240 @Override
241 public void handleError() {
242 if (cont != null) {
243 cont.cont(false);
244 }
245 }
246 });
247 }
248
249 /**
250 * Convenience method for sending messages. Timeout defaults to FOREVER.
251 *
252 * @param message the message to send
253 * @param cont called when the message has been sent successfully or on error
254 * @return a handle to cancel sending the message
255 */
256 public Cancelable transmitWhenReady(final GnunetMessage.Body message, final Continuation cont) {
257 return transmitWhenReady(RelativeTime.FOREVER, message, cont);
258 }
259
260 public final void reconnect() {
261 if (connection != null) {
262 connection.disconnect();
263 }
264 connection = new Connection(hostname, port);
265 }
266
267 /**
268 * Disconnect from the service. Cancel all pending receive/transmit requests.
269 */
270 public void disconnect() {
271 if (notifyTransmitReadyDelayed) {
272 logger.error("disconnecting while notifyTransmitReady is pending");
273 }
274 connection.disconnect();
275 connection = null;
276 }
277
278 public boolean isConnected() {
279 return (connection != null) && connection.isConnected();
280 }
281
282 @Override
283 protected void submit(Envelope ev) {
284 currentSubmit = transmitWhenReady(RelativeTime.FOREVER, ev.message, new Continuation() {
285 @Override
286 public void cont(boolean success) {
287 currentSubmit = null;
288 reportMessageSent();
289 }
290 });
291 }
292
293 @Override
294 protected void retract() {
295 if (currentSubmit == null)
296 throw new AssertionError();
297 currentSubmit.cancel();
298 currentSubmit = null;
299 }
300
301 private void activateReceiver() {
302 if (receiver_active || receiver == null)
303 return;
304 final MessageReceiver proxyReceiver = new MessageReceiver() {
305 @Override
306 public void process(GnunetMessage.Body msg) {
307 Client.this.receiver.process(msg);
308 if (connection != null && connection.isConnected())
309 connection.receive(RelativeTime.FOREVER, this);
310 else
311 receiver_active = false;
312 }
313
314 @Override
315 public void handleError() {
316 Client.this.receiver.handleError();
317 receiver_active = false;
318 }
319 };
320 connection.receive(RelativeTime.FOREVER, proxyReceiver);
321 receiver_active = true;
322 }
323
324 public void installReceiver(RunaboutMessageReceiver receiver) {
325 this.receiver = receiver;
326 if (connection != null && connection.isConnected())
327 activateReceiver();
328 }
329}
diff --git a/src/main/java/org/gnunet/util/Configuration.java b/src/main/java/org/gnunet/util/Configuration.java
new file mode 100644
index 0000000..ea67e1b
--- /dev/null
+++ b/src/main/java/org/gnunet/util/Configuration.java
@@ -0,0 +1,389 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23import com.google.common.base.Charsets;
24import com.google.common.base.Optional;
25import com.google.common.collect.HashBasedTable;
26import com.google.common.collect.Table;
27import com.google.common.io.Files;
28import org.slf4j.Logger;
29import org.slf4j.LoggerFactory;
30
31import java.io.BufferedWriter;
32import java.io.File;
33import java.io.FileWriter;
34import java.io.IOException;
35import java.nio.charset.Charset;
36import java.util.*;
37import java.util.regex.Matcher;
38import java.util.regex.Pattern;
39
40/**
41 * Configuration management.
42 *
43 * @author Florian Dold
44 */
45public class Configuration {
46 public static class ParsingError extends RuntimeException {
47 ParsingError(String msg) {
48 super(msg);
49 }
50
51 ParsingError(String msg, final Throwable t) {
52 super(msg, t);
53 }
54 }
55
56 private static final Logger logger = LoggerFactory
57 .getLogger(Configuration.class);
58
59 private static Pattern section = Pattern.compile("\\[(.*?)\\]");
60 private static Pattern tag = Pattern.compile("\\s*(\\S+?)\\s*=(.*?)");
61 private static Pattern whitspace = Pattern.compile("\\s*");
62
63 // rows are sections, colums are options
64 private final Table<String, String, String> sections = HashBasedTable.create();
65
66 private final Map<String, Set<String>> sectionSources = new HashMap<String, Set<String>>(20);
67
68 /**
69 * Start with an empty configuration.
70 */
71 public Configuration() {
72 }
73
74
75 /**
76 * Expand an expression of the form "$FOO/BAR" to "DIRECTORY/BAR" where
77 * either in the "PATHS" section or the environment "FOO" is set to
78 * "DIRECTORY".
79 *
80 * @param orig string to $-expand
81 * @return $-expanded string
82 */
83 public String expandDollar(String orig) {
84 Map<String, String> env = System.getenv();
85 for (final Map.Entry<String, String> e : env.entrySet()) {
86 orig = orig.replace("$" + e.getKey(), e.getValue());
87 }
88
89 for (final Map.Entry<String, String> e : sections.row("PATHS").entrySet()) {
90 orig = orig.replace("$" + e.getKey(), e.getValue());
91 }
92 return orig;
93 }
94
95 /**
96 * Returns all configuration options in a section.
97 *
98 * @param s the section of interest
99 * @return an unmodifiable view of the section.
100 */
101 public Map<String, String> getSection(String s) {
102 Map<String, String> m = sections.row(s);
103 return Collections.unmodifiableMap(m);
104 }
105
106 /**
107 * Returns the names of all non-empty sections
108 *
109 * @return set of non-empty section names
110 */
111 public Set<String> getSections() {
112 return sections.rowKeySet();
113 }
114
115 /**
116 * Get a configuration value that should be in a set of predefined strings
117 *
118 * @param section section of interest
119 * @param option option of interest
120 * @param choices list of legal values
121 * @return matching value from choices
122 */
123 public Optional<String> getValueChoice(String section, String option,
124 Iterable<String> choices) {
125 Optional<String> value = getValueString(section, option);
126 if (!value.isPresent()) {
127 return value;
128 }
129 for (String c : choices) {
130 if (c.equals(value.get())) {
131 return value;
132 }
133 }
134 logger.error("Failure in configuration section {}: invalid value", section);
135 return Optional.absent();
136 }
137
138
139 /**
140 * Get a configuration value that should be a number
141 *
142 * @param section section of interest
143 * @param option option of interest
144 * @return null if value not in configuration, the option's value otherwise
145 */
146 public Optional<Long> getValueNumber(String section, String option) {
147 Optional<String> num_str = getValueString(section, option);
148 if (!num_str.isPresent()) {
149 logSectionSources(section);
150 return Optional.absent();
151 }
152 try {
153 return Optional.of(Long.parseLong(num_str.get()));
154 } catch (NumberFormatException e) {
155 logger.error("Failure in configuration section "
156 + section + " option " + option + ": " + e.getMessage(), e);
157 return Optional.absent();
158 }
159 }
160
161 private void logSectionSources(String section) {
162 Set<String> sources = sectionSources.get(section);
163 if (sources == null) {
164 logger.info("No sources for section '{}'", section);
165 } else {
166 logger.info("Sources for section '{}': {}", section, sources);
167 }
168 }
169
170 /**
171 * Set an option to a string value in a section.
172 *
173 * @param section section of interest
174 * @param option option of interest
175 * @return value
176 */
177 public Optional<String> getValueString(String section, String option) {
178 if (haveValue(section, option)) {
179 return Optional.of(sections.get(section, option));
180 }
181 return Optional.absent();
182 }
183
184 /**
185 * Gets a configuration value that should be in a set of {"YES","NO"}.
186 *
187 * @param section section of interest
188 * @param option option of interest
189 * @return true, false, null
190 */
191 public Optional<Boolean> getValueYesNo(String section, String option) {
192 final Optional<String> v = getValueChoice(section, option,
193 Arrays.asList("YES", "NO"));
194 if (!v.isPresent()) {
195 Set<String> sources = sectionSources.get(section);
196 if (sources == null) {
197 logger.info("No sources for section '{}'", section);
198 } else {
199 logger.info("Sources for section '{}': {}", section, sources);
200 }
201 logger.error(String.format(
202 "Failure in configuration section '%s': option '%s' not found",
203 section, option));
204 return Optional.absent();
205 }
206 if (v.get().equalsIgnoreCase("YES")) {
207 return Optional.of(true);
208 }
209 if (v.get().equalsIgnoreCase("NO")) {
210 return Optional.of(false);
211 }
212
213 logger.error(String.format("Configuration error: section '%s', option '%s' not recognized as YES or NO", section, option));
214
215 return Optional.absent();
216 }
217
218 /**
219 * Tests if we have a value for a particular option.
220 *
221 * @param section section of interest
222 * @param option option of interest
223 * @return true if so, false of not
224 */
225 public boolean haveValue(String section, String option) {
226 return sections.contains(section, option);
227 }
228
229 /**
230 * Parse a configuration file, add all of the options in the file to the
231 * configuration environment.
232 *
233 * @param filename name of the configuration file
234 */
235 public void parse(String filename) {
236 filename = replaceHome(filename);
237
238 String current_section = "";
239
240 Iterator<String> it;
241
242 try {
243 List<String> lines = Files.readLines(new File(filename), Charset.defaultCharset());
244 it = lines.iterator();
245 } catch (IOException e) {
246 throw new ParsingError("Cannot read configuration file '" + filename + "'");
247 }
248
249 int lineNumer = 1;
250
251 while (it.hasNext()) {
252 String line = it.next();
253 String[] split_line = line.split("#");
254 if (split_line.length == 0)
255 continue;
256
257 // strip comment
258 line = split_line[0];
259 Matcher m;
260
261 if ((m = tag.matcher(line)).matches()) {
262 String option = m.group(1).trim();
263 String value = m.group(2).trim();
264
265 if (value.length() != 0 && value.charAt(0) == '"') {
266 int pos = value.indexOf('"', 1);
267 if (pos == -1) {
268 logger.warn("incorrecly quoted config value");
269 continue;
270 }
271 value = value.substring(1, pos);
272 }
273 setValueString(current_section, option, value);
274 } else if ((m = section.matcher(line)).matches()) {
275 current_section = m.group(1).trim();
276 if (sectionSources.containsKey(current_section)) {
277 sectionSources.get(current_section).add(filename);
278 } else {
279 sectionSources.put(current_section, new HashSet<String>(Collections.singleton(filename)));
280 }
281 } else if (whitspace.matcher(line).matches()) {
282 // whitespace is ok
283 } else {
284 logger.warn(String.format("skipped unreadable line %s in configuration file '%s': '%s'", lineNumer,
285 filename, line));
286 }
287
288 lineNumer++;
289 }
290 }
291
292 private String replaceHome(String filename) {
293 String home = System.getenv("HOME");
294 return home != null ? filename.replace("~", home) : filename;
295 }
296
297 /**
298 * Remove the given section and all options in it.
299 */
300 public void removeSection(String section) {
301 sections.row(section).clear();
302 }
303
304 /**
305 * Set an option to a string value in a section.
306 *
307 * @param section section of interest
308 * @param option option of interest
309 * @param value value to set
310 */
311 public void setValueNumber(String section, String option,
312 long value) {
313 setValueString(section, option, "" + value);
314 }
315
316 /**
317 * Set an option to a string value in a section.
318 *
319 * @param section section of interest
320 * @param option option of interest
321 * @param value value to set
322 */
323 public void setValueString(String section, String option,
324 String value) {
325 sections.put(section, option, value);
326 }
327
328 /**
329 * Write configuration file.
330 *
331 * @param filename where to write the configuration
332 */
333 public void write(String filename) throws IOException {
334 BufferedWriter w = Files.newWriter(new File(filename), Charsets.UTF_8);
335 try {
336 for (String section : sections.rowKeySet()) {
337 w.write("["+section+"]");
338 w.newLine();
339 for (Map.Entry<String,String> e : sections.row(section).entrySet()) {
340 w.write(e.getKey() + " = " + e.getValue());
341 w.newLine();
342 }
343 }
344 } finally {
345 w.close();
346 }
347 }
348
349 public String serialize() {
350 StringBuffer buf = new StringBuffer();
351 for (Map.Entry<String, Map<String,String>> section : sections.rowMap().entrySet()) {
352 buf.append("[" + section.getKey() + "]\n");
353 for (Map.Entry<String, String> option : section.getValue().entrySet()) {
354 buf.append(option.getKey() + " = " + option.getValue() + "\n");
355 }
356 }
357 return buf.toString();
358 }
359
360
361 public void loadDefaults() {
362 Collection<File> dirs = new ArrayList<File>(5);
363 dirs.add(new File("/usr/share/gnunet/config.d/"));
364 dirs.add(new File("/usr/local/share/gnunet/config.d/"));
365 String pfx = System.getenv("GNUNET_PREFIX");
366 if (pfx != null) {
367 dirs.add(new File(pfx, "share/gnunet/config.d/"));
368 dirs.add(new File(pfx, "config.d/"));
369 dirs.add(new File(pfx, "gnunet/config.d/"));
370 }
371 for (File dir : dirs) {
372 if (dir.exists() && dir.isDirectory()) {
373 File[] files = dir.listFiles();
374 if (files == null) {
375 continue;
376 }
377 for (File f : files) {
378 parse(f.getAbsolutePath());
379 }
380 }
381 }
382 }
383
384 public static class ConfigurationException extends RuntimeException {
385 public ConfigurationException(String string) {
386 super(string);
387 }
388 }
389}
diff --git a/src/main/java/org/gnunet/util/Connection.java b/src/main/java/org/gnunet/util/Connection.java
new file mode 100644
index 0000000..1fd1458
--- /dev/null
+++ b/src/main/java/org/gnunet/util/Connection.java
@@ -0,0 +1,696 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23import org.gnunet.construct.Construct;
24import org.gnunet.construct.MessageLoader;
25import org.gnunet.construct.ProtocolViolationException;
26import org.slf4j.Logger;
27import org.slf4j.LoggerFactory;
28
29import java.io.IOError;
30import java.io.IOException;
31import java.net.InetAddress;
32import java.net.InetSocketAddress;
33import java.nio.ByteBuffer;
34import java.nio.channels.SocketChannel;
35import java.nio.channels.spi.SelectorProvider;
36import java.util.LinkedList;
37import java.util.List;
38
39/**
40 * Integrates sockets with the gnunet-java message loop / the scheduler.
41 */
42public class Connection {
43 private static final Logger logger = LoggerFactory
44 .getLogger(Connection.class);
45
46 /**
47 * The underlying socket the client is using to talk with the service.
48 */
49 private SocketChannel connectionChannel = null;
50
51 /**
52 * The list of all address probes.
53 * Each address probe tries to connect via a different address.
54 */
55 private List<AddressProbe> addressProbes = null;
56
57 /**
58 * The task that is currently used by the resolve mechanism.
59 */
60 private Cancelable resolveHandle = null;
61
62 /**
63 * The task that is responsible for establishing the connection to the server.
64 */
65 private Cancelable connectHandle = null;
66
67 /**
68 * The ReceiveHelper responsible for receiving a whole message from the service
69 * and calling the respective MessageReceiver.
70 */
71 private ReceiveHelper currentReceiveHelper = null;
72
73 /**
74 * The buffer with the (partial) message received from the service.
75 * Initially, this buffer has the size of the smallest possible messages, but grows when
76 * receiving larger messages.
77 */
78 private ByteBuffer recvBuffer = ByteBuffer.allocate(GnunetMessage.Header.SIZE);
79
80 /**
81 * The handle for the current transmission. Writes data to the socket.
82 */
83 private TransmitHelper currentTransmitHelper = null;
84
85 /**
86 * The handle for the next transmission. The next transmission will become the current
87 * transmission once the current transmission has completed.
88 * While nextTransmitHelper is not null, no new transmit requests may be scheduled.
89 */
90 private TransmitHelper nextTransmitHelper = null;
91
92 /**
93 * The transmitters passed to transmitReadyNotify(...) write to this buffer by calling
94 * methods on the MessageSink passed to the Transmitter.transmit(MessageSink s) method.
95 * Initially, this buffer has the size of the smallest possible messages, but grows when
96 * transmitting larger messages.
97 */
98 private ByteBuffer transmitBuffer = ByteBuffer.allocate(GnunetMessage.Header.SIZE);
99 private boolean disconnected = false;
100
101 /**
102 * Timeout task for the connect notify.
103 */
104 private Scheduler.TaskConfiguration notifyConnectedTimeout;
105
106 /**
107 * Continuation to call when connected
108 */
109 private Continuation notifyConnectedContinuation;
110
111
112 /**
113 * An address probe is a connection to a socket that may succeed or not.
114 * The first address probe that succeeded is used for this connection.
115 */
116 private static class AddressProbe {
117 Cancelable connectTask;
118 SocketChannel channel;
119
120 public void cancel() {
121 if (connectTask != null) {
122 connectTask.cancel();
123 }
124 if (channel != null) {
125 try {
126 channel.close();
127 } catch (IOException e) {
128 // nothing we can do here
129 }
130 }
131 }
132 }
133
134 /**
135 * Represents a request for transmission.
136 */
137 public interface TransmitHandle extends Cancelable {
138 /**
139 * Cancel a request for the transmit ready notification.
140 * This does *not* cancel a transmission that already has been started.
141 */
142 public void cancel();
143 }
144
145 /**
146 * An interface that allows the Transmitter.transmit method to deliver their messages
147 * to the client, which sends them to the service.
148 */
149 public interface MessageSink {
150 public void send(GnunetMessage.Body m);
151 }
152
153 /**
154 * The ReceiveHelper is responsible for receiving a whole
155 * GnunetMessage and call the respective MessageReceiver with the message on success,
156 * and null on failure or timeout.
157 */
158 private class ReceiveHelper implements Scheduler.Task {
159 private MessageReceiver receiver;
160 private RelativeTime timeout;
161 private GnunetMessage.Header msgh = null;
162 private Scheduler.TaskConfiguration recvTask = null;
163 private boolean finished = false;
164 // is this receiver actively working? if not, the connection process has to kick off the receiver
165 // (or select behaves badly)
166 private boolean working = false;
167
168 public ReceiveHelper(MessageReceiver receiver, RelativeTime timeout) {
169 this.receiver = receiver;
170 this.timeout = timeout;
171 }
172
173 public void dispatchMessage() {
174 assert msgh != null;
175 currentReceiveHelper = null;
176 finished = true;
177 recvBuffer.flip();
178
179 boolean found = true;
180 Class unionClass = null;
181
182 try {
183 unionClass = MessageLoader.getUnionClass(GnunetMessage.Body.class, msgh.messageType);
184 } catch (ProtocolViolationException e) {
185 found = false;
186 }
187
188 logger.debug("dispatching received message");
189 if (found) {
190 GnunetMessage msg;
191 try {
192 msg = Construct.parseAs(recvBuffer, GnunetMessage.class);
193 } catch (OutOfMemoryError e) {
194 throw new OutOfMemoryError("oom while parsing " + unionClass);
195 }
196 receiver.process(msg.body);
197 } else {
198 UnknownMessageBody b = new UnknownMessageBody();
199 b.id = msgh.messageType;
200
201 // may throw exception, doesn't matter as it's the last call
202 receiver.process(b);
203 }
204 }
205
206 @Override
207 public void run(Scheduler.RunContext ctx) {
208 recvTask = null;
209 if (ctx.reasons.contains(Scheduler.Reason.TIMEOUT)) {
210 currentReceiveHelper = null;
211 receiver.handleError();
212 } else if (ctx.reasons.contains(Scheduler.Reason.READ_READY)) {
213 try {
214 int n = connectionChannel.read(recvBuffer);
215 if (n == -1) {
216 currentReceiveHelper = null;
217 logger.warn("lost connection to service");
218 connectionChannel.close();
219 connectionChannel = null;
220 if (Connection.this.currentTransmitHelper != null) {
221 Connection.this.currentTransmitHelper.cancel();
222 Connection.this.currentTransmitHelper = null;
223 }
224 try {
225 receiver.handleError();
226 } finally {
227 return;
228 }
229 }
230 logger.debug(String.format("read %s bytes from %s", n, connectionChannel.socket().toString()));
231 } catch (IOException e) {
232 logger.error("read failed:", e);
233 try {
234 receiver.handleError();
235 } finally {
236 return;
237 }
238 }
239 if (recvBuffer.remaining() == 0) {
240 if (msgh != null) {
241 dispatchMessage();
242 } else {
243 recvBuffer.rewind();
244 msgh = Construct.parseAs(recvBuffer, GnunetMessage.Header.class);
245
246 logger.debug("expecting message of size {}, type {}", msgh.messageSize, msgh.messageType);
247 if (msgh.messageSize > GnunetMessage.Header.SIZE) {
248 if (recvBuffer.capacity() < msgh.messageSize) {
249 ByteBuffer buf = ByteBuffer.allocate(msgh.messageSize);
250 recvBuffer.flip();
251 buf.put(recvBuffer);
252 recvBuffer = buf;
253 }
254 recvBuffer.limit(msgh.messageSize);
255 schedule();
256 } else {
257 dispatchMessage();
258 }
259 }
260 } else {
261 schedule();
262 }
263 } else if (ctx.reasons.contains(Scheduler.Reason.SHUTDOWN)) {
264 // nothing to do!
265 } else {
266 // XXX: what to do here?
267 throw new RuntimeException("receive failed");
268 }
269 }
270
271 private void schedule() {
272 working = true;
273 recvTask = Scheduler.addRead(timeout, connectionChannel, this);
274 }
275
276 public void cancel() {
277 if (finished) {
278 throw new AssertionError("canceling finished receive");
279 }
280 if (recvTask != null) {
281 recvTask.cancel();
282 recvTask = null;
283 }
284 }
285 }
286
287
288 private class TransmitHelper implements Scheduler.Task, MessageSink {
289 private final MessageTransmitter transmitter;
290
291 private Cancelable notifyTimeoutTask;
292
293 private Cancelable transmitTask = null;
294
295 public TransmitHelper(final MessageTransmitter transmitter, RelativeTime notifyTimeout) {
296 this.transmitter = transmitter;
297
298 Scheduler.TaskConfiguration tc = new Scheduler.TaskConfiguration(notifyTimeout,
299 new Scheduler.Task() {
300 @Override
301 public void run(Scheduler.RunContext ctx) {
302 transmitter.handleError();
303 }
304 });
305
306 notifyTimeoutTask = tc.schedule();
307 }
308
309 public void cancel() {
310 if (transmitTask != null) {
311 transmitTask.cancel();
312 transmitTask = null;
313 }
314 if (notifyTimeoutTask != null) {
315 notifyTimeoutTask.cancel();
316 notifyTimeoutTask = null;
317 }
318 }
319
320 @Override
321 public void run(Scheduler.RunContext ctx) {
322 this.transmitTask = null;
323 if (connectionChannel == null) {
324 logger.error("could not write to channel (null)");
325 return;
326 }
327 try {
328 int n = connectionChannel.write(transmitBuffer);
329 // logger.debug("connectionChannel has written " + n + " bytes to " + connectionChannel.socket().toString());
330 } catch (IOException e) {
331 throw new IOError(e);
332 }
333 if (transmitBuffer.remaining() == 0) {
334 //logger.debug("sent " + transmitBuffer.position() + "bytes complete message");
335 if (nextTransmitHelper == null) {
336 currentTransmitHelper = null;
337 } else {
338 currentTransmitHelper = nextTransmitHelper;
339 // we need to to this so the transmit callback can do notifyTransmitReady
340 TransmitHelper tmpTransmitHelper = nextTransmitHelper;
341 nextTransmitHelper = null;
342 tmpTransmitHelper.start();
343
344 }
345 } else {
346 schedule();
347 }
348 }
349
350 /**
351 * called to notify when we are ready to put new messages in the transmit buffer
352 */
353 public void start() {
354 notifyTimeoutTask.cancel();
355 notifyTimeoutTask = null;
356 transmitBuffer.clear();
357 transmitter.transmit(TransmitHelper.this);
358 transmitBuffer.flip();
359 schedule();
360 }
361
362 private void schedule() {
363 if (disconnected) {
364 return;
365 }
366 // timeout is forever, because there is no way to directly limit the transmission time
367 // of a message, only the max. wait time before transmission.
368 // cancel must be called on the transmitTask if we disconnect
369 Scheduler.TaskConfiguration tc = new Scheduler.TaskConfiguration(RelativeTime.FOREVER, this);
370 tc.selectWrite(connectionChannel);
371 this.transmitTask = tc.schedule();
372 }
373
374 @Override
375 public void send(final GnunetMessage.Body m) {
376 final GnunetMessage gm = new GnunetMessage();
377 gm.header = new GnunetMessage.Header();
378 gm.body = m;
379 Construct.patch(gm);
380 gm.header.messageSize = Construct.getSize(gm);
381 byte[] b = Construct.toBinary(gm);
382 if (b.length != gm.header.messageSize) {
383 throw new AssertionError(
384 String.format("tried to send message with binary size %s but size in header %s",
385 b.length, gm.header.messageSize));
386 }
387 logger.debug("sending message (size={},type={})", b.length, gm.header.messageType);
388 if (transmitBuffer.remaining() < b.length) {
389 ByteBuffer buf = ByteBuffer.allocate(b.length + transmitBuffer.capacity());
390 transmitBuffer.flip();
391 buf.put(transmitBuffer);
392 transmitBuffer = buf;
393 }
394 transmitBuffer.put(b);
395 }
396 }
397
398 /**
399 * Create a connection to the given hostname/port.
400 *
401 * @param hostname name of the host to connect to
402 * @param port port of the host to connect to
403 */
404 public Connection(String hostname, int port) {
405 addressProbes = new LinkedList<AddressProbe>();
406 ConnectionResolveHandler addressHandler = new ConnectionResolveHandler(port);
407 resolveHandle = Resolver.getInstance().resolveHostname(hostname, RelativeTime.FOREVER, addressHandler);
408 }
409
410 public Connection(SocketChannel sock) {
411 assert sock != null;
412 this.connectionChannel = sock;
413 }
414
415
416 class ConnectionResolveHandler implements Resolver.AddressCallback {
417 private final int port;
418
419 public ConnectionResolveHandler(int port) {
420 this.port = port;
421 }
422
423 @Override
424 public void onAddress(InetAddress addr) {
425 final SocketChannel channel = createChannel();
426 try {
427 channel.connect(new InetSocketAddress(addr, port));
428 } catch (IOException e) {
429 logger.error("could not connect to host");
430 return;
431 }
432
433 final AddressProbe addressProbe = new AddressProbe();
434 addressProbe.channel = channel;
435 Scheduler.TaskConfiguration tc = new Scheduler.TaskConfiguration(RelativeTime.FOREVER,
436 new Scheduler.Task() {
437 @Override
438 public void run(Scheduler.RunContext ctx) {
439 addressProbe.connectTask = null;
440 if (ctx.reasons.contains(Scheduler.Reason.SHUTDOWN)) {
441 return;
442 }
443 Connection.this.finishConnect(addressProbe);
444 }
445 });
446
447 // our channel has already disconnected
448 if (!channel.isOpen()) {
449 return;
450 }
451
452 tc.selectConnect(channel);
453
454 addressProbe.connectTask = tc.schedule();
455 }
456
457 @Override
458 public void onFinished() {
459 resolveHandle = null;
460 }
461
462 @Override
463 public void onTimeout() {
464 // do nothing
465 // todo: is this correct?
466 }
467 }
468
469
470 private void finishConnect(AddressProbe probe) {
471 // can happen if the addres probe task was already scheduled
472 if (connectionChannel != null) {
473 try {
474 probe.channel.close();
475 } catch (IOException e) {
476 logger.error("could not close channel", e);
477 }
478 return;
479 }
480
481 SocketChannel channel = probe.channel;
482 boolean connected;
483 try {
484 connected = channel.finishConnect();
485 } catch (IOException e) {
486 logger.debug("finishConnect() was not successful: {}", (Object) e);
487 return;
488 }
489
490 if (!connected) {
491 logger.error("socket reported OP_CONNECT but is not connected");
492 return;
493 }
494
495 for (AddressProbe addressProbe : addressProbes) {
496 if (addressProbe != probe && addressProbe.connectTask != null) {
497 addressProbe.connectTask.cancel();
498 try {
499 addressProbe.channel.close();
500 } catch (IOException e) {
501 logger.error("could not close channel", e);
502 }
503 }
504 }
505
506 addressProbes.clear();
507
508 connectionChannel = channel;
509
510 if (currentTransmitHelper != null) {
511 currentTransmitHelper.start();
512 }
513 if (currentReceiveHelper != null && !currentReceiveHelper.working) {
514 currentReceiveHelper.schedule();
515 }
516 Continuation c = notifyConnectedContinuation;
517 notifyConnectedContinuation = null;
518 if (notifyConnectedTimeout != null) {
519 notifyConnectedTimeout.cancel();
520 notifyConnectedTimeout = null;
521 }
522 if (c != null) {
523 c.cont(true);
524 }
525 }
526
527 /**
528 * Open a channel for this connection in non-blocking mode
529 */
530 private SocketChannel createChannel() {
531 try {
532 SocketChannel channel = SelectorProvider.provider().openSocketChannel();
533 channel.configureBlocking(false);
534 return channel;
535 } catch (IOException e) {
536 // this is fatal, no retry necessary
537 throw new IOError(e);
538 }
539 }
540
541 public boolean isConnected() {
542 return connectionChannel != null && connectionChannel.isConnected();
543 }
544
545
546 public interface ReceiveHandle extends Cancelable {
547 }
548
549 /**
550 * Receive one message from the network.
551 *
552 * @param timeout deadline after which receiver.onError() will be called
553 * @param receiver MessageReceiver that is responsible for the received message
554 */
555 public ReceiveHandle receive(RelativeTime timeout, final MessageReceiver receiver) {
556 if (currentReceiveHelper != null) {
557 throw new AssertionError("receive must not be called while receiving");
558 }
559
560 if (!isConnected()) {
561 throw new AssertionError("cannot receive if not connected");
562 }
563
564 recvBuffer.clear();
565 recvBuffer.limit(GnunetMessage.Header.SIZE);
566 final ReceiveHelper rh = new ReceiveHelper(receiver, timeout);
567 currentReceiveHelper = rh;
568
569 // we can only schedule the receive helper if we are sure the connection is made, otherwise
570 // select will misbehave!
571 if (connectionChannel.isConnected()) {
572 currentReceiveHelper.schedule();
573 }
574
575 return new ReceiveHandle() {
576 @Override
577 public void cancel() {
578 rh.cancel();
579 }
580 };
581 }
582
583 /**
584 * Call the transmitter once the we are ready to transmit data.
585 *
586 * @param size number of bytes to send
587 * @param timeout after how long should we give up (and call transmitter.transmit(null))
588 * @param transmitter the MessageTransmitter object to call once the client is ready to transmit or
589 * when the timeout is over. Guaranteed to be called *after* notifyTransmitReady has returned.
590 * @return a handle that can be used to cancel the transmit request, null if request could be satisfied immediately
591 */
592 public TransmitHandle notifyTransmitReady(int size, RelativeTime timeout, final MessageTransmitter transmitter) {
593 if (disconnected) {
594 throw new AssertionError("notifyTransmitReady called on a closed connection");
595 }
596 if (nextTransmitHelper != null) {
597 throw new AssertionError(
598 "previous transmit request must have completed before calling notifyTransmitReady again");
599 }
600
601 if (timeout.getMicroseconds() <= 0) {
602 throw new AssertionError("notifyTransmitReady timeout must be positive");
603 }
604
605 if (!isConnected()) {
606 throw new AssertionError("notifyTransmitHandle can only be called once connected");
607 }
608
609 final TransmitHelper transmit = new TransmitHelper(transmitter, timeout);
610
611 if (currentTransmitHelper == null) {
612 currentTransmitHelper = transmit;
613 currentTransmitHelper.start();
614 return null;
615 }
616
617 nextTransmitHelper = transmit;
618
619 return new TransmitHandle() {
620 @Override
621 public void cancel() {
622 transmit.cancel();
623 }
624 };
625 }
626
627
628 /**
629 * Call cont after establishing the connection or when the timeout has occured.
630 *
631 * @param timeout timeout
632 * @param cont continuation to call
633 * @return
634 */
635 /* package-protected */ Cancelable notifyConnected(RelativeTime timeout, final Continuation cont) {
636 if (notifyConnectedTimeout != null) {
637 throw new AssertionError();
638 }
639 this.notifyConnectedContinuation = cont;
640 this.notifyConnectedTimeout = Scheduler.addDelayed(timeout, new Scheduler.Task() {
641 @Override
642 public void run(Scheduler.RunContext ctx) {
643 Continuation c = notifyConnectedContinuation;
644 notifyConnectedContinuation = null;
645 Connection.this.notifyConnectedTimeout = null;
646 if (c != null) {
647 c.cont(false);
648 }
649 }
650 });
651 return this.notifyConnectedTimeout;
652 }
653
654 /**
655 * Disconnect. There must not be any pending transmit/receive requests.
656 * Any buffered data scheduled for writing is discarded.
657 */
658 public void disconnect() {
659 if (disconnected) {
660 logger.error("disconnect called twice");
661 }
662 disconnected = true;
663
664 if (currentTransmitHelper != null) {
665 currentTransmitHelper.cancel();
666 currentTransmitHelper = null;
667 }
668
669 if (nextTransmitHelper != null) {
670 nextTransmitHelper.cancel();
671 nextTransmitHelper = null;
672 }
673
674 if (currentReceiveHelper != null) {
675 currentReceiveHelper.cancel();
676 currentReceiveHelper = null;
677 }
678
679 if (resolveHandle != null) {
680 resolveHandle.cancel();
681 resolveHandle = null;
682 }
683 if (connectHandle != null) {
684 connectHandle.cancel();
685 connectHandle = null;
686 }
687 if (connectionChannel != null) {
688 try {
689 connectionChannel.close();
690 } catch (IOException e) {
691 throw new IOError(e);
692 }
693 connectionChannel = null;
694 }
695 }
696}
diff --git a/src/main/java/org/gnunet/util/Continuation.java b/src/main/java/org/gnunet/util/Continuation.java
new file mode 100644
index 0000000..e1027c0
--- /dev/null
+++ b/src/main/java/org/gnunet/util/Continuation.java
@@ -0,0 +1,25 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23public interface Continuation {
24 void cont(boolean success);
25}
diff --git a/src/main/java/org/gnunet/util/GnunetMessage.java b/src/main/java/org/gnunet/util/GnunetMessage.java
new file mode 100644
index 0000000..89c7a80
--- /dev/null
+++ b/src/main/java/org/gnunet/util/GnunetMessage.java
@@ -0,0 +1,81 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23
24import org.gnunet.construct.*;
25
26
27/**
28 * Every message used to communicate between gnunet components uses this format.
29 * First, a header is sent, containing the size of the overall message (including the header), as
30 * well as the type of the message. After that the message body is sent, whose format is specified
31 * by the message type.
32 *
33 */
34public final class GnunetMessage implements Message {
35 public static final int MINIMAL_SIZE = Header.SIZE;
36
37
38 /**
39 * The header of every gnunet message.
40 */
41 public static final class Header implements Message {
42 public static final int SIZE = 4;
43
44 @FrameSize
45 @UInt16
46 public int messageSize;
47
48 @UInt16
49 public int messageType;
50 }
51
52 /**
53 * The common interface for every message body.
54 *
55 */
56 public static interface Body extends MessageUnion {
57 }
58
59
60 /**
61 * Create a GnunetMessage from its body only. The header is added and filled with the relevant information
62 * automatically.
63 *
64 * @param b the message body to convert
65 * @return a complete and valid gnunet message
66 */
67 public static GnunetMessage fromBody(Body b) {
68 GnunetMessage msg = new GnunetMessage();
69 msg.header = new Header();
70 msg.header.messageSize = Header.SIZE + Construct.getSize(b);
71 msg.header.messageType = MessageLoader.getUnionTag(GnunetMessage.Body.class, b.getClass());
72 msg.body = b;
73 return msg;
74 }
75
76 @NestedMessage
77 public Header header;
78
79 @Union(tag = "header.messageType")
80 public Body body;
81}
diff --git a/src/main/java/org/gnunet/util/HashCode.java b/src/main/java/org/gnunet/util/HashCode.java
new file mode 100644
index 0000000..0c2790f
--- /dev/null
+++ b/src/main/java/org/gnunet/util/HashCode.java
@@ -0,0 +1,94 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23
24import com.google.common.base.Charsets;
25import org.gnunet.construct.FixedSizeIntegerArray;
26import org.gnunet.construct.Message;
27
28import java.security.MessageDigest;
29import java.security.NoSuchAlgorithmException;
30import java.util.Arrays;
31
32
33/**
34 * 512-bit hash code
35 */
36public class HashCode implements Message {
37
38 @FixedSizeIntegerArray(length = 64, signed = false, bitSize = 8)
39 public byte[] data; // should be immutable, final, can't be due to construct
40
41
42 public HashCode() {
43 // construct needs a default c'tor
44 }
45
46 public HashCode(byte[] hash) {
47 if (hash.length != 64) {
48 throw new AssertionError("HashCode has to have length 64");
49 }
50 data = Arrays.copyOf(hash, hash.length);
51 }
52
53 /**
54 * Create the HashCode of an UTF-8 String using SHA-512.
55 *
56 * @param s the string to hash
57 */
58 public HashCode(String s) {
59 MessageDigest digest;
60 try {
61 digest = MessageDigest.getInstance("SHA-512");
62 } catch (NoSuchAlgorithmException e) {
63 throw new RuntimeException("crypto algorithm required but not provided");
64 }
65 byte[] data = digest.digest(s.getBytes(Charsets.UTF_8));
66 if (data.length != 64) {
67 throw new RuntimeException("error in SHA512 algorithm");
68 }
69 this.data = data;
70 }
71
72 public boolean isAllZero() {
73 for (byte aData : data) {
74 if (aData != 0) {
75 return false;
76 }
77 }
78 return true;
79 }
80
81 @Override
82 public boolean equals(Object other) {
83 if (!(other instanceof HashCode)) {
84 return false;
85 }
86 HashCode hashCode = (HashCode) other;
87 return Arrays.equals(this.data, hashCode.data);
88 }
89
90 @Override
91 public int hashCode() {
92 return Arrays.hashCode(this.data);
93 }
94} \ No newline at end of file
diff --git a/src/main/java/org/gnunet/util/Helper.java b/src/main/java/org/gnunet/util/Helper.java
new file mode 100644
index 0000000..e8c1723
--- /dev/null
+++ b/src/main/java/org/gnunet/util/Helper.java
@@ -0,0 +1,208 @@
1package org.gnunet.util;
2
3import org.gnunet.construct.Construct;
4import org.gnunet.construct.Message;
5import org.gnunet.construct.MessageLoader;
6import org.gnunet.construct.ProtocolViolationException;
7import org.gnunet.mq.Envelope;
8import org.gnunet.mq.MessageQueue;
9
10import java.io.IOError;
11import java.io.IOException;
12import java.nio.Buffer;
13import java.nio.ByteBuffer;
14import java.nio.channels.Channels;
15import java.nio.channels.ReadableByteChannel;
16import java.util.EnumSet;
17import java.util.LinkedList;
18import java.util.List;
19
20/**
21 * Process that we can communicate with standard GNUnet messages over stdin/stdout.
22 */
23public class Helper extends MessageQueue {
24
25 private final ProcessBuilder processBuilder;
26 private final RunaboutMessageReceiver receiver;
27 private Process process;
28
29 private volatile GnunetMessage.Body writeMessage;
30
31 private final class WriteThread implements Runnable {
32 @Override
33 public void run() {
34 GnunetMessage.Body msg;
35 while (true) {
36 synchronized (Helper.this) {
37 while (writeMessage == null) {
38 try {
39 wait();
40 } catch (InterruptedException e) {
41 // do nothing
42 }
43 }
44 // we now have a message we can send
45 msg = writeMessage;
46 writeMessage = null;
47 // somebody can set the next send message
48 }
49 byte[] data = Construct.toBinary(GnunetMessage.fromBody(msg));
50 try {
51 process.getOutputStream().write(data);
52 } catch (IOException e) {
53 // fixme: what now?
54 }
55 Scheduler.addContinuation(new Scheduler.Task() {
56 @Override
57 public void run(Scheduler.RunContext ctx) {
58 reportMessageSent();
59 }
60 }, EnumSet.noneOf(Scheduler.Reason.class));
61 }
62 }
63 }
64
65 private final class ReadThread implements Runnable {
66 private ByteBuffer buffer;
67 ReadableByteChannel channel;
68
69 private void fillBuffer() {
70 while (buffer.hasRemaining()) {
71 try {
72 channel.read(buffer);
73 } catch (IOException e) {
74 // FIXME
75 return;
76 }
77 }
78 }
79
80 private void scheduleInvokeReceiver(final GnunetMessage.Body body) {
81 Scheduler.addContinuation(new Scheduler.Task() {
82 @Override
83 public void run(Scheduler.RunContext ctx) {
84 receiver.process(body);
85 }
86 }, EnumSet.noneOf(Scheduler.Reason.class));
87
88 }
89
90 @Override
91 public void run() {
92 // allocate just enough for the message header
93 buffer = ByteBuffer.allocate(4);
94 channel = Channels.newChannel(process.getInputStream());
95 while (true) {
96 buffer.clear();
97 buffer.limit(4);
98 fillBuffer();
99 buffer.rewind();
100 GnunetMessage.Header msgh = Construct.parseAs(buffer, GnunetMessage.Header.class);
101 if (msgh.messageSize > GnunetMessage.Header.SIZE) {
102 if (buffer.capacity() < msgh.messageSize) {
103 ByteBuffer newBuf = ByteBuffer.allocate(msgh.messageSize);
104 buffer.flip();
105 newBuf.put(buffer);
106 buffer = newBuf;
107 }
108 buffer.limit(msgh.messageSize);
109 fillBuffer();
110 }
111 // we now have a complete message
112 // prepare for reading again
113 buffer.flip();
114
115 boolean found = true;
116 Class unionClass = null;
117
118 try {
119 unionClass = MessageLoader.getUnionClass(GnunetMessage.Body.class, msgh.messageType);
120 } catch (ProtocolViolationException e) {
121 found = false;
122 }
123 if (found) {
124 GnunetMessage msg;
125 msg = Construct.parseAs(buffer, GnunetMessage.class);
126 scheduleInvokeReceiver(msg.body);
127 } else {
128 UnknownMessageBody b = new UnknownMessageBody();
129 b.id = msgh.messageType;
130 scheduleInvokeReceiver(b);
131 }
132 }
133 }
134 }
135
136
137 public Helper(boolean withControlPipe, String binaryName, List<String> argv,
138 RunaboutMessageReceiver receiver) {
139 this.receiver = receiver;
140 List<String> command = new LinkedList<String>();
141 if (binaryName == null) {
142 throw new AssertionError();
143 }
144 command.add(binaryName);
145 if (argv != null)
146 command.addAll(argv);
147 processBuilder = new ProcessBuilder(command);
148 try {
149 process = processBuilder.start();
150 } catch (IOException e) {
151 throw new IOError(e);
152 }
153 }
154
155 /**
156 * Sends termination signal to the helper process. The helper process is not
157 * reaped; call GNUNET_HELPER_wait() for reaping the dead helper process.
158 *
159 * @param softkill if GNUNET_YES, signals termination by closing the helper's
160 * stdin; GNUNET_NO to signal termination by sending SIGTERM to helper
161 * @return true on success, false on failure
162 */
163 public boolean kill(boolean softkill) {
164 if (softkill) {
165 try {
166 process.getInputStream().close();
167 } catch (IOException e) {
168 return false;
169 }
170 return true;
171 }
172 process.destroy();
173 return true;
174 }
175
176 /**
177 * Reap the helper process. This call is blocking(!). The helper process
178 * should either be sent a termination signal before or should be dead before
179 * calling this function
180 *
181 * @return true on success, false on failure
182 */
183 public boolean waitFor() {
184 try {
185 process.waitFor();
186 } catch (InterruptedException e) {
187 return false;
188 }
189 return true;
190 }
191
192 @Override
193 protected void submit(Envelope ev) {
194 synchronized (this) {
195 if (writeMessage != null)
196 throw new AssertionError("message queue not implemented correctly");
197 writeMessage = ev.message;
198 notifyAll();
199 }
200 }
201
202 @Override
203 protected void retract() {
204 synchronized (this) {
205 writeMessage = null;
206 }
207 }
208}
diff --git a/src/main/java/org/gnunet/util/MessageReceiver.java b/src/main/java/org/gnunet/util/MessageReceiver.java
new file mode 100644
index 0000000..548695f
--- /dev/null
+++ b/src/main/java/org/gnunet/util/MessageReceiver.java
@@ -0,0 +1,41 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23/**
24 * Callback object for receiving messages.
25 *
26 */
27public interface MessageReceiver {
28
29 /**
30 * Called when a message is received
31 *
32 * @param msg message received, null on deadline or fatal error
33 */
34 public void process(GnunetMessage.Body msg);
35
36
37 /**
38 * Called when an error (timeout, loss of connection) occured before receiving the message.
39 */
40 public void handleError();
41}
diff --git a/src/main/java/org/gnunet/util/MessageTransmitter.java b/src/main/java/org/gnunet/util/MessageTransmitter.java
new file mode 100644
index 0000000..54a6555
--- /dev/null
+++ b/src/main/java/org/gnunet/util/MessageTransmitter.java
@@ -0,0 +1,43 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23
24/**
25 * Callback object for transmitting messages.
26 */
27public interface MessageTransmitter {
28 /**
29 * Called when the client is ready to transmit messages, or on timeout/error.
30 *
31 * @param sink A message sink that receives messages to be transmitted by the client,
32 * or null on timeout/error.
33 */
34 public void transmit(Connection.MessageSink sink);
35
36
37 /**
38 * Called when the transmit request could not be fullfilled.
39 *
40 * After transmit has been called, handleError will not be called anymore (until the next transmit request)
41 */
42 void handleError();
43}
diff --git a/src/main/java/org/gnunet/util/PeerIdentity.java b/src/main/java/org/gnunet/util/PeerIdentity.java
new file mode 100644
index 0000000..46a67cd
--- /dev/null
+++ b/src/main/java/org/gnunet/util/PeerIdentity.java
@@ -0,0 +1,69 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23
24import org.gnunet.construct.FixedSizeIntegerArray;
25import org.gnunet.construct.Message;
26
27import java.util.Arrays;
28
29
30/**
31 * Identity of a peer, stored as 512-bit public key.
32 */
33public class PeerIdentity implements Message {
34
35 @FixedSizeIntegerArray(length = 64, signed = false, bitSize = 8)
36 public byte[] data;
37
38 static final String HEXES = "0123456789ABCDEF";
39
40 /**
41 * Creates a zero-filled peer identity
42 */
43 public PeerIdentity() {
44 data = new byte[64];
45 }
46
47 public String getHex() {
48 final StringBuilder hex = new StringBuilder( 2 * data.length );
49 for (final byte b : data) {
50 hex.append(HEXES.charAt((b & 0xF0) >> 4))
51 .append(HEXES.charAt((b & 0x0F)));
52 }
53 return hex.toString();
54 }
55
56 public String toString() {
57 return Strings.dataToString(data);
58 }
59
60 @Override
61 public boolean equals(Object obj) {
62 return obj != null && obj instanceof PeerIdentity && Arrays.equals(((PeerIdentity) obj).data, this.data);
63 }
64
65 @Override
66 public int hashCode() {
67 return Arrays.hashCode(data);
68 }
69}
diff --git a/src/main/java/org/gnunet/util/Program.java b/src/main/java/org/gnunet/util/Program.java
new file mode 100644
index 0000000..bd413b9
--- /dev/null
+++ b/src/main/java/org/gnunet/util/Program.java
@@ -0,0 +1,229 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23import org.apache.log4j.*;
24import org.gnunet.util.getopt.Argument;
25import org.gnunet.util.getopt.ArgumentAction;
26import org.gnunet.util.getopt.Parser;
27import org.slf4j.Logger;
28import org.slf4j.LoggerFactory;
29
30import java.io.IOException;
31
32
33/**
34 * Program is the entry point class for everything that uses gnunet services or APIs.
35 *
36 * Also specifies the default command line arguments using the org.gnunet.util.getopt annotations.
37 *
38 * @see Service
39 */
40public abstract class Program {
41 private static final Logger logger = LoggerFactory
42 .getLogger(Program.class);
43
44
45 protected final Configuration cfg = new Configuration();
46
47 @Argument(shortname = "c", longname = "config",
48 description = "Path of the configuration file",
49 argumentName = "FILENAME",
50 action = ArgumentAction.STORE_STRING)
51 public String cfgFileName;
52
53 @Argument(shortname = "h", longname = "help",
54 description = "print this help message",
55 action = ArgumentAction.SET)
56 public boolean printHelp;
57
58 @Argument(shortname = "v", longname = "version",
59 description = "print version",
60 action = ArgumentAction.SET)
61 public boolean showVersion;
62
63
64 @Argument(shortname = "L", longname = "log",
65 description = "configure logging to use LOGLEVEL",
66 argumentName = "LOGLEVEL",
67 action = ArgumentAction.STORE_STRING)
68 public String logLevel;
69
70 @Argument(shortname = "l", longname = "logfile",
71 description = "configure logging to write logs to LOGFILE",
72 argumentName = "LOGFILE",
73 action = ArgumentAction.STORE_STRING)
74 public String logFile;
75
76
77 protected String[] unprocessedArgs;
78
79 private final String[] args;
80
81 private int returnValue = 0;
82
83
84 /**
85 * A program with the desired environment for a gnunet utility.
86 * While executing, the scheduler is guaranteed to run, command arguments are parsed,
87 * the default configuration is loaded and the DNS Resolver is initialized.
88 *
89 * @param args array of command line arguments to parse. used to automatically load additional settings
90 * and configure log levels.
91 */
92 public Program(String... args) {
93 this.args = args;
94
95 /*
96 * Remember: We can't parse command line arguments here, as java's initialization order
97 * dictates that member variables of subclasses are initialized *after* the superclass constructor (here).
98 */
99 }
100
101 /**
102 * Configure logging with the given log level and log file.
103 *
104 * @param logLevel one of DEBUG,INFO,WARN,ERROR,OFF
105 * @param logFile logfile, absolute or relative to the current working directory
106 */
107 public static void configureLogging(String logLevel, String logFile) {
108 org.apache.log4j.Logger rootLogger = LogManager.getRootLogger();
109
110 rootLogger.removeAllAppenders();
111
112 // %c{2}: category 2 levels
113 Layout layout = new PatternLayout("%d{dd MMM yyyy HH:mm:ss-SSS} %c{2} %p: %m%n");
114
115 if (logFile == null) {
116 rootLogger.addAppender(new ConsoleAppender(layout, ConsoleAppender.SYSTEM_OUT));
117 } else {
118 Appender appender = null;
119 try {
120 appender = new FileAppender(layout, logFile);
121 } catch (IOException e) {
122 logger.warn("could not open log file {}", logFile);
123 }
124 if (appender!= null) {
125 rootLogger.removeAllAppenders();
126 rootLogger.addAppender(appender);
127 }
128 }
129 if (logLevel == null) {
130 rootLogger.setLevel(Level.INFO);
131 } else if (logLevel.equalsIgnoreCase("debug")) {
132 rootLogger.setLevel(Level.DEBUG);
133 } else if (logLevel.equalsIgnoreCase("info")) {
134 rootLogger.setLevel(Level.INFO);
135 } else if (logLevel.equalsIgnoreCase("warn") || logLevel.equalsIgnoreCase("warning")) {
136 rootLogger.setLevel(Level.WARN);
137 } else if (logLevel.equalsIgnoreCase("error")) {
138 rootLogger.setLevel(Level.ERROR);
139 } else if (logLevel.equalsIgnoreCase("off")) {
140 rootLogger.setLevel(Level.OFF);
141 } else {
142 rootLogger.setLevel(Level.INFO);
143 logger.info("unknown log level '{}'; defaulting to INFO", logLevel);
144 }
145 }
146
147 public static void configureLogging(String logLevel) {
148 configureLogging(logLevel, null);
149 }
150
151 public static void configureLogging() {
152 configureLogging(null, null);
153 }
154
155
156 /**
157 * Override to display a different help text on "-h/--help"
158 *
159 * @return the help text
160 */
161 protected String makeHelpText() {
162 return "gnunet-java tool";
163 }
164
165 /**
166 * Override to display a different version description on "-h/--help"
167 *
168 * @return version description
169 */
170 protected String makeVersionDescription() {
171 return "development version of gnunet-java";
172 }
173
174 final protected void setReturnValue(int x) {
175 returnValue = x;
176 }
177
178 /**
179 * Start the Program as the initial task of the Scheduler.
180 */
181 public final void start() {
182 Parser optParser = new Parser(this);
183 unprocessedArgs = optParser.parse(args);
184
185 configureLogging(logLevel, logFile);
186
187 cfg.loadDefaults();
188
189 if (cfgFileName != null) {
190 cfg.parse(cfgFileName);
191 }
192
193 Resolver.getInstance().setConfiguration(cfg);
194
195 if (showVersion) {
196 System.out.println(makeVersionDescription());
197 } else if (printHelp) {
198 System.out.println(makeHelpText());
199 System.out.print(optParser.getHelp());
200 } else {
201 Scheduler.run(new Scheduler.Task() {
202 public void run(Scheduler.RunContext c) {
203 Program.this.runHook();
204 }
205 });
206 }
207
208 System.exit(returnValue);
209 }
210
211 /**
212 * Overridden by specializations of Program, like Service.
213 *
214 * Allows for start() to be final.
215 */
216 /* package-private */
217 void runHook() {
218 run();
219 }
220
221 /**
222 * Override to implement the behavior of the Program.
223 */
224 public abstract void run();
225
226 public final Configuration getConfiguration() {
227 return cfg;
228 }
229}
diff --git a/src/main/java/org/gnunet/util/RelativeTime.java b/src/main/java/org/gnunet/util/RelativeTime.java
new file mode 100644
index 0000000..512ed01
--- /dev/null
+++ b/src/main/java/org/gnunet/util/RelativeTime.java
@@ -0,0 +1,231 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23import org.slf4j.Logger;
24import org.slf4j.LoggerFactory;
25
26/**
27 * Positive amount of time with no point of reference.
28 *
29 * @author Florian Dold
30 */
31public final class RelativeTime implements Comparable<RelativeTime> {
32 private static final Logger logger = LoggerFactory
33 .getLogger(RelativeTime.class);
34
35 public static final RelativeTime MICROSECOND = new RelativeTime(1);
36 public static final RelativeTime MILLISECOND = MICROSECOND.multiply(1000);
37 public static final RelativeTime SECOND = MILLISECOND.multiply(1000);
38 public static final RelativeTime MINUTE = SECOND.multiply(60);
39 public static final RelativeTime HOUR = MINUTE.multiply(60);
40 public static final RelativeTime DAY = HOUR.multiply(24);
41 public static final RelativeTime WEEK = DAY.multiply(7);
42 public static final RelativeTime MONTH = DAY.multiply(30);
43 public static final RelativeTime YEAR = DAY.multiply(365);
44
45 public static final RelativeTime ZERO = new RelativeTime(0);
46 public static final RelativeTime FOREVER = new RelativeTime(Long.MAX_VALUE);
47
48 /**
49 * Time offset in microseconds.
50 */
51 private final long rel_value_us;
52
53 /**
54 * Create a new RelativeTime value, with a given time in milliseconds.
55 *
56 * @param abs_value time in milliseconds
57 */
58 public RelativeTime(final long abs_value) {
59 this.rel_value_us = abs_value;
60 }
61
62 public static RelativeTime fromMilliseconds(final long ms) {
63 return new RelativeTime(ms * 1000);
64 }
65
66 public static RelativeTime fromMicroseconds(final long us) {
67 return new RelativeTime(us);
68 }
69
70 /**
71 * Add relative times together.
72 *
73 * @param other
74 * the other timestamp
75 *
76 * @return this + other
77 */
78 public RelativeTime add(final RelativeTime other) {
79 if (this.rel_value_us == Long.MAX_VALUE
80 || other.rel_value_us == Long.MAX_VALUE) {
81 return RelativeTime.FOREVER;
82 }
83 final long new_rel_value = this.rel_value_us + other.rel_value_us;
84 // check for numeric overflow
85 if (new_rel_value < this.rel_value_us) {
86 logger.warn("time overflow");
87 return RelativeTime.FOREVER;
88 }
89 return new RelativeTime(new_rel_value);
90 }
91
92 /**
93 * Divide relative time by a given factor.
94 *
95 * @param factor
96 * integer to divide by
97 * @return FOREVER if this=FOREVER or factor=0; otherwise this/factor
98 */
99 public RelativeTime divide(final int factor) {
100 if (factor == 0 || this.rel_value_us == Long.MAX_VALUE) {
101 return RelativeTime.FOREVER;
102 }
103 return new RelativeTime(this.rel_value_us / factor);
104 }
105
106 /**
107 * Returns the amount of time in milliseconds.
108 *
109 * @return the amount of time in milliseconds
110 */
111 public long getMicroseconds() {
112 return rel_value_us;
113 }
114
115 /**
116 * Return the maximum of two relative time values.
117 *
118 * @return max(t1, t2)
119 */
120 public static RelativeTime max(RelativeTime t1, RelativeTime t2) {
121 return t1.rel_value_us >= t2.rel_value_us ? t1 : t2;
122 }
123
124 /**
125 * Return the minimum of two relative time values.
126 *
127 * @return min(this, other)
128 */
129 public static RelativeTime min(RelativeTime t1, RelativeTime t2) {
130 return t1.rel_value_us <= t2.rel_value_us ? t1 : t2;
131 }
132
133 /**
134 * Multiply relative time by a given factor.
135 *
136 * @return FOREVER if this=FOREVER or on overflow; otherwise this*factor
137 */
138 public RelativeTime multiply(final int factor) {
139 if (factor == 0) {
140 return RelativeTime.ZERO;
141 }
142 final long ret = this.rel_value_us * factor;
143 // check for numeric overflow
144 if (ret / factor != rel_value_us) {
145 logger.warn("time overflow");
146 return RelativeTime.FOREVER;
147 }
148 return new RelativeTime(ret);
149 }
150
151 /**
152 * Subtract relative timestamp from the other.
153 *
154 * @param other
155 * second timestamp
156 * @return ZERO if other>=this (including both FOREVER), FOREVER if
157 * this=FOREVER, this-other otherwise
158 */
159 public RelativeTime subtract(final RelativeTime other) {
160 if (this.rel_value_us >= other.rel_value_us) {
161 return RelativeTime.ZERO;
162 } else if (this.rel_value_us == Long.MAX_VALUE) {
163 return this;
164 } else {
165 return new RelativeTime(this.rel_value_us - other.rel_value_us);
166 }
167 }
168
169 /**
170 * Converts relative time to an absolute time in the future.
171 *
172 * @return timestamp that is in the future, or FOREVER if this=FOREVER (or
173 * if we would overflow)
174 */
175 public AbsoluteTime toAbsolute() {
176 return AbsoluteTime.now().add(this);
177 }
178
179 public boolean isForever() {
180 return rel_value_us == FOREVER.rel_value_us;
181 }
182
183 public boolean equals(Object o) {
184 return (o instanceof RelativeTime) && ((RelativeTime) o).rel_value_us == rel_value_us;
185 }
186
187 @Override
188 public int hashCode() {
189 return (int) this.rel_value_us;
190 }
191
192 @Override
193 public int compareTo(RelativeTime other) {
194 if (this.rel_value_us < other.rel_value_us) {
195 return -1;
196 }
197 if (this.rel_value_us > other.rel_value_us) {
198 return 1;
199 }
200 return 0;
201 }
202
203 @Override
204 public String toString() {
205 if (this.isForever()) {
206 return "RelativeTime(FOREVER)";
207 }
208 return "RelativeTime("+this.rel_value_us +")";
209 }
210
211
212
213
214 public RelativeTimeMessage toNetwork() {
215 long rval = this.rel_value_us;
216 assert rval >= 0;
217 if (rval == FOREVER.rel_value_us) {
218 rval = -1L; /* 0xFFFFFFFFFFFFFFFF for network format! */
219 }
220 return new RelativeTimeMessage(rval);
221 }
222
223 public static RelativeTime fromNetwork(RelativeTimeMessage m) {
224 if (m.value__ < 0) {
225 return RelativeTime.FOREVER;
226 } else {
227 return new RelativeTime(m.value__);
228 }
229 }
230
231}
diff --git a/src/main/java/org/gnunet/util/RelativeTimeMessage.java b/src/main/java/org/gnunet/util/RelativeTimeMessage.java
new file mode 100644
index 0000000..ac4e66c
--- /dev/null
+++ b/src/main/java/org/gnunet/util/RelativeTimeMessage.java
@@ -0,0 +1,55 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23import org.gnunet.construct.Message;
24import org.gnunet.construct.UInt64;
25
26
27/**
28 * Representation of a RelativeTime object, to be sent over the network.
29 */
30public class RelativeTimeMessage implements Message {
31
32 /**
33 * Value__ still in Java-byte order, needs to be converted to Network byte
34 * order by the Construct class.
35 */
36 @UInt64
37 public long value__;
38
39 public RelativeTimeMessage(final long value) {
40 this.value__ = value;
41 }
42
43 public RelativeTimeMessage() {
44 // default constructor needed for Construct
45 }
46
47 public RelativeTimeMessage(final RelativeTime t) {
48 if (t.equals(RelativeTime.FOREVER)) {
49 this.value__ = -1;
50 } else {
51 this.value__ = t.getMicroseconds();
52 }
53 }
54
55}
diff --git a/src/main/java/org/gnunet/util/Resolver.java b/src/main/java/org/gnunet/util/Resolver.java
new file mode 100644
index 0000000..22047aa
--- /dev/null
+++ b/src/main/java/org/gnunet/util/Resolver.java
@@ -0,0 +1,421 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23import com.google.common.net.InetAddresses;
24import org.gnunet.construct.*;
25import org.gnunet.construct.ProtocolViolationException;
26import org.gnunet.util.getopt.Argument;
27import org.gnunet.util.getopt.ArgumentAction;
28import org.slf4j.Logger;
29import org.slf4j.LoggerFactory;
30
31import java.net.InetAddress;
32import java.net.UnknownHostException;
33import java.util.LinkedList;
34
35/**
36 * Resolve hostnames asynchronously, using the gnunet resolver service if necessary.
37 * <p/>
38 * TODO: implement reverse lookup (already done in the C-API)
39 */
40public class Resolver {
41 private static final Logger logger = LoggerFactory
42 .getLogger(Resolver.class);
43
44 private static Resolver singletonInstance;
45
46 private Configuration cfg;
47
48 private Client client;
49
50 public static InetAddress getInetAddressFromString(String ipString) {
51 try {
52 return InetAddresses.forString(ipString);
53 } catch (IllegalArgumentException e) {
54 return null;
55 }
56 }
57
58 @UnionCase(4)
59 public static class GetMessage implements GnunetMessage.Body {
60 static final int DIRECTION_GET_IP = 0;
61 static final int DIRECTION_GET_NAME = 1;
62 static final int AF_UNSPEC = 0;
63 static final int AF_INET = 2;
64 static final int AF_INET6 = 10;
65
66 @UInt32
67 public int direction;
68 @UInt32
69 public int domain;
70
71 @Union(tag = "direction", optional = true)
72 public Address addr;
73 }
74
75 public interface Address extends MessageUnion {
76 }
77
78 @UnionCase(GetMessage.DIRECTION_GET_IP)
79 public static class TextualAddress implements Address {
80 @ZeroTerminatedString
81 public String addr;
82 }
83
84 @UnionCase(GetMessage.DIRECTION_GET_NAME)
85 public static class NumericAddress implements Address {
86 @FillWith @UInt8
87 public byte[] addr;
88 }
89
90
91 @UnionCase(5)
92 public static class ResolverResponse implements GnunetMessage.Body {
93 @NestedMessage(optional = true)
94 public ResponseBody responseBody;
95 }
96
97
98 public static class ResponseBody implements Message {
99 @FillWith @UInt8
100 public byte[] addr;
101 }
102
103 /**
104 * Callback object for hostname resolution.
105 */
106 public interface AddressCallback {
107 /**
108 * Called for every address the requested hostname resolves to.
109 *
110 * @param addr address for the resolved name
111 */
112 public void onAddress(InetAddress addr);
113
114 /**
115 * Called after every result (if any) has been passed to onAddress.
116 */
117 public void onFinished();
118
119 /**
120 * Called when the resolve operation times out before returning every result.
121 */
122 void onTimeout();
123 }
124
125
126 /**
127 * Configuration to use with the Resolver.
128 * <p/>
129 * Usually called by the entry points Program/Service.
130 *
131 * @param cfg configuration to use
132 */
133 public void setConfiguration(Configuration cfg) {
134 this.cfg = cfg;
135 }
136
137 private void lazyConnect() {
138 if (client == null) {
139 if (cfg == null) {
140 throw new AssertionError("Resolver has no Configuration");
141 }
142 client = new Client("resolver", cfg);
143 }
144 }
145
146
147 private InetAddress getInet4Localhost() {
148 try {
149 return InetAddress.getByAddress(new byte[]{127, 0, 0, 1});
150 } catch (UnknownHostException e) {
151 throw new RuntimeException();
152 }
153 }
154
155 private InetAddress getInet6Localhost() {
156 try {
157 return InetAddress.getByAddress(new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1});
158 } catch (UnknownHostException e) {
159 throw new RuntimeException();
160 }
161 }
162
163 public class ResolveHandle implements Cancelable {
164 private String hostname;
165 private AbsoluteTime deadline;
166 private AddressCallback cb;
167 private boolean finished = false;
168 private boolean canceled = false;
169 private Cancelable transmitTask = null;
170 private Cancelable receiveTask = null;
171
172 public void cancel() {
173 if (finished) {
174 throw new AssertionError("Resolve already finished");
175 }
176 if (canceled) {
177 throw new AssertionError("ResolveHandle canceled twice");
178 }
179 if (queuedRequests.contains(this)) {
180 queuedRequests.remove(this);
181 } else {
182 if (receiveTask != null) {
183 receiveTask.cancel();
184 }
185 if (transmitTask != null) {
186 transmitTask.cancel();
187 }
188 }
189 canceled = true;
190 }
191 }
192
193 private LinkedList<ResolveHandle> queuedRequests = new LinkedList<ResolveHandle>();
194
195 private boolean resolveActive = false;
196
197 /**
198 * Resolve the hostname 'hostname'.
199 *
200 * @param hostname hostname to resolve
201 * @param timeout timeout, calls cb.onTimeout on expiratoin
202 * @param cb callback
203 * @return a handle to cancel the request, null if request could be satisfied immediately
204 */
205 public Cancelable resolveHostname(String hostname, RelativeTime timeout, final AddressCallback cb) {
206 // try if hostname is numeric IP or loopback
207 if (hostname.equalsIgnoreCase("localhost")) {
208 logger.debug("resolving address locally");
209 cb.onAddress(getInet6Localhost());
210 cb.onAddress(getInet4Localhost());
211 cb.onFinished();
212 return null;
213 }
214 if (hostname.equalsIgnoreCase("ip6-localhost")) {
215 cb.onAddress(getInet6Localhost());
216 cb.onFinished();
217 return null;
218 }
219 InetAddress inetAddr = getInetAddressFromString(hostname);
220
221 if (inetAddr != null) {
222 cb.onAddress(inetAddr);
223 cb.onFinished();
224 return null;
225 }
226
227 final ResolveHandle rh = new ResolveHandle();
228 rh.hostname = hostname;
229 rh.deadline = timeout.toAbsolute();
230 rh.cb = cb;
231
232 queuedRequests.addLast(rh);
233 handleNextRequest();
234 return rh;
235 }
236
237 private void handleNextRequest() {
238 if (!resolveActive && !queuedRequests.isEmpty()) {
239 ResolveHandle rh = queuedRequests.pollFirst();
240 handleRequest(rh);
241 }
242 }
243
244 private void handleRequest(final ResolveHandle rh) {
245 if (resolveActive) {
246 throw new AssertionError("resolveActive but new resolve started");
247 }
248
249 resolveActive = true;
250
251 lazyConnect();
252
253 final GetMessage req = new GetMessage();
254 req.direction = GetMessage.DIRECTION_GET_IP;
255 req.domain = GetMessage.AF_UNSPEC;
256
257 TextualAddress textAddr = new TextualAddress();
258 textAddr.addr = rh.hostname;
259
260 req.addr = textAddr;
261
262 final AbsoluteTime deadline = rh.deadline;
263
264 logger.debug("deadline is " + deadline + " | now is " + AbsoluteTime.now());
265
266 logger.debug("remaining is " + deadline.getRemaining());
267
268 rh.transmitTask = client.notifyTransmitReady(
269 deadline.getRemaining(), true,
270 0, new MessageTransmitter() {
271 @Override
272 public void transmit(Connection.MessageSink sink) {
273 if (sink == null) {
274 onTimeout(rh);
275 return;
276 }
277 sink.send(req);
278 rh.transmitTask = null;
279
280 logger.debug("recv in notifyTransmitReady cb");
281 rh.receiveTask = client.receiveOne(deadline.getRemaining(), new MessageReceiver() {
282 @Override
283 public void process(GnunetMessage.Body msg) {
284 rh.receiveTask = null;
285 ResolverResponse gmsg = (ResolverResponse) msg;
286 if (gmsg.responseBody != null) {
287 try {
288 InetAddress in_addr;
289 int len = gmsg.responseBody.addr.length;
290 if (len == 4 || len == 16) {
291 in_addr = InetAddress.getByAddress(gmsg.responseBody.addr);
292 } else {
293 throw new ProtocolViolationException("malformed address message");
294 }
295
296 rh.cb.onAddress(in_addr);
297 rh.receiveTask = client.receiveOne(deadline.getRemaining(), this);
298 } catch (UnknownHostException e) {
299 throw new ProtocolViolationException("malformed address");
300 }
301 } else {
302 resolveActive = false;
303 rh.cb.onFinished();
304 handleNextRequest();
305 }
306 }
307
308 @Override
309 public void handleError() {
310 onTimeout(rh);
311 }
312 });
313
314 }
315
316 @Override
317 public void handleError() {
318 rh.cb.onTimeout();
319 }
320 });
321 }
322
323
324 private void onTimeout(ResolveHandle h) {
325 resolveActive = false;
326 h.cb.onTimeout();
327 handleNextRequest();
328 }
329
330
331 public static Resolver getInstance() {
332 if (singletonInstance == null) {
333 singletonInstance = new Resolver();
334 }
335 return singletonInstance;
336 }
337
338
339 /**
340 * Return a textual representation of an InetAddress. Shortens IPv6 addresses.
341 *
342 * @param addr the address to convert
343 * @return textual representation of the address
344 */
345 public static String ipToString(InetAddress addr) {
346 byte[] a = addr.getAddress();
347 if (a.length == 4) {
348 return addr.getHostAddress();
349 } else if (a.length == 16) {
350 String s = addr.getHostAddress();
351 // replace the first group of zeroes (not the longest) with ::
352 return s.replaceFirst("[:]?0[:](0[:])+0?", "::");
353 } else {
354 throw new RuntimeException("unknown InetAddress format");
355 }
356 }
357
358
359 public static void main(final String[] argv) {
360 new Program(argv) {
361 @Argument(shortname = "r", longname = "reverse",
362 description = "do reverse dns lookup",
363 action = ArgumentAction.SET)
364 boolean isReverse;
365
366 @Override
367 public void run() {
368 if (isReverse) {
369 System.out.println("reverse lookup not supported");
370 } else {
371 resolve();
372 }
373 }
374
375 public void resolve() {
376 final RelativeTime timeout = RelativeTime.SECOND;
377
378 if (unprocessedArgs.length == 0) {
379 logger.warn("no hostname(s) given");
380 } else {
381 logger.info("resolving hostname '" + unprocessedArgs[0] + "'");
382 Resolver.getInstance().resolveHostname(unprocessedArgs[0], timeout, new AddressCallback() {
383 int next = 1;
384
385 @Override
386 public void onAddress(InetAddress addr) {
387 System.out.println(ipToString(addr));
388 }
389
390 @Override
391 public void onFinished() {
392 logger.info("resolve finished");
393 next();
394 }
395
396 @Override
397 public void onTimeout() {
398 logger.warn("resolve timed out");
399 next();
400
401 }
402
403 public void next() {
404 if (unprocessedArgs.length > next) {
405 logger.info("resolving hostname '" + unprocessedArgs[next] + "'");
406 Resolver.getInstance().resolveHostname(unprocessedArgs[next], timeout, this);
407 next++;
408 }
409 }
410 });
411 }
412
413 }
414
415 @Override
416 protected String makeHelpText() {
417 return "tool for forward and reverse DNS lookup";
418 }
419 }.start();
420 }
421}
diff --git a/src/main/java/org/gnunet/util/RunaboutMessageReceiver.java b/src/main/java/org/gnunet/util/RunaboutMessageReceiver.java
new file mode 100644
index 0000000..2a0f067
--- /dev/null
+++ b/src/main/java/org/gnunet/util/RunaboutMessageReceiver.java
@@ -0,0 +1,33 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23import org.grothoff.Runabout;
24
25/**
26 * An abstract base class for message receivers that want to use the runabout, dispatches
27 * messages to the appropriate visit method.
28 */
29public abstract class RunaboutMessageReceiver extends Runabout implements MessageReceiver {
30 public void process(GnunetMessage.Body msg) {
31 this.visitAppropriate(msg);
32 }
33}
diff --git a/src/main/java/org/gnunet/util/RunaboutUtil.java b/src/main/java/org/gnunet/util/RunaboutUtil.java
new file mode 100644
index 0000000..a82dc0a
--- /dev/null
+++ b/src/main/java/org/gnunet/util/RunaboutUtil.java
@@ -0,0 +1,55 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23import org.gnunet.construct.MessageLoader;
24import org.grothoff.Runabout;
25
26import java.lang.reflect.Method;
27import java.util.ArrayList;
28
29
30/**
31 * Utility methods for the runabout.
32 */
33public class RunaboutUtil {
34 public static ArrayList<Class> getRunaboutVisitees(Runabout r) {
35 Class rc = r.getClass();
36 ArrayList<Class> ret = new ArrayList<Class>(5);
37 for (Method m : rc.getMethods()) {
38 if (!(m.getName().equals("visit") && m.getParameterTypes().length == 1)) {
39 continue;
40 }
41 ret.add(m.getParameterTypes()[0]);
42 }
43 return ret;
44 }
45
46 @SuppressWarnings("unchecked")
47 public static int[] getRunaboutMessageTypes(Runabout r) {
48 ArrayList<Class> visitees = getRunaboutVisitees(r);
49 int[] msgtypes = new int[visitees.size()];
50 for (int i = 0; i < visitees.size(); ++i) {
51 msgtypes[i] = MessageLoader.getUnionTag(GnunetMessage.Body.class, visitees.get(i));
52 }
53 return msgtypes;
54 }
55}
diff --git a/src/main/java/org/gnunet/util/Scheduler.java b/src/main/java/org/gnunet/util/Scheduler.java
new file mode 100644
index 0000000..78ea288
--- /dev/null
+++ b/src/main/java/org/gnunet/util/Scheduler.java
@@ -0,0 +1,678 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23import org.slf4j.Logger;
24import org.slf4j.LoggerFactory;
25
26import java.io.*;
27import java.nio.ByteBuffer;
28import java.nio.channels.*;
29import java.nio.channels.spi.SelectorProvider;
30import java.util.*;
31
32/**
33 * Schedule computations using continuation passing style.
34 *
35 * @author Florian Dold
36 */
37public class Scheduler {
38 private static final Logger logger = LoggerFactory
39 .getLogger(Scheduler.class);
40
41 // only valid while a task is executing
42 private static TaskConfiguration activeTask = null;
43
44 // cumulative number of tasks in the ready lists
45 private static volatile int readyCount = 0;
46
47 // for every priority, there is a list of tasks that is definitely ready to run
48 @SuppressWarnings("unchecked")
49 final private static LinkedList<TaskConfiguration>[] readyLists = new LinkedList[Priority.numberOfPriorities];
50
51 static {
52 for (int i = 0; i < Priority.numberOfPriorities; ++i) {
53 readyLists[i] = new LinkedList<TaskConfiguration>();
54 }
55 }
56
57 private static final int EVENT_READ = 0, EVENT_WRITE = 1, EVENT_ACCEPT = 2, EVENT_CONNECT = 3;
58 private static final int[] eventToInterestOp = new int[]{SelectionKey.OP_READ, SelectionKey.OP_WRITE,
59 SelectionKey.OP_ACCEPT, SelectionKey.OP_CONNECT};
60 private static final Reason[] eventToReason = new Reason[]{Reason.READ_READY, Reason.WRITE_READY,
61 Reason.ACCEPT_READY, Reason.CONNECT_READY};
62
63
64 /**
65 * Selector, used to check file descriptors for readiness.
66 */
67 private static Selector selector = null;
68
69 static {
70 try {
71 selector = SelectorProvider.provider().openSelector();
72 } catch (final IOException e) {
73 // what to do here?
74 logger.error("fatal: cannot create selector");
75 System.exit(-1);
76 }
77 }
78
79 /**
80 * true iff the scheduler is currently running.
81 */
82 private static boolean scheduler_running = false;
83
84
85 // tasks that are waiting for an event, which are executed anyway after the deadline has occurred
86 final private static Queue<TaskConfiguration> pending = new PriorityQueue<TaskConfiguration>(5, new Comparator
87 <TaskConfiguration>() {
88 @Override
89 public int compare(TaskConfiguration a, TaskConfiguration b) {
90 return a.deadline.compareTo(b.deadline);
91 }
92 });
93
94
95 /**
96 * Reset the scheduler forcefully.
97 * Intended to be used internally in the Scheduler, as well as in test teardown.
98 */
99 public static void forceReset() {
100 scheduler_running = false;
101 readyCount = 0;
102 activeTask = null;
103 for (int i = 0; i < Priority.numberOfPriorities; ++i) {
104 readyLists[i] = new LinkedList<TaskConfiguration>();
105 }
106 pending.clear();
107 }
108
109
110 /**
111 * Priority for Tasks.
112 */
113 public enum Priority {
114 IDLE, BACKGROUND, DEFAULT, HIGH, UI, URGENT, SHUTDOWN;
115
116 // how many different priorities do we have?
117 private static final int numberOfPriorities = Priority.values().length;
118 }
119
120 /**
121 * Reasons for executing a task.
122 */
123 public enum Reason {
124 STARTUP, SHUTDOWN, TIMEOUT, READ_READY, WRITE_READY, ACCEPT_READY, CONNECT_READY
125 }
126
127 /**
128 * The context of a task that is ready to run.
129 */
130 public static class RunContext {
131 /**
132 * The reason this task has been called by the scheduler.
133 */
134 Set<Reason> reasons = EnumSet.noneOf(Reason.class);
135
136 public RunContext() {
137 }
138 }
139
140 /**
141 * A task is the basic unit of work that is managed by the scheduler.
142 */
143 public static interface Task {
144 public void run(RunContext ctx);
145 }
146
147 /**
148 * A TaskConfiguration represents a Task that will execute or has already been executed.
149 */
150 public static class TaskConfiguration implements Cancelable {
151 private final Task task;
152 private RunContext ctx = new RunContext();
153 private boolean lifeness = true;
154 private Priority priority;
155 private final AbsoluteTime deadline;
156
157 private ArrayList<SelectableChannel> eventChannels = null;
158 private ArrayList<Integer> eventTypes = null;
159
160 private boolean hasRun = false;
161 private boolean isCanceled = false;
162
163 /**
164 * Create a TaskIdentifier.
165 *
166 * @param delay when will the task be run?
167 * may be null to indicate that this task may not be run
168 * (but only queued directly)
169 * @param task task to run with this TaskIdentifier
170 */
171 TaskConfiguration(RelativeTime delay, Task task) {
172 this.task = task;
173 if (delay == null)
174 this.deadline = null;
175 else
176 this.deadline = delay.toAbsolute();
177 }
178
179 private void addChannelEvent(SelectableChannel channel, int eventType) {
180 if (channel == null) {
181 throw new AssertionError("channel must be non-null");
182 }
183 if (eventChannels == null) {
184 eventChannels = new ArrayList<SelectableChannel>();
185 eventTypes = new ArrayList<Integer>();
186 }
187 eventChannels.add(channel);
188 eventTypes.add(eventType);
189
190 int interestOp = eventToInterestOp[eventType];
191
192 SelectionKey key = channel.keyFor(selector);
193 if (key == null || !key.isValid()) {
194 try {
195 key = channel.register(selector, interestOp, new TaskConfiguration[4]);
196 } catch (ClosedChannelException e) {
197 throw new IOError(e);
198 }
199 } else {
200 if ((key.interestOps() & interestOp) != 0) {
201 throw new AssertionError("interest op registered twice");
202 }
203 key.interestOps(key.interestOps() | interestOp);
204 }
205
206 TaskConfiguration[] subscribers = (TaskConfiguration[]) key.attachment();
207 if (subscribers[eventType] != null) {
208 throw new AssertionError("subscriber registered twice");
209 }
210 subscribers[eventType] = this;
211
212 if (subscribers[EVENT_CONNECT] != null && subscribers[EVENT_READ] != null) {
213 throw new AssertionError("OP_CONNECT and OP_READ are incompatible in java");
214 }
215 }
216
217 private void run() {
218 if (hasRun) {
219 throw new AssertionError("same task ran twice");
220 }
221 if (isCanceled) {
222 return;
223 }
224 TaskConfiguration old = activeTask;
225 activeTask = this;
226 task.run(ctx);
227 hasRun = true;
228 activeTask = old;
229 }
230
231 public void cancel() {
232 if (isCanceled) {
233 throw new AssertionError("task canceled twice");
234 }
235 isCanceled = true;
236 pending.remove(this);
237 }
238
239 public Cancelable schedule() {
240 if (this.deadline == null)
241 throw new AssertionError("a task without deadline may not be scheduled");
242 if (priority == null) {
243 if (activeTask != null) {
244 priority = activeTask.priority;
245 } else {
246 priority = Priority.DEFAULT;
247 }
248 }
249 pending.add(this);
250 return this;
251 }
252
253 private void deregister() {
254 if (eventChannels == null) {
255 return;
256 }
257 for (int i = 0; i < eventChannels.size(); ++i) {
258 SelectionKey key = eventChannels.get(i).keyFor(selector);
259 TaskConfiguration[] subscribers = (TaskConfiguration[]) key.attachment();
260 int interestOp = eventToInterestOp[eventTypes.get(i)];
261 if (subscribers[eventTypes.get(i)] == null || (key.interestOps() | interestOp) == 0) {
262 throw new AssertionError("deregistering event that has not been registered");
263 }
264 subscribers[eventTypes.get(i)] = null;
265 key.interestOps(key.interestOps() & (~interestOp));
266 }
267 }
268
269 public void selectRead(SelectableChannel channel) {
270 addChannelEvent(channel, EVENT_READ);
271 }
272
273 public void selectWrite(SelectableChannel channel) {
274 addChannelEvent(channel, EVENT_WRITE);
275 }
276
277 public void selectConnect(SelectableChannel channel) {
278 addChannelEvent(channel, EVENT_CONNECT);
279 }
280
281 public void selectAccept(SelectableChannel channel) {
282 addChannelEvent(channel, EVENT_ACCEPT);
283 }
284 }
285
286 /**
287 * Run the task regardless of any prerequisites, before any other task of
288 * the same priority.
289 */
290 public static synchronized void addContinuation(Task task, EnumSet<Reason> reasons) {
291 TaskConfiguration t = new TaskConfiguration(null, task);
292 t.ctx.reasons = reasons;
293 t.priority = Priority.DEFAULT;
294 queueReady(t);
295 }
296
297 /**
298 * Schedule a new task to be run as soon as possible. The task will be run
299 * with the priority of the calling task.
300 *
301 * @param task main function of the task
302 * @return unique task identifier for the job only valid until "task" is
303 * started!
304 */
305 public static Cancelable add(Task task) {
306 return addDelayed(RelativeTime.ZERO, task);
307 }
308
309 /**
310 * Add a task to run after the specified delay.
311 *
312 * @param delay time to wait until running the task
313 * @param task the task to run after delay
314 * @return the TaskIdentifier, can be used to cancel the task until it has been executed.
315 */
316 public static TaskConfiguration addDelayed(RelativeTime delay, Task task) {
317 TaskConfiguration tid = new TaskConfiguration(delay, task);
318 tid.schedule();
319 return tid;
320 }
321
322 public static TaskConfiguration addRead(RelativeTime timeout,
323 SelectableChannel chan, Task task) {
324 TaskConfiguration tid = new TaskConfiguration(timeout, task);
325 tid.addChannelEvent(chan, EVENT_READ);
326 tid.schedule();
327 return tid;
328 }
329
330 public static TaskConfiguration addWrite(RelativeTime timeout,
331 SelectableChannel chan, Task task) {
332 TaskConfiguration tid = new TaskConfiguration(timeout, task);
333 tid.addChannelEvent(chan, EVENT_WRITE);
334 tid.schedule();
335 return tid;
336 }
337
338 /**
339 * Check if the system is still life. Trigger disconnect if we have tasks, but
340 * none of them give us lifeness.
341 *
342 * @return true to continue the main loop, false to exit
343 */
344 private static boolean checkLiveness() {
345 if (readyCount > 0) {
346 return true;
347 }
348 for (TaskConfiguration t : pending) {
349 if (t.lifeness) {
350 return true;
351 }
352 }
353 // trigger shutdown if we still have pending tasks, but none of them has lifeness
354 if (!pending.isEmpty()) {
355 logger.debug("tasks pending but not alive -- disconnect");
356 shutdown();
357 return true;
358 }
359
360 return false;
361 }
362
363
364 /**
365 * Queue a Task for execution.
366 *
367 * @param tid TaskIdentifier of the ready task
368 */
369 private static synchronized void queueReady(TaskConfiguration tid) {
370 int idx = tid.priority.ordinal();
371 readyLists[idx].add(tid);
372 readyCount++;
373 pending.remove(tid);
374 }
375
376
377 /**
378 * Queue all tasks with expired timeout.
379 *
380 * @return the minimum time to wait until the next timeout expiry
381 */
382 private static RelativeTime handleTimeouts() {
383 RelativeTime timeout = RelativeTime.FOREVER;
384
385 // check if any timeouts occurred
386 while (true) {
387 TaskConfiguration t = pending.peek();
388 if (t == null) {
389 break;
390 }
391 RelativeTime remaining = t.deadline.getRemaining();
392 if (remaining.getMicroseconds() <= 0) {
393 t.deregister();
394 t.ctx.reasons = EnumSet.of(Reason.TIMEOUT);
395 queueReady(t);
396 } else {
397 timeout = remaining;
398 break;
399 }
400 }
401 return timeout;
402 }
403
404 private static void addSubscriberTask(Collection<TaskConfiguration> executableTasks,
405 TaskConfiguration[] subscribers, int eventType) {
406 if (subscribers[eventType] == null) {
407 return;
408 }
409 executableTasks.add(subscribers[eventType]);
410 subscribers[eventType].ctx.reasons.add(eventToReason[eventType]);
411 }
412
413 /**
414 * Select on channels and queue tasks that become executable.
415 *
416 * @param timeout timeout for select
417 */
418 private static void handleSelect(RelativeTime timeout) {
419 long timeout_ms = timeout.getMicroseconds() / 1000;
420 try {
421 // selector.select(0) would block indefinitely (counter-intuitive, java's fault)
422 if (timeout_ms == 0) {
423 selector.selectNow();
424 } else if (timeout.isForever()) {
425 selector.select(0);
426 } else {
427 selector.select(timeout_ms);
428 }
429 } catch (IOException e) {
430 throw new IOError(e);
431 }
432
433 // we have to do this so we don't execute any task twice
434 Collection<TaskConfiguration> executableTasks = new HashSet<TaskConfiguration>();
435 for (SelectionKey sk : selector.selectedKeys()) {
436 TaskConfiguration[] subscribers = (TaskConfiguration[]) sk.attachment();
437
438 if (sk.isReadable()) {
439 addSubscriberTask(executableTasks, subscribers, EVENT_READ);
440 }
441 if (sk.isWritable()) {
442 addSubscriberTask(executableTasks, subscribers, EVENT_WRITE);
443 }
444 if (sk.isAcceptable()) {
445 addSubscriberTask(executableTasks, subscribers, EVENT_ACCEPT);
446 }
447 if (sk.isConnectable()) {
448 addSubscriberTask(executableTasks, subscribers, EVENT_CONNECT);
449 }
450
451 }
452 for (TaskConfiguration tt : executableTasks) {
453 // cancel subscriptions to other events, we can execute now!
454 tt.deregister();
455 queueReady(tt);
456 }
457 }
458
459
460 /**
461 * Initialize and run scheduler. This function will return when all tasks
462 * have completed.
463 */
464 public static void run() {
465 run(null);
466 }
467
468 /**
469 * Initialize and run scheduler. This function will return when all tasks
470 * have completed.
471 *
472 * @param initialTask the initial task to run immediately
473 */
474 public static void run(Task initialTask) {
475 logger.info("running scheduler");
476 if (scheduler_running) {
477 throw new AssertionError("Scheduler already running");
478 }
479 scheduler_running = true;
480 try {
481 run_unchecked(initialTask);
482 } finally {
483 logger.info("cleaning up after scheduler ran");
484 // ensure that after run returns, the scheduler is in its initial state,
485 // even though there was an exception (e.g. after a test case that expects an exception)
486 forceReset();
487 }
488 }
489
490
491 /**
492 * Initialize and run scheduler. This function will return when all tasks
493 * have completed. Don't check if the scheduler is already running or not.
494 *
495 * @param initialTask the initial task to run immediately
496 */
497 private static void run_unchecked(Task initialTask) {
498 if (initialTask != null) {
499 addContinuation(initialTask, EnumSet.of(Reason.STARTUP));
500 }
501
502 // the gnunet main loop
503 while (true) {
504 synchronized (Scheduler.class) {
505 if (checkLiveness() == false)
506 break;
507 RelativeTime nextTimeout = handleTimeouts();
508 if (nextTimeout.getMicroseconds() < 0) {
509 logger.warn("negative timeout for select");
510 }
511
512 // don't select if there are no tasks; we are done!
513 if (readyCount == 0 && pending.isEmpty()) {
514 return;
515 }
516
517 // don't block in select if we have tasks ready to run!
518 if (readyCount > 0) {
519 handleSelect(RelativeTime.ZERO);
520 } else {
521 handleSelect(nextTimeout);
522 }
523
524 runReady();
525 }
526 }
527
528 if (readyCount != 0) {
529 throw new AssertionError("tasks ready after scheduler ran (count)");
530 }
531
532 for (List readyList : Scheduler.readyLists) {
533 if (!readyList.isEmpty()) {
534 throw new AssertionError("tasks ready after scheduler ran (list)");
535 }
536 }
537
538 if (pending.size() != 0) {
539 throw new AssertionError("pending tasks after scheduler ran");
540 }
541
542 if (activeTask != null) {
543 throw new AssertionError("active task after scheduler ran");
544 }
545 }
546
547
548 /**
549 * Execute tasks until there either
550 * <ul>
551 * <li>there are no ready tasks</li>
552 * <li>there is a pending task (which may be of higher priority)</li>
553 * </ul>
554 */
555 private static void runReady() {
556 do {
557 if (readyCount == 0) {
558 return;
559 }
560 // start executing from the highest priority down to 0
561 for (int p = Priority.numberOfPriorities - 1; p >= 0; p--) {
562 // execute all tasks with priority p
563 LinkedList<TaskConfiguration> queue = readyLists[p];
564 while (!queue.isEmpty()) {
565 TaskConfiguration tid = queue.removeFirst();
566 readyCount--;
567 tid.run();
568 }
569 }
570 } while (pending.size() == 0);
571
572 }
573
574 /**
575 * Request the shutdown of the scheduler. Marks all currently pending tasks as
576 * ready because of disconnect. This will cause all tasks to run (as soon as
577 * possible, respecting priorities and prerequisite tasks). Note that tasks
578 * scheduled AFTER this call may still be delayed arbitrarily.
579 */
580 public static void shutdown() {
581 // queueReady() while iterating would yield concurrent modification exn otherwise
582 for (TaskConfiguration tid : new ArrayList<TaskConfiguration>(pending)) {
583 tid.ctx.reasons.add(Reason.SHUTDOWN);
584 queueReady(tid);
585 }
586 pending.clear();
587 }
588
589
590 /**
591 * A handle to a file system object that can be selected on.
592 */
593 public static class FilePipe {
594 private FilePipeThread filePipeThread;
595
596 private FilePipe(FilePipeThread filePipeThread) {
597 this.filePipeThread = filePipeThread;
598 }
599
600 public Pipe.SourceChannel getSource() {
601 return filePipeThread.pipe.source();
602 }
603
604 }
605
606 /**
607 * A thread that reads from a file pipe.
608 */
609 private static class FilePipeThread extends Thread {
610 public File file;
611 public Pipe pipe;
612
613 FilePipeThread(File file) {
614 this.file = file;
615 try {
616 pipe = SelectorProvider.provider().openPipe();
617 pipe.source().configureBlocking(false);
618 pipe.sink().configureBlocking(false);
619 } catch (IOException e) {
620 throw new RuntimeException("selector provider has no pipes");
621 }
622 }
623
624 @Override
625 public void run() {
626 // has to be done in thread, blocks if file is a fifo
627 FileChannel fileChannel;
628
629 try {
630 FileInputStream stream;
631 stream = new FileInputStream(file);
632 fileChannel = stream.getChannel();
633 } catch (FileNotFoundException e) {
634 throw new IOError(e);
635 }
636
637 // we have such a small buffer so that the pipe will not buffer
638 ByteBuffer buffer = ByteBuffer.allocate(1);
639
640 boolean quit = false;
641
642 while (!quit) {
643 try {
644 buffer.clear();
645 fileChannel.read(buffer);
646 buffer.flip();
647 pipe.sink().write(buffer);
648 } catch (IOException e) {
649 quit = true;
650 try {
651 fileChannel.close();
652 } catch (IOException ex) {
653 // nothing we can do here
654 }
655 try {
656 pipe.sink().close();
657 } catch (IOException ex) {
658 // nothing we can do here
659 }
660 try {
661 pipe.source().close();
662 } catch (IOException ex) {
663 // nothing we can do here
664 }
665 }
666 }
667
668 }
669 }
670
671 public static FilePipe openFilePipe(File file) {
672 FilePipeThread fpt = new FilePipeThread(file);
673 fpt.setDaemon(true);
674 fpt.start();
675 return new FilePipe(fpt);
676 }
677}
678
diff --git a/src/main/java/org/gnunet/util/Server.java b/src/main/java/org/gnunet/util/Server.java
new file mode 100644
index 0000000..8a86b45
--- /dev/null
+++ b/src/main/java/org/gnunet/util/Server.java
@@ -0,0 +1,509 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23import org.gnunet.construct.Construct;
24import org.grothoff.Runabout;
25import org.slf4j.Logger;
26import org.slf4j.LoggerFactory;
27
28import java.io.IOException;
29import java.net.SocketAddress;
30import java.nio.channels.ServerSocketChannel;
31import java.nio.channels.SocketChannel;
32import java.util.ArrayList;
33import java.util.Collections;
34import java.util.LinkedList;
35import java.util.List;
36
37/**
38 * A server allows to wait for incoming connections from clients and respectively communicate with those clients.
39 */
40public class Server {
41 private static final Logger logger = LoggerFactory
42 .getLogger(Server.class);
43
44 /**
45 * Default idle timeout for new clients.
46 */
47 private final RelativeTime idleTimeout;
48
49 /**
50 * If true, disconnect a client when it sends a message we do not expect to receive. Otherwise, the unexpected
51 * message will just be discarded.
52 */
53 private final boolean requireFound;
54
55 /**
56 * The sockets this server accepts new connections on.
57 */
58 private List<ServerSocketChannel> listenSockets = new ArrayList<ServerSocketChannel>();
59
60 /**
61 * The list of all clients connected to this server.
62 */
63 private List<ClientHandle> clientHandles = new LinkedList<ClientHandle>();
64
65 /**
66 * The runabout that receives received messages, as well as information about the sender of the last
67 * received message.
68 */
69 private MessageRunabout receivedMessageHandler;
70
71 /**
72 * Whenever a client is disconnected all disconnect handlers are informed.
73 */
74 private List<DisconnectHandler> disconnectHandlers = new LinkedList<DisconnectHandler>();
75
76 /**
77 * Classes of the messages we expect to receive. If a received message is not in this list, the client
78 * will be disconnected, otherwise the message is just ignored.
79 */
80 private List<Class> expectedMessages = Collections.emptyList();
81
82 /**
83 * If true, shut down as soon as all non-monitor clients have finished, and do not allow new connections
84 * to be made to this server.
85 */
86 private boolean inSoftShutdown;
87
88 /**
89 * Task that is executed as soon as a connection is ready to be accepted.
90 */
91 private Cancelable acceptTask;
92
93 /**
94 * True if we are destroyed, or in the process of being destroyed with no way back.
95 */
96 private boolean destroyed;
97
98
99 /**
100 * Interface implemented by disconnect handlers, whose onDisconnect method is called whenever a client
101 * is disconnected from the server.
102 */
103 public interface DisconnectHandler {
104 /**
105 * Called whenever a client is disconnected from the server.
106 *
107 * @param clientHandle the handle for the client that was disconnected
108 */
109 void onDisconnect(ClientHandle clientHandle);
110 }
111
112 /**
113 * A handle to a (remote) client connected to this server.
114 * <p/>
115 * Every client handle keeps a reference count..
116 * Whenever a part of the programs saves a client handle for further interaction with it, keep() should be called.
117 * This prevents the server from disconnecting the client when it is idle.
118 * Once this interaction is over, drop() will decrement the reference count and eventually disconnect the client
119 * after being idle for long enough.
120 */
121 public class ClientHandle {
122 /**
123 * The underlying connection to the client-
124 */
125 private Connection connection;
126
127 /**
128 * When referenceCount==0, the server is allowed to drop the client after a timeout.
129 */
130 private int referenceCount = 0;
131
132 /**
133 * Handle for canceling the receive process of this client, null if no receive is currently going on.
134 */
135 private Cancelable currentReceive;
136
137 /**
138 * Set to true if the connection to this client should not prevent the server from shutting down.
139 */
140 private boolean isMonitor;
141
142 /**
143 * Iff true, disconnect the client as soon as possible.
144 * Disconnecting may sometimes not be possible immediately, for example when the reference count is not zero.
145 */
146 private boolean disconnectRequested;
147
148 /**
149 * Create a client handle.
150 *
151 * @param sock
152 */
153 private ClientHandle(SocketChannel sock) {
154 connection = new Connection(sock);
155 // start receiving
156 receiveDone(true);
157 }
158
159 /**
160 * Notify us when the server has enough space to transmit
161 * a message of the given size to the given client.
162 *
163 * @param size requested amount of buffer space
164 * @param timeout after how long should we give up (and call
165 * notify with buf NULL and size 0)?
166 * @param transmitter callback
167 * @return a handle to cancel the notification
168 */
169 public Cancelable notifyTransmitReady(int size, RelativeTime timeout, MessageTransmitter transmitter) {
170 return connection.notifyTransmitReady(size, timeout, transmitter);
171 }
172
173 /**
174 * Convenience method for sending messages.
175 *
176 * @param timeout when should we give up sending the message, and call cont.cont(false)
177 * @param message the message to send
178 * @param cont called when the message has been sent successfully or on error
179 * @return a handle to cancel sending the message
180 */
181 public Cancelable transmitWhenReady(final RelativeTime timeout, final GnunetMessage.Body message, final Continuation cont) {
182 return notifyTransmitReady(0, timeout, new MessageTransmitter() {
183 @Override
184 public void transmit(Connection.MessageSink sink) {
185 sink.send(message);
186 if (cont != null) {
187 cont.cont(true);
188 }
189 }
190
191 @Override
192 public void handleError() {
193 if (cont != null) {
194 cont.cont(false);
195 }
196 }
197 });
198 }
199
200 /**
201 * Resume receiving from this client, we are done processing the
202 * current request. This function must be called from within each
203 * message handler (or its respective continuations).
204 * <p/>
205 * The server does not automatically continue to receive messages to
206 * support flow control.
207 *
208 * @param stayConnected false if connection to the client should be closed
209 */
210 public void receiveDone(boolean stayConnected) {
211 if (currentReceive != null) {
212 throw new AssertionError("receiveDone() called, but still waiting for message");
213 }
214 if (stayConnected) {
215 currentReceive = connection.receive(RelativeTime.FOREVER, new MessageReceiver() {
216 @Override
217 public void process(GnunetMessage.Body msg) {
218 currentReceive = null;
219 if ((msg instanceof UnknownMessageBody) || !expectedMessages.contains(msg.getClass())) {
220 if (requireFound) {
221 logger.info("disconnecting client sending unknown message");
222 disconnect();
223 }
224 // otherwise, just ignore it
225 }
226 if (receivedMessageHandler == null) {
227 throw new AssertionError("received message, but no handler installed");
228 }
229 receivedMessageHandler.setSender(ClientHandle.this);
230 receivedMessageHandler.visitAppropriate(msg);
231 }
232
233 @Override
234 public void handleError() {
235 logger.warn("error receiving from client");
236 disconnect();
237 }
238 });
239 } else {
240 if (referenceCount > 0) {
241 this.disconnectRequested = true;
242 } else {
243 System.out.println("disconnecting " + this.isMonitor);
244 disconnect();
245 }
246 }
247
248 }
249
250 /**
251 * Ask the server to disconnect from the given client.
252 * <p/>
253 * The client will be disconnected from the server, no matter what the current reference count is.
254 */
255 public void disconnect() {
256 connection.disconnect();
257 // if we are in the process of destruction, to not remove, the destruction function will do this,
258 // removing the client handle while in destruction would yield a concurrent modification exception
259 if (!destroyed) {
260 Server.this.clientHandles.remove(this);
261 }
262 for (DisconnectHandler dh : disconnectHandlers) {
263 dh.onDisconnect(this);
264 }
265 Server.this.testForSoftShutdown();
266 }
267
268 /**
269 * Prevent the client from being disconnected.
270 * For every keep, there should be an additional matching drop.
271 */
272 public void keep() {
273 referenceCount++;
274 }
275
276
277 /**
278 * Allow to disconnect this client, if not prevented by previous calls to keep.
279 * <p/>
280 * A call to drop should be executed for every call to keep.
281 * After drop() has been executed for every matching keep(), the next call to drop()
282 * allows the server to disconnect the client after a timeout.
283 */
284 public void drop() {
285 assert referenceCount > 0;
286 referenceCount--;
287 if (referenceCount == 0 && disconnectRequested) {
288 disconnect();
289 }
290 }
291
292
293 /**
294 * Set the 'monitor' flag on this client. Clients which have been
295 * marked as 'monitors' won't prevent the server from shutting down
296 * once 'GNUNET_SERVER_stop_listening' has been invoked. The idea is
297 * that for "normal" clients we likely want to allow them to process
298 * their requests; however, monitor-clients are likely to 'never'
299 * disconnect during shutdown and thus will not be considered when
300 * determining if the server should continue to exist after
301 * 'GNUNET_SERVER_destroy' has been called.
302 */
303 public void markMonitor() {
304 this.isMonitor = true;
305 }
306
307 public boolean isMonitor() {
308 return isMonitor;
309 }
310 }
311
312
313 /**
314 * All handlers for receiving messages from clients have to inherit this class.
315 * <p/>
316 * MessageRunabout is a standard runabout with the added possibility of getting the sender of the message.
317 * This is necessary as the runabout's visit methods can have only one parameter.
318 */
319 public abstract static class MessageRunabout extends Runabout {
320 private ClientHandle currentSender;
321
322 /**
323 * Allows implementors of MessageRunabout to get the Client that sent the message
324 * currently visited.
325 * <p/>
326 * The return value of getSender() is only valid while executing a visit method.
327 *
328 * @return handle of the client whose message is currently being visited
329 */
330 public final ClientHandle getSender() {
331 return currentSender;
332 }
333
334 /**
335 * Private method used to set the sender for the getSender() method.
336 *
337 * @param clientHandle the client handle to set as the sender
338 */
339 private void setSender(ClientHandle clientHandle) {
340 currentSender = clientHandle;
341 }
342 }
343
344 /**
345 * Create a server listening on all specified addresses.
346 *
347 * @param addresses addresses to bind on
348 * @param idleTimeout time after a client will be disconnected if idle
349 * @param requireFound allow unknown messages to be received without disconnecting the client in response
350 */
351 public Server(List<SocketAddress> addresses, RelativeTime idleTimeout, boolean requireFound) {
352 this.idleTimeout = idleTimeout;
353 this.requireFound = requireFound;
354 try {
355 for (SocketAddress addr : addresses) {
356 ServerSocketChannel socket = ServerSocketChannel.open();
357 socket.configureBlocking(false);
358 socket.socket().bind(addr);
359 logger.debug("socket listening on {}", addr.toString());
360 listenSockets.add(socket);
361 addAcceptSocket(socket);
362 }
363 } catch (IOException e) {
364 throw new RuntimeException("could not bind", e);
365 }
366 }
367
368 /**
369 * Create a server, not listening on any sockets yet for new connections.
370 *
371 * @param idleTimeout time after a client will be disconnected if idle
372 * @param requireFound allow unknown messages to be received without disconnecting the client in response
373 */
374 public Server(RelativeTime idleTimeout, boolean requireFound) {
375 this.idleTimeout = idleTimeout;
376 this.requireFound = requireFound;
377 }
378
379 /**
380 * Accept new connections from the given server socket.
381 *
382 * @param sock the new socket to accept connections from
383 */
384 public final void addAcceptSocket(final ServerSocketChannel sock) {
385 Scheduler.TaskConfiguration b = new Scheduler.TaskConfiguration(RelativeTime.FOREVER,
386 new Scheduler.Task() {
387 @Override
388 public void run(Scheduler.RunContext ctx) {
389 acceptTask = null;
390 try {
391 SocketChannel cli = sock.accept();
392
393 if (cli != null) {
394 logger.debug("client connected");
395 cli.configureBlocking(false);
396 ClientHandle clientHandle = new ClientHandle(cli);
397 clientHandles.add(clientHandle);
398 }
399
400 } catch (IOException e) {
401 throw new RuntimeException("accept failed", e);
402 }
403 addAcceptSocket(sock);
404 }
405 });
406 b.selectAccept(sock);
407 acceptTask = b.schedule();
408 }
409
410 /**
411 * Pass messages that the runabout can handle to it.
412 * There can only be one runabout per message type.
413 * (Discrepancy with the C-API, could be changed in the future)
414 *
415 * @param msgRunabout handler
416 */
417 public void setHandler(MessageRunabout msgRunabout) {
418 receivedMessageHandler = msgRunabout;
419 expectedMessages = RunaboutUtil.getRunaboutVisitees(msgRunabout);
420 }
421
422 /**
423 * Ask the server to notify us whenever a client disconnects.
424 * This handler is called whenever the actual network connection
425 * is closed; the reference count may be zero or larger than zero
426 * at this point. Note that the disconnect handler is also called when
427 *
428 * @param disconnectHandler handler to call on disconnect
429 */
430 public Cancelable notifyDisconnect(final DisconnectHandler disconnectHandler) {
431 this.disconnectHandlers.add(disconnectHandler);
432 return new Cancelable() {
433 @Override
434 public void cancel() {
435 Server.this.disconnectHandlers.remove(disconnectHandler);
436 }
437 };
438 }
439
440 /**
441 * Stop the listen socket destroy the server as soon as only monitor clients are left.
442 */
443 public void stopListening() {
444 inSoftShutdown = true;
445 if (acceptTask != null) {
446 acceptTask.cancel();
447 acceptTask = null;
448 }
449 testForSoftShutdown();
450 }
451
452 /**
453 * Disconnect all clients forcefully from the server and stop listening.
454 * <p/>
455 * No methods should be called on a server and its client handles after destroy() has been called.
456 */
457 public void destroy() {
458 if (destroyed) {
459 return;
460 }
461 destroyed = true;
462 for (ClientHandle h : clientHandles) {
463 h.disconnect();
464 }
465 clientHandles.clear();
466 if (acceptTask != null) {
467 acceptTask.cancel();
468 acceptTask = null;
469 }
470 for (ServerSocketChannel ssc : listenSockets) {
471 try {
472 ssc.close();
473 } catch (IOException e) {
474 logger.error("closing listen socket failed", e);
475 }
476 }
477 }
478
479 /**
480 * Test if we should destroy outselves.
481 */
482 private void testForSoftShutdown() {
483 // do this so we don't have many recursive calls to testForSoftShutdown when shutting down
484 if (destroyed) {
485 return;
486 }
487 if (inSoftShutdown) {
488 System.out.println(""+clientHandles.size());
489 boolean done = true;
490 for (ClientHandle clientHandle : this.clientHandles) {
491 if (!clientHandle.isMonitor) {
492 done = false;
493 }
494 }
495 if (done) {
496 destroy();
497 }
498 }
499 }
500
501 @Override
502 protected void finalize() throws Throwable {
503 super.finalize();
504 if (!destroyed) {
505 logger.warn("Server instance not destroyed, but finalizer called");
506 }
507 destroy();
508 }
509}
diff --git a/src/main/java/org/gnunet/util/Service.java b/src/main/java/org/gnunet/util/Service.java
new file mode 100644
index 0000000..d93e296
--- /dev/null
+++ b/src/main/java/org/gnunet/util/Service.java
@@ -0,0 +1,154 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23
24import org.slf4j.Logger;
25import org.slf4j.LoggerFactory;
26
27import java.io.*;
28import java.net.InetAddress;
29import java.net.InetSocketAddress;
30import java.net.SocketAddress;
31import java.nio.ByteBuffer;
32import java.nio.channels.Pipe;
33import java.util.LinkedList;
34
35/**
36 * Server the entry point class for every gnunet-java component providing services
37 * to other components.
38 *
39 * The configuration for the server (i.e. ports/interfaces) is loaded with the standard configuration system.
40 *
41 * Note that other processes can send signals to the service via a pipe, whose name has to be given in the
42 * environment variable GNUNET_OS_CONTROL_PIPE
43 */
44public abstract class Service extends Program {
45 private static final Logger logger = LoggerFactory
46 .getLogger(Service.class);
47
48 private Server s;
49 private String serviceName;
50 private RelativeTime idleTimeout;
51 private boolean requireFound;
52
53
54 private Cancelable sigpipeTask;
55 private Pipe.SourceChannel sigpipeChannel;
56
57 public Service(String serviceName, RelativeTime idleTimeout, boolean requireFound, String[] args) {
58 super(args);
59 this.serviceName = serviceName;
60 this.idleTimeout = idleTimeout;
61 this.requireFound = requireFound;
62 }
63
64 /**
65 * Obtain the server used by a service. Note that the server must NOT
66 * be destroyed by the caller.
67 *
68 * @return handle to the server for this service, NULL if there is none
69 */
70 public final Server getServer() {
71 return s;
72 }
73
74 /**
75 * Stop the service.
76 */
77 public void stop() {
78 s.stopListening();
79 }
80
81 public void runHook() {
82 String ip4AddrList = getConfiguration().getValueString(serviceName, "ACCEPT_FROM").orNull();
83 String ip6AddrList = getConfiguration().getValueString(serviceName, "ACCEPT_FROM6").orNull();
84 int port = getConfiguration().getValueNumber(serviceName, "PORT").get().intValue();
85
86 LinkedList<SocketAddress> addrs = new LinkedList<SocketAddress>();
87
88 if (ip4AddrList != null) {
89 for (String ip4Addr : ip4AddrList.split("[;]")) {
90 InetAddress addr = Resolver.getInetAddressFromString(ip4Addr);
91 addrs.add(new InetSocketAddress(addr, port));
92 }
93 }
94
95 if (ip6AddrList != null) {
96 for (String ip6Addr : ip6AddrList.split("[;]")) {
97 InetAddress addr = Resolver.getInetAddressFromString(ip6Addr);
98 addrs.add(new InetSocketAddress(addr, port));
99 }
100 }
101
102 s = new Server(addrs, idleTimeout, requireFound);
103
104 String pipeName = System.getenv("GNUNET_OS_CONTROL_PIPE");
105 if (pipeName != null && !pipeName.isEmpty()) {
106 Scheduler.FilePipe p = Scheduler.openFilePipe(new File(pipeName));
107
108 Scheduler.TaskConfiguration t = new Scheduler.TaskConfiguration(RelativeTime.FOREVER,
109 new SigpipeTask());
110 t.selectRead(p.getSource());
111 sigpipeTask = t.schedule();
112 sigpipeChannel = p.getSource();
113 }
114
115 run();
116 }
117
118 private class SigpipeTask implements Scheduler.Task {
119 @Override
120 public void run(Scheduler.RunContext ctx) {
121 ByteBuffer b = ByteBuffer.allocate(1);
122 int n;
123 try {
124 n = sigpipeChannel.read(b);
125 } catch (IOException e) {
126 logger.error("error reading signal pipe", e);
127 return;
128 }
129 b.flip();
130 boolean stopped = false;
131
132 if (n == 1) {
133 byte sig = b.get();
134 // 15=sigterm
135 if (sig == 15) {
136 logger.info("service shutting down");
137 getServer().stopListening();
138 stopped = true;
139 }
140 }
141 if (!stopped) {
142 Scheduler.TaskConfiguration t = new Scheduler.TaskConfiguration(RelativeTime.FOREVER, this);
143 sigpipeTask = t.schedule();
144 } else {
145 try {
146 sigpipeChannel.close();
147 } catch (IOException e) {
148 logger.error("could not close sigpipe channel, quitting");
149 }
150 System.exit(2);
151 }
152 }
153 }
154} \ No newline at end of file
diff --git a/src/main/java/org/gnunet/util/Strings.java b/src/main/java/org/gnunet/util/Strings.java
new file mode 100644
index 0000000..a35568a
--- /dev/null
+++ b/src/main/java/org/gnunet/util/Strings.java
@@ -0,0 +1,138 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23/**
24 * Common functions on Strings, specific to gnunet-java
25 */
26public class Strings {
27 private static final String encTable = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
28
29
30 /**
31 * Convert binary data to ASCII encoding. The ASCII encoding is rather
32 * GNUnet specific. It was chosen such that it only uses characters
33 * in [0-9A-V], can be produced without complex arithmetics and uses a
34 * small number of characters.
35 * Does not append 0-terminator, but returns a pointer to the place where
36 * it should be placed, if needed.
37 *
38 * returned string has length ((size*8) + (((size*8) % 5) > 0 ? 5 - ((size*8) % 5) : 0)) / 5 bytes
39 *
40 * @param data data to encode
41 * @return pointer to the next byte in 'out' or NULL on error.
42 */
43
44 public static String dataToString(byte[] data) {
45 StringBuilder sb = new StringBuilder();
46
47 long rpos = 0;
48 long bits = 0;
49 long vbit = 0;
50 long size = data.length;
51
52 while ((rpos < size) || (vbit > 0)) {
53 if ((rpos < size) && (vbit < 5)) {
54 byte b = data[(int) rpos++];
55 // convert double to int without sign extension
56 int s = b >= 0 ? b : (256 + b);
57 // eat 8 more bits
58 bits = (bits << 8) | s;
59 vbit += 8;
60 }
61 if (vbit < 5) {
62 // zero-padding
63 bits <<= (5 - vbit);
64 vbit = 5;
65 }
66 sb.append(encTable.charAt((int) (bits >>> (vbit - 5)) & 31));
67 vbit -= 5;
68 }
69 return sb.toString();
70 }
71
72 /**
73 * Convert ASCII encoding back to data
74 * out_size must match exactly the size of the data before it was encoded.
75 *
76 * @param string the string to decode
77 * @param outSize size of the output buffer
78 * @return GNUNET_OK on success, GNUNET_SYSERR if result has the wrong encoding
79 */
80
81 public static byte[] stringToData(String string, int outSize) {
82 long rpos;
83 long wpos;
84 long bits;
85 long vbit;
86 long ret;
87 long shift;
88 int enclen = string.length();
89 int encoded_len = outSize * 8;
90 byte[] out = new byte[outSize];
91 if (encoded_len % 5 > 0) {
92 // padding!
93 vbit = encoded_len % 5;
94 shift = 5 - vbit;
95 } else {
96 vbit = 0;
97 shift = 0;
98 }
99 if ((encoded_len + shift) / 5 != enclen) {
100 throw new AssertionError();
101 }
102
103 wpos = outSize;
104 rpos = enclen;
105 bits = (ret = getValue__(string.charAt((int) (--rpos)))) >> (5 - encoded_len % 5);
106 if (-1 == ret) {
107 throw new AssertionError();
108 }
109 while (wpos > 0) {
110 assert rpos > 0;
111 bits = ((ret = getValue__(string.charAt((int) (--rpos)))) << vbit) | bits;
112 if (-1 == ret) {
113 throw new AssertionError();
114 }
115 vbit += 5;
116 if (vbit >= 8) {
117 out[(int)--wpos] = (byte)((char) bits);
118 bits >>= 8;
119 vbit -= 8;
120 }
121 }
122 assert(rpos == 0);
123 assert(vbit == 0);
124 return out;
125 }
126
127
128 private static int getValue__ (char a) {
129 if ((a >= '0') && (a <= '9')) {
130 return a - '0';
131 }
132 if ((a >= 'A') && (a <= 'V')) {
133 return (a - 'A' + 10);
134 }
135 return -1;
136 }
137
138}
diff --git a/src/main/java/org/gnunet/util/TestMessage.java b/src/main/java/org/gnunet/util/TestMessage.java
new file mode 100644
index 0000000..b08a706
--- /dev/null
+++ b/src/main/java/org/gnunet/util/TestMessage.java
@@ -0,0 +1,31 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23import org.gnunet.construct.UnionCase;
24
25/**
26 * Sent back when a client sends this message to a service.
27 */
28@UnionCase(1)
29public class TestMessage implements GnunetMessage.Body {
30 // empty
31}
diff --git a/src/main/java/org/gnunet/util/UnknownMessageBody.java b/src/main/java/org/gnunet/util/UnknownMessageBody.java
new file mode 100644
index 0000000..b978ec6
--- /dev/null
+++ b/src/main/java/org/gnunet/util/UnknownMessageBody.java
@@ -0,0 +1,35 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23/**
24 * Special GnunetMessage body, used to signal that the message containing the body
25 * is not understood, and therefore no real message body could be constructed.
26 *
27 * Note that this class implements GnunetMessage.Body but does not have a MessageID associated.
28 * This message should not, and can not, be sent/received over the network directly as a message body.
29 *
30 * @author Florian Dold
31 */
32public class UnknownMessageBody implements GnunetMessage.Body {
33 public int id;
34 public byte[] data;
35}
diff --git a/src/main/java/org/gnunet/util/getopt/Argument.java b/src/main/java/org/gnunet/util/getopt/Argument.java
new file mode 100644
index 0000000..34159d0
--- /dev/null
+++ b/src/main/java/org/gnunet/util/getopt/Argument.java
@@ -0,0 +1,47 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util.getopt;
22
23
24import java.lang.annotation.ElementType;
25import java.lang.annotation.Retention;
26import java.lang.annotation.RetentionPolicy;
27import java.lang.annotation.Target;
28
29/**
30 * Annotation for fields receiving an argument from the command line.
31 */
32@Retention(RetentionPolicy.RUNTIME)
33@Target(ElementType.FIELD)
34public @interface Argument {
35 public String shortname();
36 public String longname();
37 /**
38 * Possible values: "store-string", "set", "reset", "count", "store-int"
39 */
40 public ArgumentAction action();
41 /*
42 * Name of the Option's argument(s), empty string of option takes no arguments
43 *
44 */
45 public String argumentName() default "";
46 public String description();
47}
diff --git a/src/main/java/org/gnunet/util/getopt/ArgumentAction.java b/src/main/java/org/gnunet/util/getopt/ArgumentAction.java
new file mode 100644
index 0000000..077e71c
--- /dev/null
+++ b/src/main/java/org/gnunet/util/getopt/ArgumentAction.java
@@ -0,0 +1,29 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util.getopt;
22
23
24/**
25 * Possibilities for what should happen when an argument is read from the command line
26 */
27public enum ArgumentAction {
28 SET, RESET, STORE_STRING, STORE_NUMBER
29}
diff --git a/src/main/java/org/gnunet/util/getopt/Parser.java b/src/main/java/org/gnunet/util/getopt/Parser.java
new file mode 100644
index 0000000..6ecc220
--- /dev/null
+++ b/src/main/java/org/gnunet/util/getopt/Parser.java
@@ -0,0 +1,294 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util.getopt;
22
23import org.gnunet.construct.ReflectUtil;
24import java.lang.reflect.Field;
25import java.util.*;
26
27/**
28 * Parser for command line options, in the format indicated by the
29 * annotated members of the target object's class.
30 */
31public class Parser {
32
33 /**
34 * An ArgumentError is thrown if the command line parameters do not match their
35 * specification in the target object's class.
36 */
37 public static class ArgumentError extends RuntimeException {
38 public ArgumentError(String s) {
39 super(s);
40 }
41 }
42
43 /**
44 * An option together with its target field.
45 */
46 static class OptionField {
47 Argument opt;
48 Field f;
49
50 public OptionField(Argument opt, Field f) {
51 this.opt = opt;
52 this.f = f;
53 }
54 }
55
56 // todo: unify with Construct.getMessageFields
57 private List<Field> getFields(Class c) {
58 LinkedList<Field> fields = new LinkedList<Field>(Arrays.asList(c.getDeclaredFields()));
59 while ((c = c.getSuperclass()) != null) {
60 fields.addAll(0, Arrays.asList(c.getDeclaredFields()));
61 }
62 return fields;
63 }
64
65 private Map<String, OptionField> longOpt = new HashMap<String, OptionField>();
66 private Map<String, OptionField> shortOpt = new HashMap<String, OptionField>();
67
68 private Collection<Argument> arguments = new LinkedList<Argument>();
69
70 private Object targetObject;
71
72
73 public Parser(Object targetObject) {
74 this.targetObject = targetObject;
75 // gather option annotations
76 for (Field f : getFields(targetObject.getClass())) {
77 Argument opt = f.getAnnotation(Argument.class);
78 if (opt != null) {
79 if (opt.shortname().length() != 1) {
80 throw new AssertionError("short name must be of length 1");
81 }
82
83 f.setAccessible(true);
84
85 longOpt.put(opt.longname(), new OptionField(opt, f));
86 shortOpt.put(opt.shortname(), new OptionField(opt, f));
87 arguments.add(opt);
88 }
89 }
90 }
91
92 public String getHelp() {
93 StringBuilder helpString = new StringBuilder();
94 for (Argument opt : arguments) {
95 StringBuilder line = new StringBuilder();
96 line.append(" -");
97 line.append(opt.shortname());
98 line.append(" --");
99 line.append(opt.longname());
100 if (!opt.argumentName().isEmpty()) {
101 line.append("=");
102 line.append(opt.argumentName());
103 }
104 while (line.length() < 30) {
105 line.append(" ");
106 }
107 helpString.append(line);
108 helpString.append(" ");
109 helpString.append(opt.description());
110 helpString.append("\n");
111
112 }
113 return helpString.toString();
114 }
115
116 private void doLongOpt(final LinkedList<String> argsList, Field targetField, Argument argument, String right) {
117 try {
118 Class targetFieldType = targetField.getType();
119 switch (argument.action()) {
120 case SET:
121 if (!targetFieldType.equals(Boolean.TYPE)) {
122 throw new AssertionError("action SET only valid on boolean member");
123 }
124 targetField.set(targetObject, true);
125 break;
126 case RESET:
127 if (!targetFieldType.equals(Boolean.TYPE)) {
128 throw new AssertionError("action RESET only valid on boolean member");
129 }
130 targetField.set(targetObject, false);
131 break;
132 case STORE_STRING:
133 if (!targetFieldType.equals(String.class)) {
134 throw new AssertionError("action STORE_STRING only valid on boolean member");
135 }
136 if (right == null) {
137 argsList.removeFirst();
138 if (argsList.isEmpty()) {
139 throw new ArgumentError("missing string argument to option " + argument.longname());
140 }
141 targetField.set(targetObject, argsList.getFirst());
142 } else {
143 targetField.set(targetObject, right);
144 }
145 break;
146 case STORE_NUMBER:
147 ReflectUtil.NumField nf = new ReflectUtil.NumField(targetField);
148 String numString;
149 if (right == null) {
150 argsList.removeFirst();
151 if (argsList.isEmpty()) {
152 throw new ArgumentError("missing number argument to option " + argument.longname());
153 }
154 numString = argsList.getFirst();
155 } else {
156 numString = right;
157 }
158 try {
159 nf.set(targetObject, Long.parseLong(numString));
160 } catch (NumberFormatException e) {
161 throw new ArgumentError("error in number format to option " + argument.longname());
162 }
163 break;
164 }
165 } catch (IllegalAccessException e) {
166 throw new AssertionError(
167 String.format("cannot acces member %s with @Option annotation", targetField.getName()));
168 }
169 }
170
171 /**
172 * returns true if we processed a shortopt with a parameter, and thus have to discard the rest
173 * of the current argument string (that is, stop scanning for more shortopts)
174 */
175 private boolean doShortOpt(final LinkedList<String> argsList, Field targetField, Argument argument, String shortName) {
176 try {
177 switch (argument.action()) {
178 case SET:
179 if (!targetField.getType().equals(Boolean.TYPE)) {
180 throw new AssertionError("action SET only valid on boolean member");
181 }
182 targetField.set(targetObject, true);
183 break;
184 case RESET:
185 if (!targetField.getType().equals(Boolean.TYPE)) {
186 throw new AssertionError("action RESET only valid on boolean field");
187 }
188 targetField.set(targetObject, false);
189 break;
190 case STORE_STRING:
191 if (!targetField.getType().equals(String.class)) {
192 throw new AssertionError("action STORE_STRING only valid on 'String' field");
193 }
194 if (argsList.getFirst().length() == 2) { // -P xxx (with space)
195 argsList.removeFirst();
196 if (argsList.isEmpty()) {
197 throw new ArgumentError(String.format("no argument for short option '%s'",
198 shortName));
199 }
200 targetField.set(targetObject, argsList.getFirst());
201 } else {
202 targetField.set(targetObject, argsList.getFirst().substring(2)); // -Pxxx...
203 }
204 return true;
205 case STORE_NUMBER:
206 ReflectUtil.NumField nf = new ReflectUtil.NumField(targetField);
207 String numString;
208 if (argsList.getFirst().length() == 2) { // -X
209 argsList.removeFirst();
210 if (argsList.isEmpty()) {
211 throw new ArgumentError("missing number argument to option " + argument.longname());
212 }
213 numString = argsList.getFirst();
214 } else {
215 numString = argsList.getFirst().substring(2);
216 }
217 try {
218 nf.set(targetObject, Long.parseLong(numString));
219 } catch (NumberFormatException e) {
220 throw new ArgumentError("error in number format to option " + argument.longname());
221 }
222 return true;
223 }
224 } catch (IllegalAccessException e) {
225 throw new ArgumentError(
226 String.format("cannot acces member %s with @Option annotation", targetField.getName()));
227 }
228 return false; // did not consume entire shortopt -Xxxxxx
229 }
230
231 /**
232 * Parses the given arguments, and sets the target object's fields
233 * according to its annotations.
234 *
235 * @param args array with the program arguments
236 * @return positional arguments
237 */
238 public String[] parse(String[] args) {
239 // unprocessed positional args
240 Deque<String> positionalArgs = new LinkedList<String>();
241
242 LinkedList<String> argsList = new LinkedList<String>(Arrays.asList(args));
243
244 while (!argsList.isEmpty()) {
245 // arguments after single "--" are all positional
246 if (argsList.getFirst().equals("--")) {
247 argsList.removeFirst();
248 positionalArgs.addAll(argsList);
249 break;
250 }
251 // long args
252 if (argsList.getFirst().startsWith("--")) {
253 // remove leading slashes
254 String longOptionString = argsList.getFirst().substring(2);
255 // maybe it is in the format --opt=val
256 String[] components = longOptionString.split("=", 2);
257 OptionField of = longOpt.get(components[0]);
258 if (of == null) {
259 throw new ArgumentError(String.format("unknown long option: '%s'", components[0]));
260 }
261 String right = (components.length == 2) ? components[1] : null;
262 doLongOpt(argsList, of.f, of.opt, right);
263 } else if ((argsList.getFirst().length() > 1) && argsList.getFirst().startsWith("-")) {
264 // handle each flag after the "-"
265 for (int i = 1; i < argsList.getFirst().length(); ++i) {
266 String optShortName = argsList.getFirst().substring(i, i + 1);
267 OptionField of = shortOpt.get(optShortName);
268 if (of == null) {
269 throw new ArgumentError(
270 String.format("unknown short option: -%s", argsList.getFirst().charAt(i)));
271 }
272
273 boolean discard = doShortOpt(argsList, of.f, of.opt, optShortName);
274
275 if (discard && (i != 1)) {
276 throw new ArgumentError("short options with argument must be seperate");
277 }
278
279 if (discard) {
280 break;
281 }
282
283 }
284 } else {
285 positionalArgs.add(argsList.getFirst());
286 }
287
288 argsList.removeFirst();
289 }
290
291 return positionalArgs.toArray(new String[positionalArgs.size()]);
292 }
293
294}
diff --git a/src/main/java/org/gnunet/util/getopt/package-info.java b/src/main/java/org/gnunet/util/getopt/package-info.java
new file mode 100644
index 0000000..0e3bdf5
--- /dev/null
+++ b/src/main/java/org/gnunet/util/getopt/package-info.java
@@ -0,0 +1,24 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21/**
22 * Command line option parsing
23 */
24package org.gnunet.util.getopt;
diff --git a/src/main/java/org/gnunet/util/package-info.java b/src/main/java/org/gnunet/util/package-info.java
new file mode 100644
index 0000000..df61afd
--- /dev/null
+++ b/src/main/java/org/gnunet/util/package-info.java
@@ -0,0 +1,24 @@
1/*
2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21/**
22 * Common utilities for gnunet components.
23 */
24package org.gnunet.util;
diff --git a/src/main/java/org/gnunet/voting/CertificateAuthorityService.java b/src/main/java/org/gnunet/voting/CertificateAuthorityService.java
new file mode 100644
index 0000000..559bd97
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/CertificateAuthorityService.java
@@ -0,0 +1,7 @@
1package org.gnunet.voting;
2
3/**
4 * Permits or denies a voter to participate in an election.
5 */
6public class CertificateAuthorityService {
7}
diff --git a/src/main/java/org/gnunet/voting/ElectionCallTool.java b/src/main/java/org/gnunet/voting/ElectionCallTool.java
new file mode 100644
index 0000000..52ad5fb
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/ElectionCallTool.java
@@ -0,0 +1,50 @@
1package org.gnunet.voting;
2
3
4import org.gnunet.util.Program;
5import org.gnunet.util.getopt.Argument;
6import org.gnunet.util.getopt.ArgumentAction;
7
8public class ElectionCallTool {
9
10 public static void main(String[] args) {
11 new Program(args) {
12 @Argument(
13 shortname = "t",
14 longname = "template",
15 action = ArgumentAction.SET,
16 description = "output election template")
17 boolean template = false;
18 @Argument(
19 shortname = "o",
20 longname = "outfile",
21 argumentName = "FILE",
22 action = ArgumentAction.STORE_STRING,
23 description = "write spec to FILE instead of standard output")
24 String outfilename = null;
25 @Argument(
26 shortname = "V",
27 longname = "verify",
28 action = ArgumentAction.SET,
29 description = "verify that the ESPEC is valid")
30 boolean verify = false;
31 @Argument(
32 shortname = "a",
33 longname = "authorize",
34 action = ArgumentAction.SET,
35 description = "authorize the ESPEC with the authorities")
36 boolean authorize = false;
37
38 @Override
39 protected String makeHelpText() {
40 return "gnunet-vote-call [OPTIONS]... ESPEC\n" +
41 "Create, authorize and verify an election specification";
42 }
43
44 @Override
45 public void run() {
46 }
47
48 }.start();
49 }
50}
diff --git a/src/main/java/org/gnunet/voting/ElectionSpecification.java b/src/main/java/org/gnunet/voting/ElectionSpecification.java
new file mode 100644
index 0000000..8671ad5
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/ElectionSpecification.java
@@ -0,0 +1,5 @@
1package org.gnunet.voting;
2
3public class ElectionSpecification {
4
5}
diff --git a/src/main/java/org/gnunet/voting/TallyAuthorityService.java b/src/main/java/org/gnunet/voting/TallyAuthorityService.java
new file mode 100644
index 0000000..435c318
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/TallyAuthorityService.java
@@ -0,0 +1,5 @@
1package org.gnunet.voting;
2
3
4public class TallyAuthorityService {
5}
diff --git a/src/main/java/org/gnunet/voting/VotingTool.java b/src/main/java/org/gnunet/voting/VotingTool.java
new file mode 100644
index 0000000..02874b6
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/VotingTool.java
@@ -0,0 +1,49 @@
1package org.gnunet.voting;
2
3
4import org.gnunet.util.Program;
5import org.gnunet.util.getopt.Argument;
6import org.gnunet.util.getopt.ArgumentAction;
7
8public class VotingTool {
9 public static void main(String[] args) {
10 new Program(args) {
11 @Argument(
12 shortname = "q",
13 longname = "query",
14 action = ArgumentAction.SET,
15 description = "query election result")
16 boolean query = false;
17
18 @Argument(
19 shortname = "s",
20 longname = "submit",
21 action = ArgumentAction.STORE_STRING,
22 argumentName = "CHOICE",
23 description = "submit vote to the election")
24 String vote = null;
25
26 @Argument(
27 shortname = "p",
28 longname = "certificate",
29 action = ArgumentAction.STORE_STRING,
30 argumentName = "FILE",
31 description = "certificate file with the permission to vote")
32 String certfile = null;
33
34 @Override
35 protected String makeHelpText() {
36 return "gnunet-vote [OPTIONS]... ESPEC\n" +
37 "Submit a vote or query an election's result.\n" +
38 "The election is identified in the ESPEC file.";
39 }
40
41 @Override
42 public void run() {
43
44 }
45 }.start();
46
47 }
48
49}
diff --git a/src/main/java/org/gnunet/voting/simulation/Authority.java b/src/main/java/org/gnunet/voting/simulation/Authority.java
new file mode 100644
index 0000000..792dbbd
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/simulation/Authority.java
@@ -0,0 +1,266 @@
1package org.gnunet.voting.simulation;
2
3import com.google.common.collect.Lists;
4import com.google.common.collect.Maps;
5
6import java.math.BigInteger;
7import java.util.List;
8import java.util.Map;
9
10/**
11 * ...
12 *
13 * @author Florian Dold
14 */
15public class Authority {
16 private BigInteger privateKeyShare;
17 private BigInteger[] secretPolynomial;
18 private TransmitShareVerification shareVerification;
19
20 private int authorityId;
21
22 private BigInteger receivedShare = BigInteger.ZERO;
23
24 private VotingParameters parameters;
25 private List<Authority> participatingAuthorities;
26
27 private List<Ballot> ballots = Lists.newLinkedList();
28
29 private Map<Integer, TallyKeyShare> specializedKeyShares = Maps.newTreeMap();
30
31 private Cyphertext encryptedTally;
32 private BigInteger tallyBaseG;
33
34
35 /**
36 * The commitments of each authority to it's share of the group secret.
37 */
38 Map<Integer, BigInteger> shareCommitments = Maps.newTreeMap();
39
40 private GroupPublicKey groupPublicKey;
41
42
43 public Authority() {
44 }
45
46 public BigInteger getPublicKeyShare() {
47 return parameters.g.modPow(privateKeyShare, parameters.p);
48 }
49
50 public BigInteger createShareForAuthority(int j) {
51 return CryptoUtil.evaluatePolynomial(secretPolynomial, BigInteger.valueOf(j), parameters.q);
52 }
53
54 public void verifyShare(BigInteger distributionShare, TransmitShareVerification senderVerification) {
55 BigInteger v = parameters.g.modPow(distributionShare, parameters.p);
56 BigInteger prod = BigInteger.ONE;
57 for (int l = 0; l < parameters.authorityThreshold; ++l) {
58 BigInteger exp = BigInteger.valueOf(this.getId()).pow(l);
59 BigInteger coeff = senderVerification.coeffs[l];
60 prod = prod.multiply(coeff.modPow(exp, parameters.p)).mod(parameters.p);
61 }
62 if (!v.equals(prod)) {
63 throw new AssertionError("verification of transmitted shared failed");
64 }
65 }
66
67 public void acceptShareFromAuthority(BigInteger distributionShare, TransmitShareVerification senderVerification, Authority sender) {
68 verifyShare(distributionShare, senderVerification);
69 receivedShare = receivedShare.add(distributionShare);
70 }
71
72 public int getId() {
73 return authorityId;
74 }
75
76 /**
77 * Supervisor -> Authority
78 *
79 * @param authorityId
80 * @param parameters
81 * @return
82 */
83 public BigInteger inviteAuthority(int authorityId, VotingParameters parameters) {
84 this.authorityId = authorityId;
85 this.parameters = parameters;
86
87 this.privateKeyShare = parameters.generateZq();
88 this.secretPolynomial = new BigInteger[parameters.authorityThreshold];
89
90 this.secretPolynomial[0] = privateKeyShare;
91 for (int i = 1; i < parameters.authorityThreshold; ++i) {
92 secretPolynomial[i] = parameters.generateZq();
93 }
94
95 shareVerification = new TransmitShareVerification(secretPolynomial, parameters);
96
97 return getPublicKeyShare();
98 }
99
100 /**
101 * Supervisor -> Authority.
102 *
103 * @param participatingAuthorities
104 */
105 public void generateKeyWithAuthorities(List<Authority> participatingAuthorities) {
106 this.participatingAuthorities = participatingAuthorities;
107
108 for (Authority otherAuthority : participatingAuthorities) {
109 otherAuthority.acceptShareFromAuthority(createShareForAuthority(otherAuthority.getId()), shareVerification, this);
110 }
111 }
112
113 public void acceptBallot(Ballot ballot) {
114 verifyBallot(ballot);
115 // todo: check duplicates
116 ballots.add(ballot);
117
118 }
119
120 public void acceptGroupPublicKey(GroupPublicKey key) {
121 groupPublicKey = key;
122 }
123
124 public void acceptSpecializedKeyShare(TallyKeyShare share, Authority sender) {
125 specializedKeyShares.put(sender.getId(), share);
126 verifyTallyKeyShare(share);
127 }
128
129 public void distributeTallyKey() {
130 computeEncryptedTally();
131
132 for (Authority other : participatingAuthorities) {
133 TallyKeyShare tallyKeyShare = new TallyKeyShare(encryptedTally.c1, receivedShare, parameters);
134 other.acceptSpecializedKeyShare(tallyKeyShare, this);
135 }
136 }
137
138 private void computeEncryptedTally() {
139 BigInteger votesX = BigInteger.ONE;
140 BigInteger votesY = BigInteger.ONE;
141 for (Ballot ballot : ballots) {
142 votesX = votesX.multiply(ballot.x).mod(parameters.p);
143 votesY = votesY.multiply(ballot.y).mod(parameters.p);
144 }
145
146 encryptedTally = new Cyphertext(votesX, votesY);
147 }
148
149 private void decryptTallyToBaseG() {
150 Map<Integer, BigInteger> lagrangeCoefficients = Maps.newTreeMap();
151 for (int j : specializedKeyShares.keySet()) {
152 BigInteger n = BigInteger.ONE;
153 BigInteger d = BigInteger.ONE;
154 for (int l : specializedKeyShares.keySet()) {
155 if (l != j) {
156 n = n.multiply(BigInteger.valueOf(l));
157 d = d.multiply(BigInteger.valueOf(l).subtract(BigInteger.valueOf(j)));
158 }
159 }
160 lagrangeCoefficients.put(j, n.multiply(d.modInverse(parameters.q)).mod(parameters.q));
161 }
162
163 BigInteger prod = BigInteger.ONE;
164 for (int authorityIndex : specializedKeyShares.keySet()) {
165 BigInteger wp = specializedKeyShares.get(authorityIndex).w.modPow(lagrangeCoefficients.get(authorityIndex), parameters.p);
166 prod = prod.multiply(wp).mod(parameters.p);
167 }
168
169 tallyBaseG = encryptedTally.c2.multiply(prod.modInverse(parameters.p)).mod(parameters.p);
170 }
171
172 public int decryptTally() {
173 decryptTallyToBaseG();
174
175 int resultRestored = 0;
176 boolean success = false;
177
178 for (int l = -ballots.size(); l <= ballots.size(); ++l) {
179 if (tallyBaseG.equals(parameters.g.modPow(BigInteger.valueOf(l), parameters.p))) {
180 success = true;
181 resultRestored = l;
182 break;
183 }
184 }
185
186 if (!success) {
187 throw new AssertionError();
188 }
189 return resultRestored;
190 }
191
192 /*
193 * Sigma is the the authority's commitment to its share
194 */
195 public void verifyTallyKeyShare(TallyKeyShare share) {
196 BigInteger p = parameters.p;
197 BigInteger g = parameters.g;
198
199 BigInteger c1 = share.c1;
200 BigInteger a = share.a;
201 BigInteger r = share.r;
202 BigInteger b = share.b;
203 BigInteger c = share.c;
204
205 // verifier
206 BigInteger expected1 = g.modPow(r, p);
207 BigInteger received1 = a.multiply(share.sigma.modPow(c, p)).mod(p);
208
209 BigInteger expected2 = c1.modPow(r, p);
210 BigInteger received2 = b.multiply(share.w.modPow(c, p)).mod(p);
211
212 if ((!expected1.equals(received1)) || (!expected2.equals(received2))) {
213 System.err.println(expected1);
214 System.err.println(received1);
215 throw new AssertionError("zero knowledge proof for decryption failed");
216 }
217 }
218
219
220
221 public void verifyBallot(Ballot ballot) {
222 BigInteger g = parameters.g;
223 BigInteger p = parameters.p;
224 BigInteger h = groupPublicKey.getKey();
225 // verifier
226
227 BigInteger voterId = ballot.voterId;
228 BigInteger x = ballot.x;
229 BigInteger y = ballot.y;
230 BigInteger a_1 = ballot.a_1;
231 BigInteger a_2 = ballot.a_2;
232 BigInteger b_1 = ballot.b_1;
233 BigInteger b_2 = ballot.b_2;
234 BigInteger c = ballot.c;
235 BigInteger d_1 = ballot.d_1;
236 BigInteger d_2 = ballot.d_2;
237 BigInteger r_1 = ballot.r_1;
238 BigInteger r_2 = ballot.r_2;
239
240
241 // todo: we should blame someone here, not throw exceptions
242
243
244 if (!c.equals(CryptoUtil.hash(voterId, x, y, a_1, b_1, a_2, b_2).mod(parameters.q))) {
245 throw new AssertionError();
246 }
247
248 if (!c.equals(d_1.add(d_2).mod(p))) {
249 throw new AssertionError();
250 }
251 if (!a_1.equals(g.modPow(r_1, p).multiply(x.modPow(d_1, p)).mod(p))) {
252 throw new AssertionError();
253 }
254 if (!b_1.equals(h.modPow(r_1, p).multiply(y.multiply(g).modPow(d_1, p)).mod(p))) {
255 throw new AssertionError();
256 }
257
258 if (!a_2.equals(g.modPow(r_2, p).multiply(x.modPow(d_2, p)).mod(p))) {
259 throw new AssertionError();
260 }
261
262 if (!b_2.equals(h.modPow(r_2, p).multiply(y.multiply(g.modInverse(p)).modPow(d_2, p)).mod(p))) {
263 throw new AssertionError();
264 }
265 }
266}
diff --git a/src/main/java/org/gnunet/voting/simulation/Ballot.java b/src/main/java/org/gnunet/voting/simulation/Ballot.java
new file mode 100644
index 0000000..1c2bfbe
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/simulation/Ballot.java
@@ -0,0 +1,105 @@
1package org.gnunet.voting.simulation;
2
3import java.math.BigInteger;
4
5/**
6 * A ballot together with a ZKP for its validity
7 *
8 * @author Florian Dold
9 */
10public class Ballot {
11 public final BigInteger voterId;
12
13 // the ElGamal encryption of the vote
14 public final BigInteger x, y;
15 // values for the zero knowledge proof
16 public final BigInteger a_1, b_1, a_2, b_2, r_1, d_1, r_2, d_2, c;
17 private VotingParameters parameters;
18
19
20 public Ballot(boolean v, BigInteger voterId, GroupPublicKey groupPublicKey, VotingParameters parameters) {
21 this.voterId = voterId;
22 this.parameters = parameters;
23
24 BigInteger g = parameters.g;
25 BigInteger p = parameters.p;
26 BigInteger h = groupPublicKey.getKey();
27
28
29 if (v) {
30 BigInteger alpha = parameters.generateZq();
31 BigInteger w = parameters.generateZq();
32 r_1 = parameters.generateZq();
33 d_1 = parameters.generateZq();
34
35 x = g.modPow(alpha, p);
36 y = h.modPow(alpha, p).multiply(g).mod(p);
37
38 a_1 = g.modPow(r_1, p).multiply(x.modPow(d_1, p)).mod(p);
39 b_1 = h.modPow(r_1, p).multiply(y.multiply(g).modPow(d_1, p)).mod(p);
40 a_2 = g.modPow(w, p);
41 b_2 = h.modPow(w, p);
42
43
44 c = CryptoUtil.hash(voterId, x, y, a_1, b_1, a_2, b_2).mod(parameters.q);
45
46 // prover
47 d_2 = c.subtract(d_1);
48 r_2 = w.subtract(alpha.multiply(d_2));
49 } else {
50 // prover
51 BigInteger alpha = parameters.generateZq();
52 BigInteger w = parameters.generateZq();
53 r_2 = parameters.generateZq();
54 d_2 = parameters.generateZq();
55
56 x = g.modPow(alpha, p);
57 y = h.modPow(alpha, p).multiply(g.modInverse(p)).mod(p);
58
59 a_1 = g.modPow(w, p);
60 b_1 = h.modPow(w, p);
61 a_2 = g.modPow(r_2, p).multiply(x.modPow(d_2, p)).mod(p);
62 b_2 = h.modPow(r_2, p).multiply(y.multiply(g.modInverse(p)).modPow(d_2, p)).mod(p);
63
64
65 c = CryptoUtil.hash(voterId, x, y, a_1, b_1, a_2, b_2).mod(parameters.q);
66
67 // prover
68 d_1 = c.subtract(d_2);
69 r_1 = w.subtract(alpha.multiply(d_1));
70 }
71 }
72
73 public void verify(GroupPublicKey groupPublicKey) {
74 BigInteger g = parameters.g;
75 BigInteger p = parameters.p;
76 BigInteger h = groupPublicKey.getKey();
77 // verifier
78
79
80 if (!c.equals(CryptoUtil.hash(voterId, x, y, a_1, b_1, a_2, b_2).mod(parameters.q))) {
81 throw new AssertionError();
82 }
83
84 if (!c.equals(d_1.add(d_2).mod(p))) {
85 throw new AssertionError();
86 }
87 if (!a_1.equals(g.modPow(r_1, p).multiply(x.modPow(d_1, p)).mod(p))) {
88 throw new AssertionError();
89 }
90
91 if (!a_2.equals(g.modPow(r_2, p).multiply(x.modPow(d_2, p)).mod(p))) {
92 throw new AssertionError();
93 }
94
95 if (!b_2.equals(h.modPow(r_2, p).multiply(y.multiply(g.modInverse(p)).modPow(d_2, p)).mod(p))) {
96 throw new AssertionError();
97 }
98
99 if (!b_1.equals(h.modPow(r_1, p).multiply(y.multiply(g).modPow(d_1, p)).mod(p))) {
100 throw new AssertionError();
101 }
102
103
104 }
105}
diff --git a/src/main/java/org/gnunet/voting/simulation/BogusAuthority.java b/src/main/java/org/gnunet/voting/simulation/BogusAuthority.java
new file mode 100644
index 0000000..d59f0cf
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/simulation/BogusAuthority.java
@@ -0,0 +1,9 @@
1package org.gnunet.voting.simulation;
2
3/**
4 * ...
5 *
6 * @author Florian Dold
7 */
8public class BogusAuthority extends Authority {
9}
diff --git a/src/main/java/org/gnunet/voting/simulation/CallForVoters.java b/src/main/java/org/gnunet/voting/simulation/CallForVoters.java
new file mode 100644
index 0000000..a84484d
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/simulation/CallForVoters.java
@@ -0,0 +1,14 @@
1package org.gnunet.voting.simulation;
2
3import java.util.List;
4
5/**
6 * ...
7 *
8 * @author Florian Dold
9 */
10public class CallForVoters {
11 public List<Authority> authorities;
12 public VotingParameters parameters;
13 public GroupPublicKey publicKey;
14}
diff --git a/src/main/java/org/gnunet/voting/simulation/CryptoUtil.java b/src/main/java/org/gnunet/voting/simulation/CryptoUtil.java
new file mode 100644
index 0000000..87ecd9f
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/simulation/CryptoUtil.java
@@ -0,0 +1,86 @@
1package org.gnunet.voting.simulation;
2
3import java.math.BigInteger;
4import java.security.MessageDigest;
5import java.security.NoSuchAlgorithmException;
6import java.security.SecureRandom;
7import java.util.Random;
8
9/**
10 * Miscellaneous helper functions.
11 *
12 * @author Florian Dold
13 */
14public class CryptoUtil {
15 public static final Random random = new Random();
16
17
18 /**
19 * Return a random BigInteger not less than 'min' and not greater than 'max' with uniform distribution.
20 *
21 * @param min the least value that may be generated
22 * @param max the greatest value that may be generated
23 * @return a random BigInteger value in the range [min,max]
24 */
25 public static BigInteger createRandomInRange(BigInteger min,
26 BigInteger max) {
27 int cmp = min.compareTo(max);
28 if (cmp >= 0) {
29 if (cmp > 0) {
30 throw new IllegalArgumentException("'min' may not be greater than 'max'");
31 }
32
33 return min;
34 }
35
36 if (min.bitLength() > max.bitLength() / 2) {
37 return createRandomInRange(BigInteger.ZERO, max.subtract(min)).add(min);
38 }
39
40 for (int i = 0; i < 1000; ++i) {
41 BigInteger x = new BigInteger(max.bitLength(), random);
42 if (x.compareTo(min) >= 0 && x.compareTo(max) <= 0) {
43 return x;
44 }
45 }
46
47 // fall back to a faster (restricted) method
48 // (using only this distribution would lead to a non-uniform distribution, see the BigInteger constructor)
49 return new BigInteger(max.subtract(min).bitLength() - 1, random).add(min);
50 }
51
52
53
54 /**
55 * Evaluate a polynomial over Zp*. Uses Horner's scheme.
56 *
57 * @param coeffs coefficients of the polynomial, where coeffs[i] is the coefficient of x^i
58 * @param x the polynomial is evaluated at this value
59 * @param p what group are we operating in?
60 * @return the result of evaluating the polynomial at x
61 */
62 public static BigInteger evaluatePolynomial(BigInteger[] coeffs, BigInteger x, BigInteger p) {
63 BigInteger z = BigInteger.ZERO;
64 for (int i = 0; i < coeffs.length; ++i) {
65 // z <- zx + c
66 z = z.multiply(x).add(coeffs[coeffs.length - i - 1]);
67 }
68 return z;
69 }
70
71 public static BigInteger hash(BigInteger... x) {
72 MessageDigest md;
73 try {
74 md = MessageDigest.getInstance("SHA-512");
75 } catch (NoSuchAlgorithmException e) {
76 throw new RuntimeException("no SHA-512 available");
77 }
78
79 for (BigInteger v : x) {
80 md.update(v.toByteArray());
81 }
82
83 return new BigInteger(md.digest());
84 }
85
86}
diff --git a/src/main/java/org/gnunet/voting/simulation/Cyphertext.java b/src/main/java/org/gnunet/voting/simulation/Cyphertext.java
new file mode 100644
index 0000000..6e7d72f
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/simulation/Cyphertext.java
@@ -0,0 +1,19 @@
1package org.gnunet.voting.simulation;
2
3import java.math.BigInteger;
4
5/**
6 * ElGamal encryption of a message.
7 *
8 * @author Florian Dold
9 */
10public class Cyphertext {
11 public final BigInteger c1;
12 public final BigInteger c2;
13
14
15 public Cyphertext(BigInteger c1, BigInteger c2) {
16 this.c1 = c1;
17 this.c2 = c2;
18 }
19}
diff --git a/src/main/java/org/gnunet/voting/simulation/ElectionSupervisor.java b/src/main/java/org/gnunet/voting/simulation/ElectionSupervisor.java
new file mode 100644
index 0000000..ee0b181
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/simulation/ElectionSupervisor.java
@@ -0,0 +1,105 @@
1package org.gnunet.voting.simulation;
2
3import com.google.common.collect.Lists;
4
5import java.math.BigInteger;
6import java.util.List;
7
8/**
9 * ...
10 *
11 * @author Florian Dold
12 */
13public class ElectionSupervisor {
14 /**
15 * Accumulator for the group public key.
16 * todo: actually we should record the group publice key share of each authority seperately
17 */
18 private BigInteger groupPublicKeyAccum = BigInteger.ONE;
19
20 private VotingParameters parameters;
21
22 /**
23 * Authorities that will be invited for participation
24 */
25 private List<Authority> availableAuthorities;
26
27 /**
28 * Authorities that have agreed to participate.
29 */
30 private List<Authority> participatingAuthorities = Lists.newArrayList();
31
32
33
34 private enum Phase { START, INVITED, KEYS_GENERATED, PUBKEY_PUBLISHED}
35
36 private Phase currentPhase = Phase.START;
37
38
39 public ElectionSupervisor(List<Authority> availableAuthorities, VotingParameters parameters) {
40 this.parameters = parameters;
41 this.availableAuthorities = availableAuthorities;
42 }
43
44 /**
45 * Send an invitation to all available authorities
46 */
47 public void inviteAuthorities() {
48 if (currentPhase != Phase.START) {
49 throw new AssertionError();
50 }
51
52 int nextAuthorityNumber = 1;
53 for (Authority authority : availableAuthorities) {
54 BigInteger part = authority.inviteAuthority(nextAuthorityNumber, parameters);
55 nextAuthorityNumber += 1;
56 if (part != null) {
57 participatingAuthorities.add(authority);
58 groupPublicKeyAccum = groupPublicKeyAccum.multiply(part).mod(parameters.p);
59 }
60 }
61
62 currentPhase = Phase.INVITED;
63 }
64
65 public CallForVoters createCallForVote() {
66 if (currentPhase != Phase.PUBKEY_PUBLISHED) {
67 throw new AssertionError();
68 }
69
70 CallForVoters callForVoters = new CallForVoters();
71 callForVoters.authorities = participatingAuthorities;
72 callForVoters.parameters = parameters;
73 callForVoters.publicKey = new GroupPublicKey(groupPublicKeyAccum);
74 return callForVoters;
75
76 }
77
78
79 public void ascertainGroupPublicKey() {
80 if (currentPhase != Phase.KEYS_GENERATED) {
81 throw new AssertionError();
82 }
83
84 for (Authority authority : participatingAuthorities) {
85 authority.acceptGroupPublicKey(new GroupPublicKey(groupPublicKeyAccum));
86 }
87
88 currentPhase = Phase.PUBKEY_PUBLISHED;
89
90 }
91
92
93 /**
94 * Publish participating authorities of the key generation, receiving authorities will start the key generation.
95 */
96 public void startKeyGeneration() {
97 if (currentPhase != Phase.INVITED) {
98 throw new AssertionError();
99 }
100 for (Authority authority : participatingAuthorities) {
101 authority.generateKeyWithAuthorities(participatingAuthorities);
102 }
103 currentPhase = Phase.KEYS_GENERATED;
104 }
105}
diff --git a/src/main/java/org/gnunet/voting/simulation/GroupPublicKey.java b/src/main/java/org/gnunet/voting/simulation/GroupPublicKey.java
new file mode 100644
index 0000000..a6f2c74
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/simulation/GroupPublicKey.java
@@ -0,0 +1,28 @@
1package org.gnunet.voting.simulation;
2
3import java.math.BigInteger;
4
5/**
6 * ...
7 *
8 * @author Florian Dold
9 */
10public class GroupPublicKey {
11 final BigInteger key;
12
13 public GroupPublicKey(Authority[] authorities, VotingParameters parameters) {
14 BigInteger h = BigInteger.ONE;
15 for (Authority authority : authorities) {
16 h = h.multiply(authority.getPublicKeyShare()).mod(parameters.p);
17 }
18 key = h;
19 }
20
21 public GroupPublicKey(BigInteger groupPublicKey) {
22 this.key = groupPublicKey;
23 }
24
25 public BigInteger getKey() {
26 return key;
27 }
28}
diff --git a/src/main/java/org/gnunet/voting/simulation/TallyKeyShare.java b/src/main/java/org/gnunet/voting/simulation/TallyKeyShare.java
new file mode 100644
index 0000000..1982358
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/simulation/TallyKeyShare.java
@@ -0,0 +1,41 @@
1package org.gnunet.voting.simulation;
2
3import java.math.BigInteger;
4
5/**
6 * ...
7 *
8 * @author Florian Dold
9 */
10public class TallyKeyShare {
11 final public BigInteger w;
12
13 final public BigInteger sigma;
14
15 final public BigInteger c1;
16 final public BigInteger a, b, c, r;
17
18 final VotingParameters parameters;
19
20 public TallyKeyShare(BigInteger c1, BigInteger share, VotingParameters parameters) {
21 this.parameters = parameters;
22 this.c1 = c1;
23
24 sigma = parameters.g.modPow(share, parameters.p);
25
26 w = c1.modPow(share, parameters.p);
27
28 BigInteger p = parameters.p;
29 BigInteger g = parameters.g;
30
31 // prover
32 BigInteger beta = parameters.generateZq();
33 a = g.modPow(beta, p);
34 b = c1.modPow(beta, p);
35 // verifier
36 c = CryptoUtil.hash(a, b);
37 // prover
38 r = beta.add(share.multiply(c));
39 }
40}
41
diff --git a/src/main/java/org/gnunet/voting/simulation/TransmitShareVerification.java b/src/main/java/org/gnunet/voting/simulation/TransmitShareVerification.java
new file mode 100644
index 0000000..13da325
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/simulation/TransmitShareVerification.java
@@ -0,0 +1,18 @@
1package org.gnunet.voting.simulation;
2
3import java.math.BigInteger;
4
5/**
6 * ...
7 *
8 * @author Florian Dold
9 */
10public class TransmitShareVerification {
11 public final BigInteger[] coeffs;
12 public TransmitShareVerification(BigInteger[] secretPolynomial, VotingParameters parameters) {
13 coeffs = new BigInteger[secretPolynomial.length];
14 for (int i = 0; i < secretPolynomial.length; ++i) {
15 coeffs[i] = parameters.g.modPow(secretPolynomial[i], parameters.p);
16 }
17 }
18}
diff --git a/src/main/java/org/gnunet/voting/simulation/Voter.java b/src/main/java/org/gnunet/voting/simulation/Voter.java
new file mode 100644
index 0000000..53a5640
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/simulation/Voter.java
@@ -0,0 +1,31 @@
1package org.gnunet.voting.simulation;
2
3import java.math.BigInteger;
4
5/**
6 *
7 *
8 * @author Florian Dold
9 */
10public class Voter {
11 boolean b;
12 BigInteger voterId;
13 private CallForVoters callForVoters;
14
15 public Voter(CallForVoters callForVoters) {
16 b = CryptoUtil.random.nextBoolean();
17 voterId = new BigInteger(64, CryptoUtil.random);
18 this.callForVoters = callForVoters;
19 }
20
21 public Ballot generateBallot() {
22 return new Ballot(b, voterId, callForVoters.publicKey, callForVoters.parameters);
23 }
24
25 public void vote() {
26 Ballot ballot = generateBallot();
27 for (Authority authority : callForVoters.authorities) {
28 authority.acceptBallot(ballot);
29 }
30 }
31}
diff --git a/src/main/java/org/gnunet/voting/simulation/VotingParameters.java b/src/main/java/org/gnunet/voting/simulation/VotingParameters.java
new file mode 100644
index 0000000..bc6b987
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/simulation/VotingParameters.java
@@ -0,0 +1,165 @@
1package org.gnunet.voting.simulation;
2
3import java.math.BigInteger;
4import java.security.SecureRandom;
5
6/**
7 * Utilities for the modified ElGamal algorithm.
8 * <p/>
9 * p is a large prime, and g is a high-order element (or even a generator) of Zp*
10 *
11 * @author Florian Dold
12 */
13public class VotingParameters {
14 // large prime, p = 2q + 1
15 public final BigInteger p;
16 // large prime, so that q divides (p-1)
17 public final BigInteger q;
18 // generator of Gq
19 public final BigInteger g;
20
21 public final int authorityCount;
22 public final int authorityThreshold;
23
24 public VotingParameters(BigInteger p, BigInteger q, BigInteger g, int authorityCount, int authorityThreshold) {
25 this.p = p;
26 this.q = q;
27 this.g = g;
28 this.authorityCount = authorityCount;
29 this.authorityThreshold = authorityThreshold;
30 }
31
32 /**
33 * which generates the p and g values from the given parameters,
34 * returning the ElGamalScheme object.
35 * <p/>
36 * Note: can take a while...
37 */
38 public static VotingParameters generateRandomParameters(int size, int certainty, int authorityCount, int authorityThreshold) {
39 BigInteger[] safePrimes = generateSafePrimes(size, certainty);
40 BigInteger p = safePrimes[0];
41 BigInteger q = safePrimes[1];
42 BigInteger alpha = selectGenerator(p, q);
43 BigInteger g = selectSubgroupHigherOrderElement(alpha, p, q);
44 if (!g.modPow(q, p).equals(BigInteger.ONE)) {
45 throw new AssertionError();
46 }
47 if (!(g.compareTo(p) < 0)) {
48 throw new AssertionError();
49 }
50 return new VotingParameters(p, q, g, authorityCount, authorityThreshold);
51 }
52
53 /**
54 * Finds a pair of prime BigInteger's {p, q: p = 2q + 1}, called safe primes.
55 * <p/>
56 * (see: Handbook of Applied Cryptography 4.86)
57 *
58 * @return A 2-element array {p,q} of safe primes.
59 */
60 private static BigInteger[] generateSafePrimes(int size, int certainty) {
61 BigInteger p, q;
62 int qLength = size - 1;
63
64 while (true) {
65 q = new BigInteger(qLength, 2, CryptoUtil.random);
66
67 // p <- 2q + 1
68 p = q.shiftLeft(1).add(BigInteger.ONE);
69
70 // XXX(dold): why do we test q for primality again?
71 if (p.isProbablePrime(certainty) && (certainty <= 2 || q.isProbablePrime(certainty))) {
72 break;
73 }
74 }
75
76 return new BigInteger[]{p, q};
77 }
78
79 /*
80 * Select a high order element of the multiplicative group Zn*
81 */
82 private static BigInteger selectHighOrderElement(BigInteger n, SecureRandom random) {
83 BigInteger g;
84 final BigInteger nMinusTwo = n.subtract(BigInteger.valueOf(2));
85 do {
86 BigInteger h = CryptoUtil.createRandomInRange(BigInteger.valueOf(2), nMinusTwo);
87
88 g = h.modPow(BigInteger.valueOf(2), n);
89 }
90 while (g.equals(BigInteger.valueOf(1)));
91
92 return g;
93 }
94
95 /**
96 * Returns a higher-order-element of Gq, the subgroup of Zp*, with order q where alpha is a generator of Zp*
97 *
98 * (see Handbook of Applied Cryptography 4.81)
99 */
100 private static BigInteger selectSubgroupHigherOrderElement(BigInteger alpha, BigInteger p, BigInteger q) {
101 return alpha.modPow(p.subtract(BigInteger.ONE).divide(q), p);
102 }
103
104 /**
105 * Get the size of the cyclic group Gp used for ElGamal
106 *
107 * @return the size of the cyclic group Gp used for ElGamal
108 */
109 public BigInteger getP() {
110 return p;
111 }
112
113 /**
114 * Get the generator of Gq
115 *
116 * @return the generator of Gq
117 */
118 public BigInteger getG() {
119 return g;
120 }
121
122 /**
123 * Get the generator of Zp*
124 *
125 * @return the generator of Zp*
126 */
127 public BigInteger getQ() {
128 return q;
129 }
130
131 public BigInteger generateGq() {
132 BigInteger r;
133 while (true) {
134 r = CryptoUtil.createRandomInRange(BigInteger.ZERO, this.p);
135 if (r.modPow(q, p).equals(BigInteger.ONE)) {
136 break;
137 }
138 }
139 return r;
140 }
141
142 public static BigInteger selectGenerator(BigInteger p, BigInteger q) {
143 BigInteger pMinusTwo = p.subtract(BigInteger.valueOf(2));
144 BigInteger g;
145 /*
146 * (see: Handbook of Applied Cryptography 4.80)
147 */
148 do {
149 g = CryptoUtil.createRandomInRange(BigInteger.valueOf(2), pMinusTwo);
150 }
151 while (g.modPow(BigInteger.valueOf(2), p).equals(BigInteger.ONE) || g.modPow(q, p).equals(BigInteger.ONE));
152 return g;
153 }
154
155 public BigInteger generateZq() {
156 return CryptoUtil.createRandomInRange(BigInteger.ZERO, this.q.subtract(BigInteger.ONE));
157 }
158
159 public Cyphertext encrypt(BigInteger message, BigInteger publicKey) {
160 BigInteger secret = generateZq();
161 return new Cyphertext(g.modPow(secret, p), message.multiply(publicKey.modPow(secret, p).mod(p)));
162 }
163
164
165}
diff --git a/src/main/java/org/gnunet/voting/simulation/VotingSimulation.java b/src/main/java/org/gnunet/voting/simulation/VotingSimulation.java
new file mode 100644
index 0000000..607c653
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/simulation/VotingSimulation.java
@@ -0,0 +1,122 @@
1package org.gnunet.voting.simulation;
2
3import com.google.common.collect.Lists;
4
5import java.util.ArrayList;
6import java.util.Collection;
7import java.util.Collections;
8import java.util.List;
9
10/**
11 * Simulation of the voting protocol.
12 *
13 * @author Florian Dold
14 */
15public class VotingSimulation {
16 public static void main(String... args) {
17 final int authorityCount = 10;
18 final int authorityThreshold = 6;
19 final VotingParameters parameters = VotingParameters.generateRandomParameters(64, 10, authorityCount, authorityThreshold);
20
21 List<Authority> availableAuthorities = spawnAuthorities(authorityCount, authorityCount);
22
23 final ElectionSupervisor supervisor = new ElectionSupervisor(availableAuthorities, parameters);
24
25 supervisor.inviteAuthorities();
26
27 // todo: what if to little authorities accepted the invitation?
28
29 supervisor.startKeyGeneration();
30
31 // make sure that every authority and the supervisor has the same public key
32 supervisor.ascertainGroupPublicKey();
33
34 CallForVoters callForVoters = supervisor.createCallForVote();
35
36 List<Voter> voters = spawnVoters(callForVoters, 100, 50);
37
38 int checkTally = countCheckTally(voters);
39
40 for (Voter voter : voters) {
41 voter.vote();
42 }
43
44 // todo: make sure every authority has the same votes
45
46 for (Authority authority : callForVoters.authorities) {
47 authority.distributeTallyKey();
48 }
49
50 // todo: blame authorities that failed the zero knowledge proof
51
52 for (Authority authority : callForVoters.authorities) {
53 int tally = authority.decryptTally();
54
55 if (tally != checkTally) {
56 throw new AssertionError();
57 }
58 }
59
60 }
61
62 private static int countCheckTally(List<Voter> voters) {
63 int tally = 0;
64 for (Voter voter : voters) {
65 tally += voter.b ? 1 : -1;
66 }
67 return tally;
68 }
69
70
71 /**
72 * Create all authorities involved in the election, where some authorities are bogus authorities.
73 *
74 * @param authorityCount number of returned authorities
75 * @param authorityThreshold minimum number of honest authorities
76 * @return list of authorities
77 */
78 public static List<Authority> spawnAuthorities(int authorityCount, int authorityThreshold) {
79 List<Authority> authorities = Lists.newArrayList();
80 Collection<Integer> honestAuthorityIndices = VotingSimulation.randomIndices(authorityCount, authorityThreshold);
81 for (int i = 0; i < authorityCount; ++i) {
82 if (honestAuthorityIndices.contains(i)) {
83 authorities.add(new Authority());
84 }
85 }
86 return authorities;
87 }
88
89
90 /**
91 * Create voters, where some voters may be malicious.
92 *
93 * @param callForVote description of the election for the voter
94 * @param voterCount number of all voters
95 * @param honestVoterCount number of honest, non-malicious voters
96 */
97 private static List<Voter> spawnVoters(CallForVoters callForVote, int voterCount, int honestVoterCount) {
98 List<Voter> voters = Lists.newArrayList();
99 for (int i = 0; i < voterCount; ++i) {
100 voters.add(new Voter(callForVote));
101 }
102 return voters;
103 }
104
105 /*
106 * Return between authorityThreshold and authorityCount indices
107 */
108 private static List<Integer> randomIndices(int authorityCount, int authorityThreshold) {
109 ArrayList<Integer> x = Lists.newArrayListWithCapacity(authorityCount);
110 for (int i = 0; i < authorityCount; ++i) {
111 x.add(i);
112 }
113 Collections.shuffle(x);
114 int len;
115 if (authorityCount == authorityThreshold) {
116 len = authorityThreshold;
117 } else {
118 len = CryptoUtil.random.nextInt(authorityCount - authorityThreshold) + authorityThreshold;
119 }
120 return Collections.unmodifiableList(x.subList(0, len));
121 }
122}
diff --git a/src/main/java/org/grothoff/Runabout.java b/src/main/java/org/grothoff/Runabout.java
new file mode 100644
index 0000000..2a1dbb0
--- /dev/null
+++ b/src/main/java/org/grothoff/Runabout.java
@@ -0,0 +1,574 @@
1/*
2 * (C) 2002, 2003, 2004, 2005, 2006 Christian Grothoff
3 *
4 * The Runabout is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License as published by the Free Software
6 * Foundation; either version 2, or (at your option) any later version. The
7 * Runabout is distributed in the hope that it will be useful, but WITHOUT ANY
8 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
9 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
10 * You should have received a copy of the GNU General Public License along with
11 * the Runabout; see the file COPYING. If not, write to the Free Software
12 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
13 *
14 * This software is also licensed under the Eclipse Public License v1.0
15 * available at http://www.eclipse.org/legal/epl-v10.html.
16 */
17package org.grothoff;
18
19import java.io.UnsupportedEncodingException;
20import java.lang.reflect.Array;
21import java.lang.reflect.InvocationTargetException;
22import java.lang.reflect.Method;
23import java.lang.reflect.Modifier;
24import java.util.HashMap;
25
26/**
27 * Runabout is a fast implementation of the Walkabout which is a variant of the
28 * Visitor Pattern that does not require an accept method and uses reflection
29 * instead.
30 * <p/>
31 * An instance of Runabout is able to walk over an arbitrary object graph using
32 * visit methods which take arguments of the specific type of the object to
33 * visit. For each node in the object graph the Runabout invokes the most
34 * appropriate visit method.
35 * <p/>
36 * Using the Runabout typically involves subclassing Runabout and adding a
37 * couple of visit methods. The Runabout provides a 'visitAppropriate' method
38 * which will invoke the most appropriate visit method of the current Runabout
39 * instance. If no visit method is applicable, visitAppropriate calls
40 * visitDefault() which, if not overridden, throws an exception.
41 * <p/>
42 * The elements of the object graph typically extend the Element class, which
43 * provides a generic way to quickly invoke the Runabout on all the fields of
44 * the Element.
45 * <p/>
46 * Note that the Runabout uses dynamic code generation and dynamic loading in
47 * order to be quickly able to invoke the appropriate visit methods. To make the
48 * dynamic code generation fast, the code inlines parts of Java class-files in
49 * binary form (ugly!).<br>
50 * A per-thread Cache is used to speed-up the creation of the Runabout by
51 * caching reflection, code creation and dynamic loading operations.
52 * <p/>
53 * <bf>Restrictions:</bf> Java semantics require:
54 * <ul>
55 * <li>all subclasses must be public, sadly this also means that <strong>you
56 * can not use an anonymous inner class</strong> (!)</li>
57 * <li>the types_length to all arguments of visit methods must be public</li>
58 * <li>all visit methods must be public (!)</li>
59 * </ul>
60 * Otherwise the visitor will die with an IllegalAccessError during execution.
61 * <p/>
62 *
63 * @author Christian Grothoff
64 * @version 5.0
65 */
66public class Runabout {
67
68 /**
69 * Singleton of the Runabout.Cache. We cache reflective information per VM;
70 * this avoids the need for repeated reflection, code generation and
71 * dispatching map updates.
72 */
73 private static final Runabout.Cache cache_ = new Runabout.Cache();
74
75 /**
76 * map_ contains a mapping from a class to the appropriate visit method.
77 * Note that at the beginning, map_ only contains the explicit mappings as
78 * given by the visit methods. Over time, map_ will be amended to also
79 * contain direct mappings for subclasses to the appropriate visit methods
80 * if they are used.
81 */
82 private final HashMap<Class, Runabout.Code> map_;
83
84 /**
85 * Code to invoke if no visitor is found (used to avoid scanning the
86 * hierarchy again and again).
87 */
88 private final Code noCode = new NoCode();
89
90 /**
91 * Set when the subclass of the runabout is not public.
92 */
93 private final boolean isPublic;
94
95 /**
96 * Create a Runabout.
97 */
98 public Runabout() {
99 this.isPublic = Modifier.isPublic(getClass().getModifiers());
100 map_ = cache_.get(this);
101 }
102
103 /**
104 * Call the appropriate visit method. Use this method if you are visiting a
105 * graph of objects (no primitives).
106 *
107 * @param o the object to visit
108 */
109 public void visitAppropriate(Object o) {
110 getAppropriateCodeInternal(o.getClass()).visit(this, o);
111 }
112
113 /**
114 * Obtain the appropriate code to call for class c. The method either
115 * obtains the code quickly from the code map (fast path) or by calling the
116 * lookup method getAppropriateCode.
117 *
118 * @return the code, never returns null
119 */
120 private Code getAppropriateCodeInternal(Class c) {
121 synchronized (cache_) {
122 Code co = map_.get(c);
123 if (co != null)
124 return co;
125 co = getAppropriateCode(c);
126 if (co == null)
127 co = noCode;
128 map_.put(c, co);
129 return co;
130 }
131 }
132
133 /**
134 * Find the appropriate Code to call in the map. If no code is found, return
135 * null. This lookup strategy first attempts to find a visit method defined
136 * for the parent classes of c. If no such method exists, it attempts to
137 * find an unambiguous visit method matching any interface transitively
138 * implemented by c. If that does not exist either, null is returned. If
139 * only an ambiguous visit method exists, an exception is raised.
140 *
141 * @param c the class for which to find the code
142 * @return the code to run, or null if no code was found
143 * @throws RunaboutException if the lookup would be ambiguous
144 */
145 private Code getAppropriateCode(Class c) {
146 if (c.isArray()) {
147 // uh uh, array subtyping in action...
148 int dims = 1;
149 Class component = c.getComponentType();
150 while (component.isArray()) {
151 dims++;
152 component = component.getComponentType();
153 }
154 Class superComp = component.getSuperclass();
155 while (superComp != null) {
156 Code co = map_.get(Array.newInstance(superComp, new int[dims]).getClass());
157 if (co != null)
158 return co;
159 superComp = superComp.getSuperclass();
160 }
161 // now try subtyping with multi-dimensional Object[]
162 // (see crazy runabout test).
163 Class objectClass = c.getSuperclass();
164 while (dims > 1) {
165 Code co = map_.get(Array.newInstance(objectClass, new int[dims]).getClass());
166 if (co != null)
167 return co;
168 dims--;
169 }
170 }
171 Class cl = c.getSuperclass();
172 while (cl != null) {
173 Code co = map_.get(cl);
174 if (co != null)
175 return co;
176 cl = cl.getSuperclass();
177 }
178 return getAppropriateCode_ifc(c, c);
179 }
180
181 /**
182 * Find the appropriate Code to call in the map. If no code is found, return
183 * null.
184 *
185 * @param c the class for which to find the code
186 * @param cl the class where to start looking from
187 * @return the code to run, or null if no code was found
188 */
189 private Code getAppropriateCode_ifc(Class c, Class cl) {
190 Code co = null;
191 while (cl != null) {
192 Class[] ifc = cl.getInterfaces();
193 for (Class anIfc : ifc) {
194 Code r = map_.get(anIfc);
195 if (r != null) {
196 if ((co != null) && (r != co))
197 throw new RunaboutException("Ambiguous resolution for visit call to "
198 + c + " in " + this.getClass().getName());
199 co = r;
200 }
201 }
202 for (Class anIfc : ifc) {
203 Code r = getAppropriateCode_ifc(c, anIfc);
204 if (r != null) {
205 if ((co != null) && (r != co))
206 throw new RunaboutException("Ambiguous resolution for visit call to "
207 + c + " in " + this.getClass().getName());
208 co = r;
209 }
210 }
211 cl = cl.getSuperclass();
212 }
213 return co;
214 }
215
216 /**
217 * Generate the initial version of the map that maps classes to Code to call
218 * the appropriate visit method.
219 */
220 final HashMap<Class, Runabout.Code> makeMap() {
221 // get number of methods
222 int size = 0;
223 Class me = this.getClass();
224 while (me != null) {
225 size += me.getDeclaredMethods().length;
226 me = me.getSuperclass();
227 }
228 // create map with slight over-estimate
229 HashMap<Class, Runabout.Code> result = new HashMap<Class, Runabout.Code>(size * 2);
230 // for all methods - create call code, put
231 me = this.getClass();
232 while (me != null) {
233 Method[] methods = me.getDeclaredMethods();
234 for (final Method m : methods) {
235 if ((m.getName().equals("visit"))
236 && (!Modifier.isStatic(m.getModifiers()))) {
237 Class[] args = m.getParameterTypes();
238 if (args.length != 1)
239 throw new RunaboutException("Invalid number of arguments for Runabout in method "
240 + m);
241 final Class viC = args[0];
242 if (result.get(viC) != null)
243 continue;
244 Code co;
245 if (isPublic) {
246 // invoke the visitor with generated code
247 co = makeCode(viC);
248 if (co == null) {
249 throw new RunaboutException("Could not create/load dynamic code!");
250 }
251 } else {
252 if (!m.isAccessible()) {
253 m.setAccessible(true);
254 }
255 // invoke the visitor with an anonymous inner class,
256 // allows for the runabout to be public as the method made accessible
257 // by the Method instance.
258 // For Java 7+ the performance of this could be improved by using a MethodHandle
259 co = new Code() {
260 @Override
261 public void visit(Runabout r, Object o) {
262 try {
263
264 m.invoke(r, o);
265 } catch (IllegalAccessException e) {
266 throw new RunaboutException(e.toString());
267 } catch (InvocationTargetException e) {
268 System.err.println("stacktrace:");
269 e.getCause().printStackTrace(System.err);
270 throw new RunaboutException(e.getCause().toString());
271 }
272 }
273 };
274 }
275
276 result.put(viC, co);
277
278 }
279 }
280 me = me.getSuperclass();
281 }
282 return result;
283 }
284
285 /**
286 * Create the code to invoke a visit method.
287 *
288 * @param c the type of the argument to the visit method
289 */
290 private Code makeCode(Class c) {
291 byte[] myName // Lovm/util/RunaboutExample; substitute
292 = canonicalName(getClass(), false);
293 final int myNameLen = myName.length;
294 final int myNameLenM2 = myNameLen - 2;
295 byte[] cName // Ljava/lang/String; substitute
296 = canonicalName(c, false);
297 byte[] cNameCast = canonicalName(c, true);
298 final int cNameLen = cName.length;
299 final int cNameLenCast = cNameCast.length - 2;
300 byte[] code = new byte[genCodeTemplate.length - 62 + myNameLenM2
301 + cNameLenCast + cNameLen];
302
303 // Build code by substituting a few strings in genCodeTemplate.
304 // 117-145: org/grothoff/RunaboutExample => myName
305 // 148-164: java/lang/String => cName
306 // 192-200: XXXXXXXX => number
307 // 250-271: (Ljava/lang/String;)V => "("+cName+")V"
308
309 System.arraycopy(genCodeTemplate, 0, code, 0, 115);
310 code[115] = (byte) ((myNameLenM2) >> 8);
311 code[116] = (byte) ((myNameLenM2) & 255);
312 System.arraycopy(myName, 1, code, 117, myNameLenM2);
313 code[117 + myNameLenM2] = 1; // tag for string
314 code[118 + myNameLenM2] = (byte) ((cNameLenCast) >> 8);
315 code[119 + myNameLenM2] = (byte) ((cNameLenCast) & 255);
316 System.arraycopy(cNameCast, 1, code, 120 + myNameLenM2, cNameLenCast);
317 System.arraycopy(genCodeTemplate, 164, code, 120 + myNameLenM2
318 + cNameLenCast, 248 - 164);
319 code[120 + myNameLenM2 + cNameLenCast + 248 - 164] = (byte) ((cNameLen + 3) >> 8);
320 code[120 + myNameLenM2 + cNameLenCast + 249 - 164] = (byte) ((cNameLen + 3) & 255);
321 code[120 + myNameLenM2 + cNameLenCast + 250 - 164] = (byte) '(';
322 System.arraycopy(cName, 0, code, 120 + myNameLenM2 + cNameLenCast + 251
323 - 164, cNameLen);
324 code[120 + myNameLenM2 + cNameLenCast + 250 - 164 + cNameLen + 1] = (byte) ')';
325 code[120 + myNameLenM2 + cNameLenCast + 250 - 164 + cNameLen + 2] = (byte) 'V';
326 System.arraycopy(genCodeTemplate,
327 271,
328 code,
329 120 + myNameLenM2 + cNameLenCast + 250 - 164
330 + cNameLen + 3,
331 genCodeTemplate.length - 271);
332 return cache_.loadCode(code, 120 + myNameLenM2 + cNameLenCast + 192
333 - 164);
334 }
335
336 /**
337 * Get the class name in canonical form.
338 *
339 * @param cls the class, may not be primitive
340 * @return the ovm name, following the convention of
341 * {@code java.util.Class.forName} according to the JavaDoc
342 * specification (JDK 1.2.2/1.3/1.4) which differs from the actual
343 * implementation in both SUN and IBM VMs.
344 */
345 private static byte[] canonicalName(Class cls, boolean forCast) {
346 String cname = cls.getName();
347 try {
348 byte[] utf = cname.getBytes("UTF-8");
349 int len = utf.length; // may be > cname.length()!
350 if ((cname.charAt(0) != '[') || (forCast)) {
351 byte[] ret = new byte[len + 2];
352 ret[0] = (byte) 'L';
353 System.arraycopy(utf, 0, ret, 1, len);
354 ret[len + 1] = (byte) ';';
355 for (int i = len; i > 0; i--)
356 if (ret[i] == (byte) '.')
357 ret[i] = (byte) '/';
358 return ret;
359 }
360 for (int i = len - 1; i >= 0; i--)
361 if (utf[i] == (byte) '.')
362 utf[i] = (byte) '/';
363 return utf;
364 } catch (UnsupportedEncodingException uee) {
365 throw new RunaboutException("UTF8 encoding not supported!?: " + uee);
366 }
367 }
368
369 /**
370 * The Runabout.Cache is essentially a per-class cache of the internal
371 * constant state of a Runabout instance. It contains the generated code to
372 * quicly invoke the appropriate visit methods.
373 *
374 * @author Christian Grothoff
375 */
376 static final class Cache {
377
378 /**
379 * ClassLoader to use to load the code.
380 */
381 private final ClassLoader loader_;
382
383 /**
384 * Last name used by the class loader.
385 */
386 private final byte[] lastName_;
387
388 /**
389 * Mapping of classes to Maps.
390 */
391 private final HashMap<Class, HashMap<Class, Runabout.Code>> cachemap_;
392
393 /**
394 * Code that the loader should use.
395 */
396 byte[] code;
397
398 /**
399 * Create the Cache.
400 */
401 Cache() {
402 loader_ = new ClassLoader() {
403 public Class<?> loadClass(String name)
404 throws ClassNotFoundException {
405 //noinspection StringEquality
406 if (name == "Code") // == works here, as both strings are guaranteed to be interned
407 return defineClass(null, code, 0, code.length);
408 return Thread.currentThread().getContextClassLoader().loadClass(name);
409 }
410 };
411 cachemap_ = new HashMap<Class, HashMap<Class, Runabout.Code>>();
412 lastName_ = new byte[8];
413 for (int i = 0; i < 8; i++)
414 lastName_[i] = (byte) '0';
415 }
416
417 /**
418 * Create a class from the given bytecode. Since classes loaded by the
419 * same Loader must have a unique name, this method patches the bytecode
420 * at the given offset, changing the next 8 characters to a unique Java
421 * classname.
422 *
423 * @param byteCode the bytecode of the class which must describe a class
424 * of type 'Code'. The class must contain a sequence XXXXXXXX at
425 * offset xIdx where the classname is to be patched
426 * @param xIdx the index of the XXXXXXXX sequence
427 * @return an instance of the loaded class, null on error
428 * @throws ArrayIndexOutOfBoundsException if more than 62<sup>8</sup>
429 * classes are loaded :-)
430 * @throws RunaboutException if there are problems with dynamic loading
431 * of the byteCode
432 */
433 Code loadCode(byte[] byteCode, int xIdx) {
434 boolean overflow = true;
435 int index = 7;
436 while (overflow) {
437 overflow = false;
438 lastName_[index]++;
439 if (lastName_[index] == (byte) ('9' + 1))
440 lastName_[index] = (byte) 'A';
441 if (lastName_[index] == (byte) ('Z' + 1))
442 lastName_[index] = (byte) 'a';
443 if (lastName_[index] == (byte) ('z' + 1)) {
444 lastName_[index] = (byte) '0';
445 overflow = true;
446 index--;
447 }
448 }
449 System.arraycopy(lastName_, 0, byteCode, xIdx, 8);
450 code = byteCode;
451
452 Code co;
453 try {
454 co = (Code) loader_.loadClass("Code").newInstance();
455 } catch (InstantiationException ie) {
456 throw new RunaboutException(ie.toString());
457 } catch (ClassNotFoundException cnfe) {
458 throw new RunaboutException(cnfe.toString());
459 } catch (IllegalArgumentException iae) {
460 throw new RunaboutException(iae.toString());
461 } catch (ClassFormatError cfe) {
462 throw new RunaboutException(cfe.toString());
463 } catch (IllegalAccessException iae) {
464 throw new RunaboutException(iae.toString());
465 }
466 code = null; // help GC
467 return co;
468 }
469
470 /**
471 * Obtain a map from the cache.
472 */
473 synchronized HashMap<Class, Runabout.Code> get(Runabout r) {
474 Class c = r.getClass();
475 HashMap<Class, Runabout.Code> map = cachemap_.get(c);
476 if (map == null) {
477 map = r.makeMap();
478 cachemap_.put(c, map);
479 }
480 return map;
481 }
482
483 } // end of Runabout.Cache
484
485 /**
486 * Code is the generic interface that all generated classes implement. It is
487 * used to quickly map a given class to the appropriate visit method.
488 *
489 * @author Christian Grothoff
490 */
491 public static abstract class Code {
492 public Code() {
493 }
494
495 public abstract void visit(Runabout r, Object o);
496
497 } // end of Runabout.Code
498
499 /**
500 * Implementation of Code that is called if no visit method matches (calls
501 * visitDefault).
502 *
503 * @author Christian Grothoff
504 */
505 static final class NoCode extends Code {
506 public final void visit(Runabout r, Object o) {
507 r.visitDefault(o);
508 }
509 } // end of Runabout.NoCode
510
511 /**
512 * Override this method to provide a default behavior when no other visit
513 * matches. The Runabout semantics are to search for a visit(X) and if there
514 * is no match, call visitDefault(). As usual with the Runabout, visit(X)
515 * looks at classes before interfaces. By default, visitDefault throws an
516 * exception.
517 */
518 protected void visitDefault(Object o) {
519 throw new RunaboutException("No visit method defined in "
520 + this.getClass() + " for " + o.getClass());
521 }
522
523 /**
524 * Generic Exception for problems in the Runabout.
525 *
526 * @author Christian Grothoff
527 */
528 public static final class RunaboutException extends RuntimeException {
529 RunaboutException(String s) {
530 super(s);
531 }
532 }
533
534 /**
535 * Compile 'GenCodeXXXXXXXX.java' with the option '-g:none' to tell javac
536 * not to include any debugging information. This is the generated class
537 * file.
538 */
539 private final static byte genCodeTemplate[] = {-54, -2, -70, -66, 0, 0, 0,
540 49, 0, 22, 10, 0, 6, 0, 12, 7, 0, 13, 7, 0, 14, 10, 0, 2, 0, 15, 7,
541 0, 16, 7, 0, 18, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40,
542 41, 86, 1, 0, 4, 67, 111, 100, 101, 1, 0, 5, 118, 105, 115, 105,
543 116, 1, 0, 44, 40, 76, 111, 114, 103, 47, 103, 114, 111, 116, 104,
544 111, 102, 102, 47, 82, 117, 110, 97, 98, 111, 117, 116, 59, 76,
545 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99,
546 116, 59, 41, 86, 12, 0, 7, 0, 8, 1, 0, 28, 111, 114, 103, 47, 103,
547 114, 111, 116, 104, 111, 102, 102, 47, 82, 117, 110, 97, 98, 111,
548 117, 116, 69, 120, 97, 109, 112, 108, 101, 1, 0, 16, 106, 97, 118,
549 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 12, 0,
550 10, 0, 20, 1, 0, 28, 111, 114, 103, 47, 103, 114, 111, 116, 104,
551 111, 102, 102, 47, 71, 101, 110, 67, 111, 100, 101, 88, 88, 88, 88,
552 88, 88, 88, 88, 7, 0, 21, 1, 0, 26, 111, 114, 103, 47, 103, 114,
553 111, 116, 104, 111, 102, 102, 47, 82, 117, 110, 97, 98, 111, 117,
554 116, 36, 67, 111, 100, 101, 1, 0, 12, 73, 110, 110, 101, 114, 67,
555 108, 97, 115, 115, 101, 115, 1, 0, 21, 40, 76, 106, 97, 118, 97,
556 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 86,
557 1, 0, 21, 111, 114, 103, 47, 103, 114, 111, 116, 104, 111, 102,
558 102, 47, 82, 117, 110, 97, 98, 111, 117, 116, 0, 33, 0, 5, 0, 6, 0,
559 0, 0, 0, 0, 2, 0, 1, 0, 7, 0, 8, 0, 1, 0, 9, 0, 0, 0, 17, 0, 1, 0,
560 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 0, 0, 1, 0, 10, 0, 11,
561 0, 1, 0, 9, 0, 0, 0, 24, 0, 2, 0, 3, 0, 0, 0, 12, 43, -64, 0, 2,
562 44, -64, 0, 3, -74, 0, 4, -79, 0, 0, 0, 0, 0, 1, 0, 19, 0, 0, 0,
563 10, 0, 1, 0, 6, 0, 17, 0, 9, 4, 9}; // GenCodeXXXXXXXX.class
564
565 public static void main(String[] args) {
566 Runabout r = new Runabout() {
567 public void visit(String s) {
568 System.out.println("hi!!");
569 }
570 };
571 r.visitAppropriate("foo");
572 }
573
574} // end of Runabout
diff --git a/src/main/java/org/grothoff/package-info.java b/src/main/java/org/grothoff/package-info.java
new file mode 100644
index 0000000..2a8b8f0
--- /dev/null
+++ b/src/main/java/org/grothoff/package-info.java
@@ -0,0 +1,4 @@
1/**
2 * Pure java implementation of single argument multiple dispatch
3 */
4package org.grothoff;