aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/gnunet/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/gnunet/util')
-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
31 files changed, 5343 insertions, 0 deletions
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;