aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/grothoff
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2013-08-27 17:16:18 +0000
committerFlorian Dold <florian.dold@gmail.com>2013-08-27 17:16:18 +0000
commita942ffadee0fe9fd385decdf818ad6baae8c99b3 (patch)
treed500fbdba7379631b0591a19417c7c3f3df29194 /src/main/java/org/grothoff
parent6be9a1ed1b7847c795cb700e3e0bd87824fc0573 (diff)
downloadgnunet-java-a942ffadee0fe9fd385decdf818ad6baae8c99b3.tar.gz
gnunet-java-a942ffadee0fe9fd385decdf818ad6baae8c99b3.zip
- adapted source tree structure to gradle/maven conventions
- added gradle wrapper - fixes to adapt to GNUnet changes (new time unit, ...) - helper process in util - started implementing testbed - skeleton for voting tools - use new mq api - implemented some more transport api - mesh
Diffstat (limited to 'src/main/java/org/grothoff')
-rw-r--r--src/main/java/org/grothoff/Runabout.java574
-rw-r--r--src/main/java/org/grothoff/package-info.java4
2 files changed, 578 insertions, 0 deletions
diff --git a/src/main/java/org/grothoff/Runabout.java b/src/main/java/org/grothoff/Runabout.java
new file mode 100644
index 0000000..2a1dbb0
--- /dev/null
+++ b/src/main/java/org/grothoff/Runabout.java
@@ -0,0 +1,574 @@
1/*
2 * (C) 2002, 2003, 2004, 2005, 2006 Christian Grothoff
3 *
4 * The Runabout is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License as published by the Free Software
6 * Foundation; either version 2, or (at your option) any later version. The
7 * Runabout is distributed in the hope that it will be useful, but WITHOUT ANY
8 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
9 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
10 * You should have received a copy of the GNU General Public License along with
11 * the Runabout; see the file COPYING. If not, write to the Free Software
12 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
13 *
14 * This software is also licensed under the Eclipse Public License v1.0
15 * available at http://www.eclipse.org/legal/epl-v10.html.
16 */
17package org.grothoff;
18
19import java.io.UnsupportedEncodingException;
20import java.lang.reflect.Array;
21import java.lang.reflect.InvocationTargetException;
22import java.lang.reflect.Method;
23import java.lang.reflect.Modifier;
24import java.util.HashMap;
25
26/**
27 * Runabout is a fast implementation of the Walkabout which is a variant of the
28 * Visitor Pattern that does not require an accept method and uses reflection
29 * instead.
30 * <p/>
31 * An instance of Runabout is able to walk over an arbitrary object graph using
32 * visit methods which take arguments of the specific type of the object to
33 * visit. For each node in the object graph the Runabout invokes the most
34 * appropriate visit method.
35 * <p/>
36 * Using the Runabout typically involves subclassing Runabout and adding a
37 * couple of visit methods. The Runabout provides a 'visitAppropriate' method
38 * which will invoke the most appropriate visit method of the current Runabout
39 * instance. If no visit method is applicable, visitAppropriate calls
40 * visitDefault() which, if not overridden, throws an exception.
41 * <p/>
42 * The elements of the object graph typically extend the Element class, which
43 * provides a generic way to quickly invoke the Runabout on all the fields of
44 * the Element.
45 * <p/>
46 * Note that the Runabout uses dynamic code generation and dynamic loading in
47 * order to be quickly able to invoke the appropriate visit methods. To make the
48 * dynamic code generation fast, the code inlines parts of Java class-files in
49 * binary form (ugly!).<br>
50 * A per-thread Cache is used to speed-up the creation of the Runabout by
51 * caching reflection, code creation and dynamic loading operations.
52 * <p/>
53 * <bf>Restrictions:</bf> Java semantics require:
54 * <ul>
55 * <li>all subclasses must be public, sadly this also means that <strong>you
56 * can not use an anonymous inner class</strong> (!)</li>
57 * <li>the types_length to all arguments of visit methods must be public</li>
58 * <li>all visit methods must be public (!)</li>
59 * </ul>
60 * Otherwise the visitor will die with an IllegalAccessError during execution.
61 * <p/>
62 *
63 * @author Christian Grothoff
64 * @version 5.0
65 */
66public class Runabout {
67
68 /**
69 * Singleton of the Runabout.Cache. We cache reflective information per VM;
70 * this avoids the need for repeated reflection, code generation and
71 * dispatching map updates.
72 */
73 private static final Runabout.Cache cache_ = new Runabout.Cache();
74
75 /**
76 * map_ contains a mapping from a class to the appropriate visit method.
77 * Note that at the beginning, map_ only contains the explicit mappings as
78 * given by the visit methods. Over time, map_ will be amended to also
79 * contain direct mappings for subclasses to the appropriate visit methods
80 * if they are used.
81 */
82 private final HashMap<Class, Runabout.Code> map_;
83
84 /**
85 * Code to invoke if no visitor is found (used to avoid scanning the
86 * hierarchy again and again).
87 */
88 private final Code noCode = new NoCode();
89
90 /**
91 * Set when the subclass of the runabout is not public.
92 */
93 private final boolean isPublic;
94
95 /**
96 * Create a Runabout.
97 */
98 public Runabout() {
99 this.isPublic = Modifier.isPublic(getClass().getModifiers());
100 map_ = cache_.get(this);
101 }
102
103 /**
104 * Call the appropriate visit method. Use this method if you are visiting a
105 * graph of objects (no primitives).
106 *
107 * @param o the object to visit
108 */
109 public void visitAppropriate(Object o) {
110 getAppropriateCodeInternal(o.getClass()).visit(this, o);
111 }
112
113 /**
114 * Obtain the appropriate code to call for class c. The method either
115 * obtains the code quickly from the code map (fast path) or by calling the
116 * lookup method getAppropriateCode.
117 *
118 * @return the code, never returns null
119 */
120 private Code getAppropriateCodeInternal(Class c) {
121 synchronized (cache_) {
122 Code co = map_.get(c);
123 if (co != null)
124 return co;
125 co = getAppropriateCode(c);
126 if (co == null)
127 co = noCode;
128 map_.put(c, co);
129 return co;
130 }
131 }
132
133 /**
134 * Find the appropriate Code to call in the map. If no code is found, return
135 * null. This lookup strategy first attempts to find a visit method defined
136 * for the parent classes of c. If no such method exists, it attempts to
137 * find an unambiguous visit method matching any interface transitively
138 * implemented by c. If that does not exist either, null is returned. If
139 * only an ambiguous visit method exists, an exception is raised.
140 *
141 * @param c the class for which to find the code
142 * @return the code to run, or null if no code was found
143 * @throws RunaboutException if the lookup would be ambiguous
144 */
145 private Code getAppropriateCode(Class c) {
146 if (c.isArray()) {
147 // uh uh, array subtyping in action...
148 int dims = 1;
149 Class component = c.getComponentType();
150 while (component.isArray()) {
151 dims++;
152 component = component.getComponentType();
153 }
154 Class superComp = component.getSuperclass();
155 while (superComp != null) {
156 Code co = map_.get(Array.newInstance(superComp, new int[dims]).getClass());
157 if (co != null)
158 return co;
159 superComp = superComp.getSuperclass();
160 }
161 // now try subtyping with multi-dimensional Object[]
162 // (see crazy runabout test).
163 Class objectClass = c.getSuperclass();
164 while (dims > 1) {
165 Code co = map_.get(Array.newInstance(objectClass, new int[dims]).getClass());
166 if (co != null)
167 return co;
168 dims--;
169 }
170 }
171 Class cl = c.getSuperclass();
172 while (cl != null) {
173 Code co = map_.get(cl);
174 if (co != null)
175 return co;
176 cl = cl.getSuperclass();
177 }
178 return getAppropriateCode_ifc(c, c);
179 }
180
181 /**
182 * Find the appropriate Code to call in the map. If no code is found, return
183 * null.
184 *
185 * @param c the class for which to find the code
186 * @param cl the class where to start looking from
187 * @return the code to run, or null if no code was found
188 */
189 private Code getAppropriateCode_ifc(Class c, Class cl) {
190 Code co = null;
191 while (cl != null) {
192 Class[] ifc = cl.getInterfaces();
193 for (Class anIfc : ifc) {
194 Code r = map_.get(anIfc);
195 if (r != null) {
196 if ((co != null) && (r != co))
197 throw new RunaboutException("Ambiguous resolution for visit call to "
198 + c + " in " + this.getClass().getName());
199 co = r;
200 }
201 }
202 for (Class anIfc : ifc) {
203 Code r = getAppropriateCode_ifc(c, anIfc);
204 if (r != null) {
205 if ((co != null) && (r != co))
206 throw new RunaboutException("Ambiguous resolution for visit call to "
207 + c + " in " + this.getClass().getName());
208 co = r;
209 }
210 }
211 cl = cl.getSuperclass();
212 }
213 return co;
214 }
215
216 /**
217 * Generate the initial version of the map that maps classes to Code to call
218 * the appropriate visit method.
219 */
220 final HashMap<Class, Runabout.Code> makeMap() {
221 // get number of methods
222 int size = 0;
223 Class me = this.getClass();
224 while (me != null) {
225 size += me.getDeclaredMethods().length;
226 me = me.getSuperclass();
227 }
228 // create map with slight over-estimate
229 HashMap<Class, Runabout.Code> result = new HashMap<Class, Runabout.Code>(size * 2);
230 // for all methods - create call code, put
231 me = this.getClass();
232 while (me != null) {
233 Method[] methods = me.getDeclaredMethods();
234 for (final Method m : methods) {
235 if ((m.getName().equals("visit"))
236 && (!Modifier.isStatic(m.getModifiers()))) {
237 Class[] args = m.getParameterTypes();
238 if (args.length != 1)
239 throw new RunaboutException("Invalid number of arguments for Runabout in method "
240 + m);
241 final Class viC = args[0];
242 if (result.get(viC) != null)
243 continue;
244 Code co;
245 if (isPublic) {
246 // invoke the visitor with generated code
247 co = makeCode(viC);
248 if (co == null) {
249 throw new RunaboutException("Could not create/load dynamic code!");
250 }
251 } else {
252 if (!m.isAccessible()) {
253 m.setAccessible(true);
254 }
255 // invoke the visitor with an anonymous inner class,
256 // allows for the runabout to be public as the method made accessible
257 // by the Method instance.
258 // For Java 7+ the performance of this could be improved by using a MethodHandle
259 co = new Code() {
260 @Override
261 public void visit(Runabout r, Object o) {
262 try {
263
264 m.invoke(r, o);
265 } catch (IllegalAccessException e) {
266 throw new RunaboutException(e.toString());
267 } catch (InvocationTargetException e) {
268 System.err.println("stacktrace:");
269 e.getCause().printStackTrace(System.err);
270 throw new RunaboutException(e.getCause().toString());
271 }
272 }
273 };
274 }
275
276 result.put(viC, co);
277
278 }
279 }
280 me = me.getSuperclass();
281 }
282 return result;
283 }
284
285 /**
286 * Create the code to invoke a visit method.
287 *
288 * @param c the type of the argument to the visit method
289 */
290 private Code makeCode(Class c) {
291 byte[] myName // Lovm/util/RunaboutExample; substitute
292 = canonicalName(getClass(), false);
293 final int myNameLen = myName.length;
294 final int myNameLenM2 = myNameLen - 2;
295 byte[] cName // Ljava/lang/String; substitute
296 = canonicalName(c, false);
297 byte[] cNameCast = canonicalName(c, true);
298 final int cNameLen = cName.length;
299 final int cNameLenCast = cNameCast.length - 2;
300 byte[] code = new byte[genCodeTemplate.length - 62 + myNameLenM2
301 + cNameLenCast + cNameLen];
302
303 // Build code by substituting a few strings in genCodeTemplate.
304 // 117-145: org/grothoff/RunaboutExample => myName
305 // 148-164: java/lang/String => cName
306 // 192-200: XXXXXXXX => number
307 // 250-271: (Ljava/lang/String;)V => "("+cName+")V"
308
309 System.arraycopy(genCodeTemplate, 0, code, 0, 115);
310 code[115] = (byte) ((myNameLenM2) >> 8);
311 code[116] = (byte) ((myNameLenM2) & 255);
312 System.arraycopy(myName, 1, code, 117, myNameLenM2);
313 code[117 + myNameLenM2] = 1; // tag for string
314 code[118 + myNameLenM2] = (byte) ((cNameLenCast) >> 8);
315 code[119 + myNameLenM2] = (byte) ((cNameLenCast) & 255);
316 System.arraycopy(cNameCast, 1, code, 120 + myNameLenM2, cNameLenCast);
317 System.arraycopy(genCodeTemplate, 164, code, 120 + myNameLenM2
318 + cNameLenCast, 248 - 164);
319 code[120 + myNameLenM2 + cNameLenCast + 248 - 164] = (byte) ((cNameLen + 3) >> 8);
320 code[120 + myNameLenM2 + cNameLenCast + 249 - 164] = (byte) ((cNameLen + 3) & 255);
321 code[120 + myNameLenM2 + cNameLenCast + 250 - 164] = (byte) '(';
322 System.arraycopy(cName, 0, code, 120 + myNameLenM2 + cNameLenCast + 251
323 - 164, cNameLen);
324 code[120 + myNameLenM2 + cNameLenCast + 250 - 164 + cNameLen + 1] = (byte) ')';
325 code[120 + myNameLenM2 + cNameLenCast + 250 - 164 + cNameLen + 2] = (byte) 'V';
326 System.arraycopy(genCodeTemplate,
327 271,
328 code,
329 120 + myNameLenM2 + cNameLenCast + 250 - 164
330 + cNameLen + 3,
331 genCodeTemplate.length - 271);
332 return cache_.loadCode(code, 120 + myNameLenM2 + cNameLenCast + 192
333 - 164);
334 }
335
336 /**
337 * Get the class name in canonical form.
338 *
339 * @param cls the class, may not be primitive
340 * @return the ovm name, following the convention of
341 * {@code java.util.Class.forName} according to the JavaDoc
342 * specification (JDK 1.2.2/1.3/1.4) which differs from the actual
343 * implementation in both SUN and IBM VMs.
344 */
345 private static byte[] canonicalName(Class cls, boolean forCast) {
346 String cname = cls.getName();
347 try {
348 byte[] utf = cname.getBytes("UTF-8");
349 int len = utf.length; // may be > cname.length()!
350 if ((cname.charAt(0) != '[') || (forCast)) {
351 byte[] ret = new byte[len + 2];
352 ret[0] = (byte) 'L';
353 System.arraycopy(utf, 0, ret, 1, len);
354 ret[len + 1] = (byte) ';';
355 for (int i = len; i > 0; i--)
356 if (ret[i] == (byte) '.')
357 ret[i] = (byte) '/';
358 return ret;
359 }
360 for (int i = len - 1; i >= 0; i--)
361 if (utf[i] == (byte) '.')
362 utf[i] = (byte) '/';
363 return utf;
364 } catch (UnsupportedEncodingException uee) {
365 throw new RunaboutException("UTF8 encoding not supported!?: " + uee);
366 }
367 }
368
369 /**
370 * The Runabout.Cache is essentially a per-class cache of the internal
371 * constant state of a Runabout instance. It contains the generated code to
372 * quicly invoke the appropriate visit methods.
373 *
374 * @author Christian Grothoff
375 */
376 static final class Cache {
377
378 /**
379 * ClassLoader to use to load the code.
380 */
381 private final ClassLoader loader_;
382
383 /**
384 * Last name used by the class loader.
385 */
386 private final byte[] lastName_;
387
388 /**
389 * Mapping of classes to Maps.
390 */
391 private final HashMap<Class, HashMap<Class, Runabout.Code>> cachemap_;
392
393 /**
394 * Code that the loader should use.
395 */
396 byte[] code;
397
398 /**
399 * Create the Cache.
400 */
401 Cache() {
402 loader_ = new ClassLoader() {
403 public Class<?> loadClass(String name)
404 throws ClassNotFoundException {
405 //noinspection StringEquality
406 if (name == "Code") // == works here, as both strings are guaranteed to be interned
407 return defineClass(null, code, 0, code.length);
408 return Thread.currentThread().getContextClassLoader().loadClass(name);
409 }
410 };
411 cachemap_ = new HashMap<Class, HashMap<Class, Runabout.Code>>();
412 lastName_ = new byte[8];
413 for (int i = 0; i < 8; i++)
414 lastName_[i] = (byte) '0';
415 }
416
417 /**
418 * Create a class from the given bytecode. Since classes loaded by the
419 * same Loader must have a unique name, this method patches the bytecode
420 * at the given offset, changing the next 8 characters to a unique Java
421 * classname.
422 *
423 * @param byteCode the bytecode of the class which must describe a class
424 * of type 'Code'. The class must contain a sequence XXXXXXXX at
425 * offset xIdx where the classname is to be patched
426 * @param xIdx the index of the XXXXXXXX sequence
427 * @return an instance of the loaded class, null on error
428 * @throws ArrayIndexOutOfBoundsException if more than 62<sup>8</sup>
429 * classes are loaded :-)
430 * @throws RunaboutException if there are problems with dynamic loading
431 * of the byteCode
432 */
433 Code loadCode(byte[] byteCode, int xIdx) {
434 boolean overflow = true;
435 int index = 7;
436 while (overflow) {
437 overflow = false;
438 lastName_[index]++;
439 if (lastName_[index] == (byte) ('9' + 1))
440 lastName_[index] = (byte) 'A';
441 if (lastName_[index] == (byte) ('Z' + 1))
442 lastName_[index] = (byte) 'a';
443 if (lastName_[index] == (byte) ('z' + 1)) {
444 lastName_[index] = (byte) '0';
445 overflow = true;
446 index--;
447 }
448 }
449 System.arraycopy(lastName_, 0, byteCode, xIdx, 8);
450 code = byteCode;
451
452 Code co;
453 try {
454 co = (Code) loader_.loadClass("Code").newInstance();
455 } catch (InstantiationException ie) {
456 throw new RunaboutException(ie.toString());
457 } catch (ClassNotFoundException cnfe) {
458 throw new RunaboutException(cnfe.toString());
459 } catch (IllegalArgumentException iae) {
460 throw new RunaboutException(iae.toString());
461 } catch (ClassFormatError cfe) {
462 throw new RunaboutException(cfe.toString());
463 } catch (IllegalAccessException iae) {
464 throw new RunaboutException(iae.toString());
465 }
466 code = null; // help GC
467 return co;
468 }
469
470 /**
471 * Obtain a map from the cache.
472 */
473 synchronized HashMap<Class, Runabout.Code> get(Runabout r) {
474 Class c = r.getClass();
475 HashMap<Class, Runabout.Code> map = cachemap_.get(c);
476 if (map == null) {
477 map = r.makeMap();
478 cachemap_.put(c, map);
479 }
480 return map;
481 }
482
483 } // end of Runabout.Cache
484
485 /**
486 * Code is the generic interface that all generated classes implement. It is
487 * used to quickly map a given class to the appropriate visit method.
488 *
489 * @author Christian Grothoff
490 */
491 public static abstract class Code {
492 public Code() {
493 }
494
495 public abstract void visit(Runabout r, Object o);
496
497 } // end of Runabout.Code
498
499 /**
500 * Implementation of Code that is called if no visit method matches (calls
501 * visitDefault).
502 *
503 * @author Christian Grothoff
504 */
505 static final class NoCode extends Code {
506 public final void visit(Runabout r, Object o) {
507 r.visitDefault(o);
508 }
509 } // end of Runabout.NoCode
510
511 /**
512 * Override this method to provide a default behavior when no other visit
513 * matches. The Runabout semantics are to search for a visit(X) and if there
514 * is no match, call visitDefault(). As usual with the Runabout, visit(X)
515 * looks at classes before interfaces. By default, visitDefault throws an
516 * exception.
517 */
518 protected void visitDefault(Object o) {
519 throw new RunaboutException("No visit method defined in "
520 + this.getClass() + " for " + o.getClass());
521 }
522
523 /**
524 * Generic Exception for problems in the Runabout.
525 *
526 * @author Christian Grothoff
527 */
528 public static final class RunaboutException extends RuntimeException {
529 RunaboutException(String s) {
530 super(s);
531 }
532 }
533
534 /**
535 * Compile 'GenCodeXXXXXXXX.java' with the option '-g:none' to tell javac
536 * not to include any debugging information. This is the generated class
537 * file.
538 */
539 private final static byte genCodeTemplate[] = {-54, -2, -70, -66, 0, 0, 0,
540 49, 0, 22, 10, 0, 6, 0, 12, 7, 0, 13, 7, 0, 14, 10, 0, 2, 0, 15, 7,
541 0, 16, 7, 0, 18, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40,
542 41, 86, 1, 0, 4, 67, 111, 100, 101, 1, 0, 5, 118, 105, 115, 105,
543 116, 1, 0, 44, 40, 76, 111, 114, 103, 47, 103, 114, 111, 116, 104,
544 111, 102, 102, 47, 82, 117, 110, 97, 98, 111, 117, 116, 59, 76,
545 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99,
546 116, 59, 41, 86, 12, 0, 7, 0, 8, 1, 0, 28, 111, 114, 103, 47, 103,
547 114, 111, 116, 104, 111, 102, 102, 47, 82, 117, 110, 97, 98, 111,
548 117, 116, 69, 120, 97, 109, 112, 108, 101, 1, 0, 16, 106, 97, 118,
549 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 12, 0,
550 10, 0, 20, 1, 0, 28, 111, 114, 103, 47, 103, 114, 111, 116, 104,
551 111, 102, 102, 47, 71, 101, 110, 67, 111, 100, 101, 88, 88, 88, 88,
552 88, 88, 88, 88, 7, 0, 21, 1, 0, 26, 111, 114, 103, 47, 103, 114,
553 111, 116, 104, 111, 102, 102, 47, 82, 117, 110, 97, 98, 111, 117,
554 116, 36, 67, 111, 100, 101, 1, 0, 12, 73, 110, 110, 101, 114, 67,
555 108, 97, 115, 115, 101, 115, 1, 0, 21, 40, 76, 106, 97, 118, 97,
556 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 86,
557 1, 0, 21, 111, 114, 103, 47, 103, 114, 111, 116, 104, 111, 102,
558 102, 47, 82, 117, 110, 97, 98, 111, 117, 116, 0, 33, 0, 5, 0, 6, 0,
559 0, 0, 0, 0, 2, 0, 1, 0, 7, 0, 8, 0, 1, 0, 9, 0, 0, 0, 17, 0, 1, 0,
560 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 0, 0, 1, 0, 10, 0, 11,
561 0, 1, 0, 9, 0, 0, 0, 24, 0, 2, 0, 3, 0, 0, 0, 12, 43, -64, 0, 2,
562 44, -64, 0, 3, -74, 0, 4, -79, 0, 0, 0, 0, 0, 1, 0, 19, 0, 0, 0,
563 10, 0, 1, 0, 6, 0, 17, 0, 9, 4, 9}; // GenCodeXXXXXXXX.class
564
565 public static void main(String[] args) {
566 Runabout r = new Runabout() {
567 public void visit(String s) {
568 System.out.println("hi!!");
569 }
570 };
571 r.visitAppropriate("foo");
572 }
573
574} // end of Runabout
diff --git a/src/main/java/org/grothoff/package-info.java b/src/main/java/org/grothoff/package-info.java
new file mode 100644
index 0000000..2a8b8f0
--- /dev/null
+++ b/src/main/java/org/grothoff/package-info.java
@@ -0,0 +1,4 @@
1/**
2 * Pure java implementation of single argument multiple dispatch
3 */
4package org.grothoff;