1 package org.random.api;
2 
3 import com.google.gson.JsonArray;
4 import com.google.gson.JsonObject;
5 import com.google.gson.JsonParser;
6 import java.io.BufferedReader;
7 import java.io.DataOutputStream;
8 import java.io.IOException;
9 import java.io.InputStreamReader;
10 import java.net.MalformedURLException;
11 import java.net.URL;
12 import java.util.Calendar;
13 import java.util.GregorianCalendar;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.LinkedList;
17 import java.util.UUID;
18 import java.util.logging.Level;
19 import java.util.logging.Logger;
20 import javax.net.ssl.HttpsURLConnection;
21 import org.random.api.exception.RandomOrgBadHTTPResponseException;
22 import org.random.api.exception.RandomOrgInsufficientBitsError;
23 import org.random.api.exception.RandomOrgInsufficientRequestsError;
24 import org.random.api.exception.RandomOrgJSONRPCError;
25 import org.random.api.exception.RandomOrgKeyNotRunningError;
26 import org.random.api.exception.RandomOrgRANDOMORGError;
27 import org.random.api.exception.RandomOrgSendTimeoutException;
28 
29 /**
30  * RandomOrgClient main class through which API functions are accessed. * * This
31  * class provides either serialized or unserialized (determined on class
32  * creation) * access to both the signed and unsigned methods of the RANDOM.ORG
33  * API. These are * threadsafe and implemented as blocking remote procedure
34  * calls. * * If requests are to be issued serially a background Thread will
35  * maintain a Queue of * requests to process in sequence. * * The class also
36  * provides access to creation of a convenience class, RandomOrgCache, * for
37  * precaching API responses when the request is known in advance. * * This class
38  * will only allow the creation of one instance per API key. If an instance * of
39  * this class already exists for a given key, that instance will be returned
40  * instead * of a new instance. * * This class obeys most of the guidelines set
41  * forth in https://api.random.org/guidelines * All requests respect the
42  * server's advisoryDelay returned in any responses, or use * DEFAULT_DELAY if
43  * no advisoryDelay is returned. If the supplied API key is paused, i.e., * has
44  * exceeded its daily bit/request allowance, this implementation will back off
45  * until * midnight UTC. * * @see https://api.random.org/
46  *
47  ** @see http://code.google.com/p/google-gson/
48  ** @author Anders Haahr
49  *
50  */
51 public class RandomOrgClient {
52 
53     // Basic RANDOM.ORG API functions https://api.random.org/json-rpc/1/
54     private static final String INTEGER_METHOD = "generateIntegers";
55     private static final String DECIMAL_FRACTION_METHOD = "generateDecimalFractions";
56     private static final String GAUSSIAN_METHOD = "generateGaussians";
57     private static final String STRING_METHOD = "generateStrings";
58     private static final String UUID_METHOD = "generateUUIDs";
59     private static final String BLOB_METHOD = "generateBlobs";
60     private static final String GET_USAGE_METHOD = "getUsage";
61 
62     // Signed RANDOM.ORG API functions https://api.random.org/json-rpc/1/signing
63     private static final String SIGNED_INTEGER_METHOD = "generateSignedIntegers";
64     private static final String SIGNED_DECIMAL_FRACTION_METHOD = "generateSignedDecimalFractions";
65     private static final String SIGNED_GAUSSIAN_METHOD = "generateSignedGaussians";
66     private static final String SIGNED_STRING_METHOD = "generateSignedStrings";
67     private static final String SIGNED_UUID_METHOD = "generateSignedUUIDs";
68     private static final String SIGNED_BLOB_METHOD = "generateSignedBlobs";
69     private static final String VERIFY_SIGNATURE_METHOD = "verifySignature";
70 
71     // Blob format literals
72     public static final String BLOB_FORMAT_BASE64 = "base64";
73     public static final String BLOB_FORMAT_HEX = "hex";
74 
75     // Default back-off to use if no advisoryDelay back-off supplied by server (1 second)
76     private static final int DEFAULT_DELAY = 1 * 1000;
77 
78     // On request fetch fresh allowance state if current state data is older than this value (1 hour)
79     private static final int ALLOWANCE_STATE_REFRESH_SECONDS = 3600 * 1000;
80 
81     // default data sizes in bits
82     private static final int UUID_SIZE = 122;
83 
84     // Maintain a dictionary of API keys and their instances.
85     private static HashMap<String, RandomOrgClient> keyIndexedInstances = new HashMap<String, RandomOrgClient>();
86 
87     private static HashSet<Integer> randomOrgErrors = new HashSet<Integer>();
88 
89     private static final Logger LOGGER = Logger.getLogger(RandomOrgClient.class.getPackage().getName());
90 
91     static {
92         int[] ints = {100, 101, 200, 201, 202, 203, 204, 300, 301, 302, 303, 304, 400, 401, 402, 403, 500, 32000};
93         for (int i : ints) {
94             RandomOrgClient.randomOrgErrors.add(i);
95         }
96     }
97 
98     /**
99      * Ensure only one instance of RandomOrgClient exists per API key. Create a
100      * new instance if the * supplied key isn't already known, otherwise return
101      * the previously instantiated one. * New instance will have a
102      * blockingTimeout of 24*60*60*1000 milliseconds, i.e., 1 day, a httpTimeout
103      * of * 120*1000 milliseconds, and will issue serialized requests.
104      *
105      * *
106      * @param apiKey of instance to create/find, obtained from RANDOM.ORG, see:
107      * https://api.random.org/api-keys * * @return new instance if instance
108      * doesn't already exist for this key, else existing instance.
109      *
110      */
getRandomOrgClient(String apiKey)111     public static RandomOrgClient getRandomOrgClient(String apiKey) {
112         return RandomOrgClient.getRandomOrgClient(apiKey, 24 * 60 * 60 * 1000, 120 * 1000, true);
113     }
114 
115     /**
116      * Ensure only one instance of RandomOrgClient exists per API key. Create a
117      * new instance if the * supplied key isn't already known, otherwise return
118      * the previously instantiated one. * * @param apiKey of instance to
119      * create/find, obtained from RANDOM.ORG, see:
120      * https://api.random.org/api-keys
121      *
122      ** @param blockingTimeout maximum time in milliseconds to wait before
123      * being allowed to send a request. * Note this is a hint not a guarantee.
124      * Be advised advisory delay from server must always be obeyed. * Supply a
125      * value of -1 to allow blocking forever. (default 24*60*60*1000, i.e., 1
126      * day).
127      ** @param httpTimeout maximum time in milliseconds to wait for the server
128      * response to a request. (default 120*1000).
129      ** @param serialized determines whether or not requests from this instance
130      * will be added to a Queue and *	issued serially or sent when received,
131      * obeying any advisory delay (default true). * * @return new instance if
132      * instance doesn't already exist for this key, else existing instance.
133      *
134      */
getRandomOrgClient(String apiKey, long blockingTimeout, int httpTimeout, boolean serialized)135     public static RandomOrgClient getRandomOrgClient(String apiKey, long blockingTimeout, int httpTimeout, boolean serialized) {
136         RandomOrgClient instance = RandomOrgClient.keyIndexedInstances.get(apiKey);
137 
138         if (instance == null) {
139             instance = new RandomOrgClient(apiKey, blockingTimeout, httpTimeout, serialized);
140             RandomOrgClient.keyIndexedInstances.put(apiKey, instance);
141         }
142 
143         return instance;
144     }
145 
146     private String apiKey;
147     private long blockingTimeout;
148     private int httpTimeout;
149     private boolean serialized;
150 
151     // maintain info to obey server advisory delay
152     private Object advisoryDelayLock = new Object();
153     private int advisoryDelay = 0;
154     private long lastResponseReceivedTime = 0;
155 
156     // maintain usage statistics from server
157     private int requestsLeft = -1;
158     private int bitsLeft = -1;
159 
160     // Back-off info for when API key is detected as not running - probably because key
161     // has exceeded its daily usage limit. Back-off runs until midnight UTC.
162     private long backoff = -1;
163     private String backoffError;
164 
165     private LinkedList<HashMap<String, Object>> serializedQueue;
166 
167     /**
168      * Constructor. Initialize class and start serialized request sending Thread
169      * running as a daemon if applicable. * * @param apiKey of instance to
170      * create/find, obtained from RANDOM.ORG, see:
171      * https://api.random.org/api-keys
172      *
173      ** @param blockingTimeout maximum time in milliseconds to wait before
174      * being allowed to send a request. * Note this is a hint not a guarantee.
175      * Be advised advisory delay from server must always be obeyed. * Supply a
176      * value of -1 to allow blocking forever. (default 24*60*60*1000, i.e., 1
177      * day).
178      ** @param httpTimeout maximum time in milliseconds to wait for the server
179      * response to a request. (default 120*1000).
180      ** @param serialized determines whether or not requests from this instance
181      * will be added to a Queue and *	issued serially or sent when received,
182      * obeying any advisory delay (default true).
183      *
184      */
RandomOrgClient(String apiKey, long blockingTimeout, int httpTimeout, boolean serialized)185     private RandomOrgClient(String apiKey, long blockingTimeout, int httpTimeout, boolean serialized) {
186 
187         if (serialized) {
188             // set up the serialized request Queue and Thread
189             this.serializedQueue = new LinkedList<HashMap<String, Object>>();
190 
191             Thread t = new Thread(new Runnable() {
192                 @Override
193                 public void run() {
194                     RandomOrgClient.this.threadedRequestSending();
195                 }
196             });
197             t.setDaemon(true);
198             t.start();
199         }
200 
201         this.serialized = serialized;
202 
203         this.apiKey = apiKey;
204         this.blockingTimeout = blockingTimeout;
205         this.httpTimeout = httpTimeout;
206     }
207 
208     // Basic methods for generating randomness, see: https://api.random.org/json-rpc/1/basic
209     /**
210      * Request and return an array of true random integers within a user-defined
211      * range from the server. * See:
212      * https://api.random.org/json-rpc/1/basic#generateIntegers * * @param n how
213      * many random integers you need. Must be within the [1,1e4] range.
214      *
215      ** @param min the lower boundary for the range from which the random
216      * numbers will be picked. Must be within the [-1e9,1e9] range.
217      ** @param max the upper boundary for the range from which the random
218      * numbers will be picked. Must be within the [-1e9,1e9] range. * * @return
219      * array of random integers. * * @throws RandomOrgSendTimeoutException
220      * blocking timeout is exceeded before the request can be sent.
221      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
222      ** @throws RandomOrgInsufficientRequestsError API key's server requests
223      * allowance has been exceeded.
224      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
225      * has been exceeded.
226      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
227      * received.
228      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
229      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
230      ** @throws MalformedURLException in the unlikely event something goes
231      * wrong with URL creation. @see java.net.MalformedURLException
232      ** @throws IOException @see java.io.IOException
233      *
234      */
generateIntegers(int n, int min, int max)235     public int[] generateIntegers(int n, int min, int max) throws RandomOrgSendTimeoutException,
236             RandomOrgKeyNotRunningError,
237             RandomOrgInsufficientRequestsError,
238             RandomOrgInsufficientBitsError,
239             RandomOrgBadHTTPResponseException,
240             RandomOrgRANDOMORGError,
241             RandomOrgJSONRPCError,
242             MalformedURLException,
243             IOException {
244         return generateIntegers(n, min, max, true);
245     }
246 
247     /**
248      * Request and return an array of true random integers within a user-defined
249      * range from the server. * See:
250      * https://api.random.org/json-rpc/1/basic#generateIntegers * * @param n how
251      * many random integers you need. Must be within the [1,1e4] range.
252      *
253      ** @param min the lower boundary for the range from which the random
254      * numbers will be picked. Must be within the [-1e9,1e9] range.
255      ** @param max the upper boundary for the range from which the random
256      * numbers will be picked. Must be within the [-1e9,1e9] range.
257      ** @param replacement specifies whether the random numbers should be
258      * picked with replacement. *	If True the resulting numbers may contain
259      * duplicate values, otherwise the numbers will all be unique (default
260      * True). * * @return array of random integers. * * @throws
261      * RandomOrgSendTimeoutException blocking timeout is exceeded before the
262      * request can be sent.
263      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
264      ** @throws RandomOrgInsufficientRequestsError API key's server requests
265      * allowance has been exceeded.
266      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
267      * has been exceeded.
268      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
269      * received.
270      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
271      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
272      ** @throws MalformedURLException in the unlikely event something goes
273      * wrong with URL creation. @see java.net.MalformedURLException
274      ** @throws IOException @see java.io.IOException
275      *
276      */
generateIntegers(int n, int min, int max, boolean replacement)277     public int[] generateIntegers(int n, int min, int max, boolean replacement) throws RandomOrgSendTimeoutException,
278             RandomOrgKeyNotRunningError,
279             RandomOrgInsufficientRequestsError,
280             RandomOrgInsufficientBitsError,
281             RandomOrgBadHTTPResponseException,
282             RandomOrgRANDOMORGError,
283             RandomOrgJSONRPCError,
284             MalformedURLException,
285             IOException {
286 
287         JsonObject request = new JsonObject();
288 
289         request.addProperty("n", n);
290         request.addProperty("min", min);
291         request.addProperty("max", max);
292         request.addProperty("replacement", replacement);
293 
294         request = this.generateKeyedRequest(request, INTEGER_METHOD);
295 
296         JsonObject response = this.sendRequest(request);
297 
298         return this.extractInts(response);
299     }
300 
301     /**
302      * Request and return a list (size n) of true random decimal fractions, from
303      * a uniform distribution across * the [0,1] interval with a user-defined
304      * number of decimal places from the server. * See:
305      * https://api.random.org/json-rpc/1/basic#generateDecimalFractions
306      *
307      * *
308      * @param n how many random decimal fractions you need. Must be within the
309      * [1,1e4] range.
310      ** @param decimalPlaces the number of decimal places to use. Must be
311      * within the [1,20] range. * * @return array of random doubles. * * @throws
312      * RandomOrgSendTimeoutException blocking timeout is exceeded before the
313      * request can be sent.
314      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
315      ** @throws RandomOrgInsufficientRequestsError API key's server requests
316      * allowance has been exceeded.
317      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
318      * has been exceeded.
319      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
320      * received.
321      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
322      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
323      ** @throws MalformedURLException in the unlikely event something goes
324      * wrong with URL creation. @see java.net.MalformedURLException
325      ** @throws IOException @see java.io.IOException
326      *
327      */
generateDecimalFractions(int n, int decimalPlaces)328     public double[] generateDecimalFractions(int n, int decimalPlaces) throws RandomOrgSendTimeoutException,
329             RandomOrgKeyNotRunningError,
330             RandomOrgInsufficientRequestsError,
331             RandomOrgInsufficientBitsError,
332             RandomOrgBadHTTPResponseException,
333             RandomOrgRANDOMORGError,
334             RandomOrgJSONRPCError,
335             MalformedURLException,
336             IOException {
337         return this.generateDecimalFractions(n, decimalPlaces, true);
338     }
339 
340     /**
341      * Request and return a list (size n) of true random decimal fractions, from
342      * a uniform distribution across * the [0,1] interval with a user-defined
343      * number of decimal places from the server. * See:
344      * https://api.random.org/json-rpc/1/basic#generateDecimalFractions
345      *
346      * *
347      * @param n how many random decimal fractions you need. Must be within the
348      * [1,1e4] range.
349      ** @param decimalPlaces the number of decimal places to use. Must be
350      * within the [1,20] range.
351      ** @param replacement specifies whether the random numbers should be
352      * picked with replacement. *	If True the resulting numbers may contain
353      * duplicate values, otherwise the numbers will all be unique (default
354      * True). * * @return array of random doubles. * * @throws
355      * RandomOrgSendTimeoutException blocking timeout is exceeded before the
356      * request can be sent.
357      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
358      ** @throws RandomOrgInsufficientRequestsError API key's server requests
359      * allowance has been exceeded.
360      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
361      * has been exceeded.
362      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
363      * received.
364      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
365      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
366      ** @throws MalformedURLException in the unlikely event something goes
367      * wrong with URL creation. @see java.net.MalformedURLException
368      ** @throws IOException @see java.io.IOException
369      *
370      */
generateDecimalFractions(int n, int decimalPlaces, boolean replacement)371     public double[] generateDecimalFractions(int n, int decimalPlaces, boolean replacement) throws RandomOrgSendTimeoutException,
372             RandomOrgKeyNotRunningError,
373             RandomOrgInsufficientRequestsError,
374             RandomOrgInsufficientBitsError,
375             RandomOrgBadHTTPResponseException,
376             RandomOrgRANDOMORGError,
377             RandomOrgJSONRPCError,
378             MalformedURLException,
379             IOException {
380 
381         JsonObject request = new JsonObject();
382 
383         request.addProperty("n", n);
384         request.addProperty("decimalPlaces", decimalPlaces);
385         request.addProperty("replacement", replacement);
386 
387         request = this.generateKeyedRequest(request, DECIMAL_FRACTION_METHOD);
388 
389         JsonObject response = this.sendRequest(request);
390 
391         return this.extractDoubles(response);
392     }
393 
394     /**
395      * Request and return a list (size n) of true random numbers from a Gaussian
396      * distribution (also known as a * normal distribution). The form uses a
397      * Box-Muller Transform to generate the Gaussian distribution from *
398      * uniformly distributed numbers. See:
399      * https://api.random.org/json-rpc/1/basic#generateGaussians * * @param n
400      * how many random numbers you need. Must be within the [1,1e4] range.
401      *
402      ** @param mean the distribution's mean. Must be within the [-1e6,1e6]
403      * range.
404      ** @param standardDeviation the distribution's standard deviation. Must be
405      * within the [-1e6,1e6] range.
406      ** @param significantDigits the number of significant digits to use. Must
407      * be within the [2,20] range. * * @return array of true random doubles from
408      * a Gaussian distribution. * * @throws RandomOrgSendTimeoutException
409      * blocking timeout is exceeded before the request can be sent.
410      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
411      ** @throws RandomOrgInsufficientRequestsError API key's server requests
412      * allowance has been exceeded.
413      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
414      * has been exceeded.
415      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
416      * received.
417      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
418      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
419      ** @throws MalformedURLException in the unlikely event something goes
420      * wrong with URL creation. @see java.net.MalformedURLException
421      ** @throws IOException @see java.io.IOException
422      *
423      */
generateGaussians(int n, double mean, double standardDeviation, int significantDigits)424     public double[] generateGaussians(int n, double mean, double standardDeviation, int significantDigits) throws RandomOrgSendTimeoutException,
425             RandomOrgKeyNotRunningError,
426             RandomOrgInsufficientRequestsError,
427             RandomOrgInsufficientBitsError,
428             RandomOrgBadHTTPResponseException,
429             RandomOrgRANDOMORGError,
430             RandomOrgJSONRPCError,
431             MalformedURLException,
432             IOException {
433 
434         JsonObject request = new JsonObject();
435 
436         request.addProperty("n", n);
437         request.addProperty("mean", mean);
438         request.addProperty("standardDeviation", standardDeviation);
439         request.addProperty("significantDigits", significantDigits);
440 
441         request = this.generateKeyedRequest(request, GAUSSIAN_METHOD);
442 
443         JsonObject response = this.sendRequest(request);
444 
445         return this.extractDoubles(response);
446     }
447 
448     /**
449      * Request and return a list (size n) of true random unicode strings from
450      * the server. * See:
451      * https://api.random.org/json-rpc/1/basic#generateStrings * * @param n how
452      * many random strings you need. Must be within the [1,1e4] range.
453      *
454      ** @param length the length of each string. Must be within the [1,20]
455      * range. All strings will be of the same length.
456      ** @param characters a string that contains the set of characters that are
457      * allowed to occur in the random strings. *	The maximum number of
458      * characters is 80. * * @return array of random Strings. * * @throws
459      * RandomOrgSendTimeoutException blocking timeout is exceeded before the
460      * request can be sent.
461      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
462      ** @throws RandomOrgInsufficientRequestsError API key's server requests
463      * allowance has been exceeded.
464      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
465      * has been exceeded.
466      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
467      * received.
468      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
469      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
470      ** @throws MalformedURLException in the unlikely event something goes
471      * wrong with URL creation. @see java.net.MalformedURLException
472      ** @throws IOException @see java.io.IOException
473      *
474      */
generateStrings(int n, int length, String characters)475     public String[] generateStrings(int n, int length, String characters) throws RandomOrgSendTimeoutException,
476             RandomOrgKeyNotRunningError,
477             RandomOrgInsufficientRequestsError,
478             RandomOrgInsufficientBitsError,
479             RandomOrgBadHTTPResponseException,
480             RandomOrgRANDOMORGError,
481             RandomOrgJSONRPCError,
482             MalformedURLException,
483             IOException {
484         return this.generateStrings(n, length, characters, true);
485     }
486 
487     /**
488      * Request and return a list (size n) of true random unicode strings from
489      * the server. * See:
490      * https://api.random.org/json-rpc/1/basic#generateStrings * * @param n how
491      * many random strings you need. Must be within the [1,1e4] range.
492      *
493      ** @param length the length of each string. Must be within the [1,20]
494      * range. All strings will be of the same length.
495      ** @param characters a string that contains the set of characters that are
496      * allowed to occur in the random strings. *	The maximum number of
497      * characters is 80.
498      ** @param replacement specifies whether the random strings should be
499      * picked with replacement. If True the resulting *	list of strings may
500      * contain duplicates, otherwise the strings will all be unique (default
501      * True). * * @return array of random Strings. * * @throws
502      * RandomOrgSendTimeoutException blocking timeout is exceeded before the
503      * request can be sent.
504      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
505      ** @throws RandomOrgInsufficientRequestsError API key's server requests
506      * allowance has been exceeded.
507      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
508      * has been exceeded.
509      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
510      * received.
511      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
512      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
513      ** @throws MalformedURLException in the unlikely event something goes
514      * wrong with URL creation. @see java.net.MalformedURLException
515      ** @throws IOException @see java.io.IOException
516      *
517      */
generateStrings(int n, int length, String characters, boolean replacement)518     public String[] generateStrings(int n, int length, String characters, boolean replacement) throws RandomOrgSendTimeoutException,
519             RandomOrgKeyNotRunningError,
520             RandomOrgInsufficientRequestsError,
521             RandomOrgInsufficientBitsError,
522             RandomOrgBadHTTPResponseException,
523             RandomOrgRANDOMORGError,
524             RandomOrgJSONRPCError,
525             MalformedURLException,
526             IOException {
527 
528         JsonObject request = new JsonObject();
529 
530         request.addProperty("n", n);
531         request.addProperty("length", length);
532         request.addProperty("characters", characters);
533         request.addProperty("replacement", replacement);
534 
535         request = this.generateKeyedRequest(request, STRING_METHOD);
536 
537         JsonObject response = this.sendRequest(request);
538 
539         return this.extractStrings(response);
540     }
541 
542     /**
543      * Request and return a list (size n) of version 4 true random Universally
544      * Unique IDentifiers (UUIDs) in accordance * with section 4.4 of RFC 4122,
545      * from the server. See:
546      * https://api.random.org/json-rpc/1/basic#generateUUIDs * * @param n how
547      * many random UUIDs you need. Must be within the [1,1e3] range. * * @return
548      * array of random UUIDs. * * @throws RandomOrgSendTimeoutException blocking
549      * timeout is exceeded before the request can be sent.
550      *
551      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
552      ** @throws RandomOrgInsufficientRequestsError API key's server requests
553      * allowance has been exceeded.
554      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
555      * has been exceeded.
556      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
557      * received.
558      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
559      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
560      ** @throws MalformedURLException in the unlikely event something goes
561      * wrong with URL creation. @see java.net.MalformedURLException
562      ** @throws IOException @see java.io.IOException
563      *
564      */
generateUUIDs(int n)565     public UUID[] generateUUIDs(int n) throws RandomOrgSendTimeoutException,
566             RandomOrgKeyNotRunningError,
567             RandomOrgInsufficientRequestsError,
568             RandomOrgInsufficientBitsError,
569             RandomOrgBadHTTPResponseException,
570             RandomOrgRANDOMORGError,
571             RandomOrgJSONRPCError,
572             MalformedURLException,
573             IOException {
574 
575         JsonObject request = new JsonObject();
576 
577         request.addProperty("n", n);
578 
579         request = this.generateKeyedRequest(request, UUID_METHOD);
580 
581         JsonObject response = this.sendRequest(request);
582 
583         return this.extractUUIDs(response);
584     }
585 
586     /**
587      * Request and return a list (size n) of Binary Large OBjects (BLOBs) as
588      * unicode strings * containing true random data from the server. See:
589      * https://api.random.org/json-rpc/1/basic#generateBlobs * * @param n how
590      * many random blobs you need. Must be within the [1,100] range.
591      *
592      ** @param size the size of each blob, measured in bits. Must be within the
593      * [1,1048576] range and must be divisible by 8. * * @return array of random
594      * blobs as Strings. * * @throws RandomOrgSendTimeoutException blocking
595      * timeout is exceeded before the request can be sent.
596      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
597      ** @throws RandomOrgInsufficientRequestsError API key's server requests
598      * allowance has been exceeded.
599      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
600      * has been exceeded.
601      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
602      * received.
603      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
604      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
605      ** @throws MalformedURLException in the unlikely event something goes
606      * wrong with URL creation. @see java.net.MalformedURLException
607      ** @throws IOException @see java.io.IOException
608      *
609      */
generateBlobs(int n, int size)610     public String[] generateBlobs(int n, int size) throws RandomOrgSendTimeoutException,
611             RandomOrgKeyNotRunningError,
612             RandomOrgInsufficientRequestsError,
613             RandomOrgInsufficientBitsError,
614             RandomOrgBadHTTPResponseException,
615             RandomOrgRANDOMORGError,
616             RandomOrgJSONRPCError,
617             MalformedURLException,
618             IOException {
619 
620         return this.generateBlobs(n, size, RandomOrgClient.BLOB_FORMAT_BASE64);
621     }
622 
623     /**
624      * Request and return a list (size n) of Binary Large OBjects (BLOBs) as
625      * unicode strings * containing true random data from the server. See:
626      * https://api.random.org/json-rpc/1/basic#generateBlobs * * @param n how
627      * many random blobs you need. Must be within the [1,100] range.
628      *
629      ** @param size the size of each blob, measured in bits. Must be within the
630      * [1,1048576] range and must be divisible by 8.
631      ** @param format specifies the format in which the blobs will be returned.
632      * Values allowed are * BLOB_FORMAT_BASE64 and BLOB_FORMAT_HEX (default
633      * BLOB_FORMAT_BASE64). * * @return array of random blobs as Strings.
634      *
635      * @throws RandomOrgSendTimeoutException blocking timeout is exceeded before
636      * the request can be sent.
637      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
638      ** @throws RandomOrgInsufficientRequestsError API key's server requests
639      * allowance has been exceeded.
640      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
641      * has been exceeded.
642      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
643      * received.
644      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
645      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
646      ** @throws MalformedURLException in the unlikely event something goes
647      * wrong with URL creation. @see java.net.MalformedURLException
648      ** @throws IOException @see java.io.IOException
649      *
650      */
generateBlobs(int n, int size, String format)651     public String[] generateBlobs(int n, int size, String format) throws RandomOrgSendTimeoutException,
652             RandomOrgKeyNotRunningError,
653             RandomOrgInsufficientRequestsError,
654             RandomOrgInsufficientBitsError,
655             RandomOrgBadHTTPResponseException,
656             RandomOrgRANDOMORGError,
657             RandomOrgJSONRPCError,
658             MalformedURLException,
659             IOException {
660 
661         JsonObject request = new JsonObject();
662 
663         request.addProperty("n", n);
664         request.addProperty("size", size);
665         request.addProperty("format", format);
666 
667         request = this.generateKeyedRequest(request, BLOB_METHOD);
668 
669         JsonObject response = this.sendRequest(request);
670 
671         return this.extractStrings(response);
672     }
673 
674     // Signed methods for generating randomness, see: https://api.random.org/json-rpc/1/signing
675     /**
676      * Request a list (size n) of true random integers within a user-defined
677      * range from the server. Returns a * dictionary object with the parsed
678      * integer list mapped to 'data', the original response mapped to 'random',
679      * * and the response's signature mapped to 'signature'. See:
680      * https://api.random.org/json-rpc/1/signing#generateSignedIntegers
681      *
682      * *
683      * @param n how many random integers you need. Must be within the [1,1e4]
684      * range.
685      ** @param min the lower boundary for the range from which the random
686      * numbers will be picked. Must be within the [-1e9,1e9] range.
687      ** @param max the upper boundary for the range from which the random
688      * numbers will be picked. Must be within the [-1e9,1e9] range. * * @return
689      * HashMap with "random": random JsonObject, "signature": signature String,
690      * "data": random int[] * * @throws RandomOrgSendTimeoutException blocking
691      * timeout is exceeded before the request can be sent.
692      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
693      ** @throws RandomOrgInsufficientRequestsError API key's server requests
694      * allowance has been exceeded.
695      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
696      * has been exceeded.
697      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
698      * received.
699      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
700      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
701      ** @throws MalformedURLException in the unlikely event something goes
702      * wrong with URL creation. @see java.net.MalformedURLException
703      ** @throws IOException @see java.io.IOException
704      *
705      */
generateSignedIntegers(int n, int min, int max)706     public HashMap<String, Object> generateSignedIntegers(int n, int min, int max) throws RandomOrgSendTimeoutException,
707             RandomOrgKeyNotRunningError,
708             RandomOrgInsufficientRequestsError,
709             RandomOrgInsufficientBitsError,
710             RandomOrgBadHTTPResponseException,
711             RandomOrgRANDOMORGError,
712             RandomOrgJSONRPCError,
713             MalformedURLException,
714             IOException {
715         return generateSignedIntegers(n, min, max, true);
716     }
717 
718     /**
719      * Request a list (size n) of true random integers within a user-defined
720      * range from the server. Returns a * dictionary object with the parsed
721      * integer list mapped to 'data', the original response mapped to 'random',
722      * * and the response's signature mapped to 'signature'. See:
723      * https://api.random.org/json-rpc/1/signing#generateSignedIntegers
724      *
725      * *
726      * @param n how many random integers you need. Must be within the [1,1e4]
727      * range.
728      ** @param min the lower boundary for the range from which the random
729      * numbers will be picked. Must be within the [-1e9,1e9] range.
730      ** @param max the upper boundary for the range from which the random
731      * numbers will be picked. Must be within the [-1e9,1e9] range.
732      ** @param replacement specifies whether the random numbers should be
733      * picked with replacement. *	If True the resulting numbers may contain
734      * duplicate values, otherwise the numbers will all be unique (default
735      * True). * * @return HashMap with "random": random JsonObject, "signature":
736      * signature String, "data": random int[] * * @throws
737      * RandomOrgSendTimeoutException blocking timeout is exceeded before the
738      * request can be sent.
739      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
740      ** @throws RandomOrgInsufficientRequestsError API key's server requests
741      * allowance has been exceeded.
742      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
743      * has been exceeded.
744      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
745      * received.
746      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
747      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
748      ** @throws MalformedURLException in the unlikely event something goes
749      * wrong with URL creation. @see java.net.MalformedURLException
750      ** @throws IOException @see java.io.IOException
751      *
752      */
generateSignedIntegers(int n, int min, int max, boolean replacement)753     public HashMap<String, Object> generateSignedIntegers(int n, int min, int max, boolean replacement) throws RandomOrgSendTimeoutException,
754             RandomOrgKeyNotRunningError,
755             RandomOrgInsufficientRequestsError,
756             RandomOrgInsufficientBitsError,
757             RandomOrgBadHTTPResponseException,
758             RandomOrgRANDOMORGError,
759             RandomOrgJSONRPCError,
760             MalformedURLException,
761             IOException {
762 
763         JsonObject request = new JsonObject();
764 
765         request.addProperty("n", n);
766         request.addProperty("min", min);
767         request.addProperty("max", max);
768         request.addProperty("replacement", replacement);
769 
770         request = this.generateKeyedRequest(request, SIGNED_INTEGER_METHOD);
771 
772         JsonObject response = this.sendRequest(request);
773 
774         HashMap<String, Object> result = new HashMap<String, Object>();
775         result.put("data", this.extractInts(response));
776 
777         return this.extractSignedResponse(response, result);
778     }
779 
780     /**
781      * Request a list (size n) of true random decimal fractions, from a uniform
782      * distribution across the [0,1] interval * with a user-defined number of
783      * decimal places from the server. Returns a dictionary object with the
784      * parsed decimal * fraction list mapped to 'data', the original response
785      * mapped to 'random', and the response's signature mapped to * 'signature'.
786      * See:
787      * https://api.random.org/json-rpc/1/signing#generateSignedDecimalFractions
788      *
789      * * * @param n how many random decimal fractions you need. Must be within
790      * the [1,1e4] range.
791      ** @param decimalPlaces the number of decimal places to use. Must be
792      * within the [1,20] range. * * @return HashMap with "random": random
793      * JsonObject, "signature": signature String, "data": random double[]
794      *
795      * @throws RandomOrgSendTimeoutException blocking timeout is exceeded before
796      * the request can be sent.
797      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
798      ** @throws RandomOrgInsufficientRequestsError API key's server requests
799      * allowance has been exceeded.
800      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
801      * has been exceeded.
802      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
803      * received.
804      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
805      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
806      ** @throws MalformedURLException in the unlikely event something goes
807      * wrong with URL creation. @see java.net.MalformedURLException
808      ** @throws IOException @see java.io.IOException
809      *
810      */
generateSignedDecimalFractions(int n, int decimalPlaces)811     public HashMap<String, Object> generateSignedDecimalFractions(int n, int decimalPlaces) throws RandomOrgSendTimeoutException,
812             RandomOrgKeyNotRunningError,
813             RandomOrgInsufficientRequestsError,
814             RandomOrgInsufficientBitsError,
815             RandomOrgBadHTTPResponseException,
816             RandomOrgRANDOMORGError,
817             RandomOrgJSONRPCError,
818             MalformedURLException,
819             IOException {
820 
821         return this.generateSignedDecimalFractions(n, decimalPlaces, true);
822     }
823 
824     /**
825      * Request a list (size n) of true random decimal fractions, from a uniform
826      * distribution across the [0,1] interval * with a user-defined number of
827      * decimal places from the server. Returns a dictionary object with the
828      * parsed decimal * fraction list mapped to 'data', the original response
829      * mapped to 'random', and the response's signature mapped to * 'signature'.
830      * See:
831      * https://api.random.org/json-rpc/1/signing#generateSignedDecimalFractions
832      *
833      * * * @param n how many random decimal fractions you need. Must be within
834      * the [1,1e4] range.
835      ** @param decimalPlaces the number of decimal places to use. Must be
836      * within the [1,20] range.
837      ** @param replacement specifies whether the random numbers should be
838      * picked with replacement. *	If True the resulting numbers may contain
839      * duplicate values, otherwise the numbers will all be unique (default
840      * True). * * @return HashMap with "random": random JsonObject, "signature":
841      * signature String, "data": random double[] * * @throws
842      * RandomOrgSendTimeoutException blocking timeout is exceeded before the
843      * request can be sent.
844      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
845      ** @throws RandomOrgInsufficientRequestsError API key's server requests
846      * allowance has been exceeded.
847      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
848      * has been exceeded.
849      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
850      * received.
851      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
852      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
853      ** @throws MalformedURLException in the unlikely event something goes
854      * wrong with URL creation. @see java.net.MalformedURLException
855      ** @throws IOException @see java.io.IOException
856      *
857      */
generateSignedDecimalFractions(int n, int decimalPlaces, boolean replacement)858     public HashMap<String, Object> generateSignedDecimalFractions(int n, int decimalPlaces, boolean replacement) throws RandomOrgSendTimeoutException,
859             RandomOrgKeyNotRunningError,
860             RandomOrgInsufficientRequestsError,
861             RandomOrgInsufficientBitsError,
862             RandomOrgBadHTTPResponseException,
863             RandomOrgRANDOMORGError,
864             RandomOrgJSONRPCError,
865             MalformedURLException,
866             IOException {
867 
868         JsonObject request = new JsonObject();
869 
870         request.addProperty("n", n);
871         request.addProperty("decimalPlaces", decimalPlaces);
872         request.addProperty("replacement", replacement);
873 
874         request = this.generateKeyedRequest(request, SIGNED_DECIMAL_FRACTION_METHOD);
875 
876         JsonObject response = this.sendRequest(request);
877 
878         HashMap<String, Object> result = new HashMap<String, Object>();
879         result.put("data", this.extractDoubles(response));
880 
881         return this.extractSignedResponse(response, result);
882     }
883 
884     /**
885      * Request a list (size n) of true random numbers from a Gaussian
886      * distribution (also known as a normal distribution). * The form uses a
887      * Box-Muller Transform to generate the Gaussian distribution from uniformly
888      * distributed numbers. * Returns a dictionary object with the parsed random
889      * number list mapped to 'data', the original response mapped to 'random', *
890      * and the response's signature mapped to 'signature'. See:
891      * https://api.random.org/json-rpc/1/signing#generateSignedGaussians
892      *
893      * *
894      * @param n how many random numbers you need. Must be within the [1,1e4]
895      * range.
896      ** @param mean the distribution's mean. Must be within the [-1e6,1e6]
897      * range.
898      ** @param standardDeviation the distribution's standard deviation. Must be
899      * within the [-1e6,1e6] range.
900      ** @param significantDigits the number of significant digits to use. Must
901      * be within the [2,20] range. * * @return HashMap with "random": random
902      * JsonObject, "signature": signature String, "data": random double[]
903      *
904      * @throws RandomOrgSendTimeoutException blocking timeout is exceeded before
905      * the request can be sent.
906      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
907      ** @throws RandomOrgInsufficientRequestsError API key's server requests
908      * allowance has been exceeded.
909      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
910      * has been exceeded.
911      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
912      * received.
913      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
914      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
915      ** @throws MalformedURLException in the unlikely event something goes
916      * wrong with URL creation. @see java.net.MalformedURLException
917      ** @throws IOException @see java.io.IOException
918      *
919      */
generateSignedGaussians(int n, double mean, double standardDeviation, int significantDigits)920     public HashMap<String, Object> generateSignedGaussians(int n, double mean, double standardDeviation, int significantDigits) throws RandomOrgSendTimeoutException,
921             RandomOrgKeyNotRunningError,
922             RandomOrgInsufficientRequestsError,
923             RandomOrgInsufficientBitsError,
924             RandomOrgBadHTTPResponseException,
925             RandomOrgRANDOMORGError,
926             RandomOrgJSONRPCError,
927             MalformedURLException,
928             IOException {
929 
930         JsonObject request = new JsonObject();
931 
932         request.addProperty("n", n);
933         request.addProperty("mean", mean);
934         request.addProperty("standardDeviation", standardDeviation);
935         request.addProperty("significantDigits", significantDigits);
936 
937         request = this.generateKeyedRequest(request, SIGNED_GAUSSIAN_METHOD);
938 
939         JsonObject response = this.sendRequest(request);
940 
941         HashMap<String, Object> result = new HashMap<String, Object>();
942         result.put("data", this.extractDoubles(response));
943 
944         return this.extractSignedResponse(response, result);
945     }
946 
947     /**
948      * Request a list (size n) of true random strings from the server. Returns a
949      * dictionary object with the parsed random * string list mapped to 'data',
950      * the original response mapped to 'random', and the response's signature
951      * mapped to 'signature'. * See:
952      * https://api.random.org/json-rpc/1/signing#generateSignedStrings
953      *
954      * *
955      * @param n how many random strings you need. Must be within the [1,1e4]
956      * range.
957      ** @param length the length of each string. Must be within the [1,20]
958      * range. All strings will be of the same length.
959      ** @param characters a string that contains the set of characters that are
960      * allowed to occur in the random strings. *	The maximum number of
961      * characters is 80. * * @return HashMap with "random": random JsonObject,
962      * "signature": signature String, "data": random String[] * * @throws
963      * RandomOrgSendTimeoutException blocking timeout is exceeded before the
964      * request can be sent.
965      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
966      ** @throws RandomOrgInsufficientRequestsError API key's server requests
967      * allowance has been exceeded.
968      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
969      * has been exceeded.
970      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
971      * received.
972      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
973      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
974      ** @throws MalformedURLException in the unlikely event something goes
975      * wrong with URL creation. @see java.net.MalformedURLException
976      ** @throws IOException @see java.io.IOException
977      *
978      */
generateSignedStrings(int n, int length, String characters)979     public HashMap<String, Object> generateSignedStrings(int n, int length, String characters) throws RandomOrgSendTimeoutException,
980             RandomOrgKeyNotRunningError,
981             RandomOrgInsufficientRequestsError,
982             RandomOrgInsufficientBitsError,
983             RandomOrgBadHTTPResponseException,
984             RandomOrgRANDOMORGError,
985             RandomOrgJSONRPCError,
986             MalformedURLException,
987             IOException {
988         return this.generateSignedStrings(n, length, characters, true);
989     }
990 
991     /**
992      * Request a list (size n) of true random strings from the server. Returns a
993      * dictionary object with the parsed random * string list mapped to 'data',
994      * the original response mapped to 'random', and the response's signature
995      * mapped to 'signature'. * See:
996      * https://api.random.org/json-rpc/1/signing#generateSignedStrings
997      *
998      * *
999      * @param n how many random strings you need. Must be within the [1,1e4]
1000      * range.
1001      ** @param length the length of each string. Must be within the [1,20]
1002      * range. All strings will be of the same length.
1003      ** @param characters a string that contains the set of characters that are
1004      * allowed to occur in the random strings. *	The maximum number of
1005      * characters is 80.
1006      ** @param replacement specifies whether the random strings should be
1007      * picked with replacement. If True the resulting *	list of strings may
1008      * contain duplicates, otherwise the strings will all be unique (default
1009      * True). * * @return HashMap with "random": random JsonObject, "signature":
1010      * signature String, "data": random String[] * * @throws
1011      * RandomOrgSendTimeoutException blocking timeout is exceeded before the
1012      * request can be sent.
1013      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
1014      ** @throws RandomOrgInsufficientRequestsError API key's server requests
1015      * allowance has been exceeded.
1016      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
1017      * has been exceeded.
1018      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
1019      * received.
1020      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
1021      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
1022      ** @throws MalformedURLException in the unlikely event something goes
1023      * wrong with URL creation. @see java.net.MalformedURLException
1024      ** @throws IOException @see java.io.IOException
1025      *
1026      */
generateSignedStrings(int n, int length, String characters, boolean replacement)1027     public HashMap<String, Object> generateSignedStrings(int n, int length, String characters, boolean replacement) throws RandomOrgSendTimeoutException,
1028             RandomOrgKeyNotRunningError,
1029             RandomOrgInsufficientRequestsError,
1030             RandomOrgInsufficientBitsError,
1031             RandomOrgBadHTTPResponseException,
1032             RandomOrgRANDOMORGError,
1033             RandomOrgJSONRPCError,
1034             MalformedURLException,
1035             IOException {
1036 
1037         JsonObject request = new JsonObject();
1038 
1039         request.addProperty("n", n);
1040         request.addProperty("length", length);
1041         request.addProperty("characters", characters);
1042         request.addProperty("replacement", replacement);
1043 
1044         request = this.generateKeyedRequest(request, SIGNED_STRING_METHOD);
1045 
1046         JsonObject response = this.sendRequest(request);
1047 
1048         HashMap<String, Object> result = new HashMap<String, Object>();
1049         result.put("data", this.extractStrings(response));
1050 
1051         return this.extractSignedResponse(response, result);
1052     }
1053 
1054     /**
1055      * Request a list (size n) of version 4 true random Universally Unique
1056      * IDentifiers (UUIDs) in accordance with * section 4.4 of RFC 4122, from
1057      * the server. Returns a dictionary object with the parsed random UUID list
1058      * mapped * to 'data', the original response mapped to 'random', and the
1059      * response's signature mapped to 'signature'. * See:
1060      * https://api.random.org/json-rpc/1/signing#generateSignedUUIDs * * @param
1061      * n how many random UUIDs you need. Must be within the [1,1e3] range.
1062      *
1063      * *
1064      * @return HashMap with "random": random JsonObject, "signature": signature
1065      * String, "data": random UUID[] * * @throws RandomOrgSendTimeoutException
1066      * blocking timeout is exceeded before the request can be sent.
1067      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
1068      ** @throws RandomOrgInsufficientRequestsError API key's server requests
1069      * allowance has been exceeded.
1070      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
1071      * has been exceeded.
1072      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
1073      * received.
1074      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
1075      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
1076      ** @throws MalformedURLException in the unlikely event something goes
1077      * wrong with URL creation. @see java.net.MalformedURLException
1078      ** @throws IOException @see java.io.IOException
1079      *
1080      */
generateSignedUUIDs(int n)1081     public HashMap<String, Object> generateSignedUUIDs(int n) throws RandomOrgSendTimeoutException,
1082             RandomOrgKeyNotRunningError,
1083             RandomOrgInsufficientRequestsError,
1084             RandomOrgInsufficientBitsError,
1085             RandomOrgBadHTTPResponseException,
1086             RandomOrgRANDOMORGError,
1087             RandomOrgJSONRPCError,
1088             MalformedURLException,
1089             IOException {
1090 
1091         JsonObject request = new JsonObject();
1092 
1093         request.addProperty("n", n);
1094 
1095         request = this.generateKeyedRequest(request, SIGNED_UUID_METHOD);
1096 
1097         JsonObject response = this.sendRequest(request);
1098 
1099         HashMap<String, Object> result = new HashMap<String, Object>();
1100         result.put("data", this.extractUUIDs(response));
1101 
1102         return this.extractSignedResponse(response, result);
1103     }
1104 
1105     /**
1106      * Request a list (size n) of Binary Large OBjects (BLOBs) containing true
1107      * random data from the server. Returns a * dictionary object with the
1108      * parsed random BLOB list mapped to 'data', the original response mapped to
1109      * 'random', * and the response's signature mapped to 'signature'. See:
1110      * https://api.random.org/json-rpc/1/signing#generateSignedBlobs * * @param
1111      * n how many random blobs you need. Must be within the [1,100] range.
1112      *
1113      ** @param size the size of each blob, measured in bits. Must be within the
1114      * [1,1048576] range and must be divisible by 8. * * @return HashMap with
1115      * "random": random JsonObject, "signature": signature String, "data":
1116      * random String[] * * @throws RandomOrgSendTimeoutException blocking
1117      * timeout is exceeded before the request can be sent.
1118      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
1119      ** @throws RandomOrgInsufficientRequestsError API key's server requests
1120      * allowance has been exceeded.
1121      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
1122      * has been exceeded.
1123      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
1124      * received.
1125      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
1126      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
1127      ** @throws MalformedURLException in the unlikely event something goes
1128      * wrong with URL creation. @see java.net.MalformedURLException
1129      ** @throws IOException @see java.io.IOException
1130      *
1131      */
generateSignedBlobs(int n, int size)1132     public HashMap<String, Object> generateSignedBlobs(int n, int size) throws RandomOrgSendTimeoutException,
1133             RandomOrgKeyNotRunningError,
1134             RandomOrgInsufficientRequestsError,
1135             RandomOrgInsufficientBitsError,
1136             RandomOrgBadHTTPResponseException,
1137             RandomOrgRANDOMORGError,
1138             RandomOrgJSONRPCError,
1139             MalformedURLException,
1140             IOException {
1141 
1142         return this.generateSignedBlobs(n, size, RandomOrgClient.BLOB_FORMAT_BASE64);
1143     }
1144 
1145     /**
1146      * Request a list (size n) of Binary Large OBjects (BLOBs) containing true
1147      * random data from the server. Returns a * dictionary object with the
1148      * parsed random BLOB list mapped to 'data', the original response mapped to
1149      * 'random', * and the response's signature mapped to 'signature'. See:
1150      * https://api.random.org/json-rpc/1/signing#generateSignedBlobs * * @param
1151      * n how many random blobs you need. Must be within the [1,100] range.
1152      *
1153      ** @param size the size of each blob, measured in bits. Must be within the
1154      * [1,1048576] range and must be divisible by 8.
1155      ** @param format specifies the format in which the blobs will be returned.
1156      * Values allowed are * BLOB_FORMAT_BASE64 and BLOB_FORMAT_HEX (default
1157      * BLOB_FORMAT_BASE64). * * @return HashMap with "random": random
1158      * JsonObject, "signature": signature String, "data": random String[]
1159      *
1160      * @throws RandomOrgSendTimeoutException blocking timeout is exceeded before
1161      * the request can be sent.
1162      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
1163      ** @throws RandomOrgInsufficientRequestsError API key's server requests
1164      * allowance has been exceeded.
1165      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
1166      * has been exceeded.
1167      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
1168      * received.
1169      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
1170      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
1171      ** @throws MalformedURLException in the unlikely event something goes
1172      * wrong with URL creation. @see java.net.MalformedURLException
1173      ** @throws IOException @see java.io.IOException
1174      *
1175      */
generateSignedBlobs(int n, int size, String format)1176     public HashMap<String, Object> generateSignedBlobs(int n, int size, String format) throws RandomOrgSendTimeoutException,
1177             RandomOrgKeyNotRunningError,
1178             RandomOrgInsufficientRequestsError,
1179             RandomOrgInsufficientBitsError,
1180             RandomOrgBadHTTPResponseException,
1181             RandomOrgRANDOMORGError,
1182             RandomOrgJSONRPCError,
1183             MalformedURLException,
1184             IOException {
1185 
1186         JsonObject request = new JsonObject();
1187 
1188         request.addProperty("n", n);
1189         request.addProperty("size", size);
1190         request.addProperty("format", format);
1191 
1192         request = this.generateKeyedRequest(request, SIGNED_BLOB_METHOD);
1193 
1194         JsonObject response = this.sendRequest(request);
1195 
1196         HashMap<String, Object> result = new HashMap<String, Object>();
1197         result.put("data", this.extractStrings(response));
1198 
1199         return this.extractSignedResponse(response, result);
1200     }
1201 
1202     // Signature verification for signed methods, see: https://api.random.org/json-rpc/1/signing
1203     /**
1204      * Verify the signature of a response previously received from one of the
1205      * methods in the Signed API with the * server. This is used to examine the
1206      * authenticity of numbers. Return True on verification success. * See:
1207      * https://api.random.org/json-rpc/1/signing#verifySignature * * @param
1208      * random the random field from a response returned by RANDOM.ORG through
1209      * one of the Signed API methods.
1210      *
1211      ** @param signature the signature field from the same response that the
1212      * random field originates from. * * @return verification success. * @throws
1213      * RandomOrgSendTimeoutException blocking timeout is exceeded before the
1214      * request can be sent.
1215      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
1216      ** @throws RandomOrgInsufficientRequestsError API key's server requests
1217      * allowance has been exceeded.
1218      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
1219      * has been exceeded.
1220      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
1221      * received.
1222      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
1223      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
1224      ** @throws MalformedURLException in the unlikely event something goes
1225      * wrong with URL creation. @see java.net.MalformedURLException
1226      ** @throws IOException @see java.io.IOException
1227      *
1228      */
verifySignature(JsonObject random, String signature)1229     public boolean verifySignature(JsonObject random, String signature) throws RandomOrgSendTimeoutException,
1230             RandomOrgKeyNotRunningError,
1231             RandomOrgInsufficientRequestsError,
1232             RandomOrgInsufficientBitsError,
1233             RandomOrgBadHTTPResponseException,
1234             RandomOrgRANDOMORGError,
1235             RandomOrgJSONRPCError,
1236             MalformedURLException,
1237             IOException {
1238 
1239         JsonObject request = new JsonObject();
1240 
1241         request.add("random", random);
1242         request.addProperty("signature", signature);
1243 
1244         request = this.generateRequest(request, VERIFY_SIGNATURE_METHOD);
1245 
1246         JsonObject response = this.sendRequest(request);
1247 
1248         return this.extractVerificationResponse(response);
1249     }
1250 
1251     // Methods used to create a cache for any given randomness request.
1252     /**
1253      * Get a RandomOrgCache to obtain random integers. The RandomOrgCache can be
1254      * polled for new results conforming to * the output format of the input
1255      * request. RandomOrgCache type is same as expected return value. * * @param
1256      * n how many random integers you need. Must be within the [1,1e4] range.
1257      *
1258      ** @param min the lower boundary for the range from which the random
1259      * numbers will be picked. Must be within the [-1e9,1e9] range.
1260      ** @param max the upper boundary for the range from which the random
1261      * numbers will be picked. Must be within the [-1e9,1e9] range. * * @return
1262      * RandomOrgCache<int[]>
1263      *
1264      */
createIntegerCache(int n, int min, int max)1265     public RandomOrgCache<int[]> createIntegerCache(int n, int min, int max) {
1266         return this.createIntegerCache(n, min, max, true, 20);
1267     }
1268 
1269     /**
1270      * Get a RandomOrgCache to obtain random integers. The RandomOrgCache can be
1271      * polled for new results conforming to * the output format of the input
1272      * request. RandomOrgCache type is same as expected return value. * * @param
1273      * n how many random integers you need. Must be within the [1,1e4] range.
1274      *
1275      ** @param min the lower boundary for the range from which the random
1276      * numbers will be picked. Must be within the [-1e9,1e9] range.
1277      ** @param max the upper boundary for the range from which the random
1278      * numbers will be picked. Must be within the [-1e9,1e9] range.
1279      ** @param replacement specifies whether the random numbers should be
1280      * picked with replacement. *	If True the resulting numbers may contain
1281      * duplicate values, otherwise the numbers will all be unique (default
1282      * True).
1283      ** @param cacheSize number of result-sets for the cache to try to maintain
1284      * at any given time (default 20, minimum 2). * * @return
1285      * RandomOrgCache<int[]>
1286      *
1287      */
createIntegerCache(int n, int min, int max, boolean replacement, int cacheSize)1288     public RandomOrgCache<int[]> createIntegerCache(int n, int min, int max, boolean replacement, int cacheSize) {
1289         if (cacheSize < 2) {
1290             cacheSize = 2;
1291         }
1292 
1293         JsonObject request = new JsonObject();
1294 
1295         request.addProperty("min", min);
1296         request.addProperty("max", max);
1297         request.addProperty("replacement", replacement);
1298 
1299         int bulkN = 0;
1300 
1301         // If possible, make requests more efficient by bulk-ordering from the server.
1302         // initially set at cache_size/2, but cache will auto-shrink bulk request size if requests can't be fulfilled.
1303         if (replacement) {
1304             bulkN = cacheSize / 2;
1305             request.addProperty("n", bulkN * n);
1306 
1307             // not possible to make the request more efficient
1308         } else {
1309             request.addProperty("n", n);
1310         }
1311 
1312         // get the request object for use in all requests from this cache
1313         request = this.generateKeyedRequest(request, INTEGER_METHOD);
1314 
1315         // max single request size, in bits, for adjusting bulk requests later
1316         int maxRequestSize = (int) Math.ceil(Math.log(max - min + 1) / Math.log(2) * n);
1317 
1318         return new RandomOrgCache<int[]>(
1319                 new JsonObjectInputCallable<JsonObject>() {
1320             @Override
1321             public JsonObject call() throws RandomOrgSendTimeoutException, RandomOrgKeyNotRunningError, RandomOrgInsufficientRequestsError, RandomOrgInsufficientBitsError, RandomOrgBadHTTPResponseException, RandomOrgRANDOMORGError, RandomOrgJSONRPCError, MalformedURLException, IOException {
1322                 return RandomOrgClient.this.sendRequest(this.input);
1323             }
1324         }, new JsonObjectInputCallable<int[]>() {
1325             @Override
1326             public int[] call() {
1327                 return RandomOrgClient.this.extractInts(this.input);
1328             }
1329         },
1330                 request, cacheSize, bulkN, n, maxRequestSize);
1331     }
1332 
1333     /**
1334      * Get a RandomOrgCache to obtain random decimal fractions. The
1335      * RandomOrgCache can be polled for new results * conforming to the output
1336      * format of the input request. RandomOrgCache type is same as expected
1337      * return value. * * @param n how many random decimal fractions you need.
1338      * Must be within the [1,1e4] range.
1339      *
1340      ** @param decimalPlaces the number of decimal places to use. Must be
1341      * within the [1,20] range. * * @return RandomOrgCache<double[]>
1342      *
1343      */
1344     public RandomOrgCache<double[]> createDecimalFractionCache(int n, int decimalPlaces) {
1345         return this.createDecimalFractionCache(n, decimalPlaces, true, 20);
1346     }
1347 
1348     /**
1349      * Get a RandomOrgCache to obtain random decimal fractions. The
1350      * RandomOrgCache can be polled for new results * conforming to the output
1351      * format of the input request. RandomOrgCache type is same as expected
1352      * return value. * * @param n how many random decimal fractions you need.
1353      * Must be within the [1,1e4] range.
1354      *
1355      ** @param decimalPlaces the number of decimal places to use. Must be
1356      * within the [1,20] range.
1357      ** @param replacement specifies whether the random numbers should be
1358      * picked with replacement. *	If True the resulting numbers may contain
1359      * duplicate values, otherwise the numbers will all be unique (default
1360      * True).
1361      ** @param cacheSize number of result-sets for the cache to try to maintain
1362      * at any given time (default 20, minimum 2). * * @return
1363      * RandomOrgCache<double[]>
1364      *
1365      */
1366     public RandomOrgCache<double[]> createDecimalFractionCache(int n, int decimalPlaces, boolean replacement, int cacheSize) {
1367         if (cacheSize < 2) {
1368             cacheSize = 2;
1369         }
1370 
1371         JsonObject request = new JsonObject();
1372 
1373         request.addProperty("decimalPlaces", decimalPlaces);
1374         request.addProperty("replacement", replacement);
1375 
1376         int bulkN = 0;
1377 
1378         // If possible, make requests more efficient by bulk-ordering from the server.
1379         // initially set at cache_size/2, but cache will auto-shrink bulk request size if requests can't be fulfilled.
1380         if (replacement) {
1381             bulkN = cacheSize / 2;
1382             request.addProperty("n", bulkN * n);
1383 
1384             // not possible to make the request more efficient
1385         } else {
1386             request.addProperty("n", n);
1387         }
1388 
1389         // get the request object for use in all requests from this cache
1390         request = this.generateKeyedRequest(request, DECIMAL_FRACTION_METHOD);
1391 
1392         // max single request size, in bits, for adjusting bulk requests later
1393         int maxRequestSize = (int) Math.ceil(Math.log(10) / Math.log(2) * decimalPlaces * n);
1394 
1395         return new RandomOrgCache<double[]>(
1396                 new JsonObjectInputCallable<JsonObject>() {
1397             @Override
1398             public JsonObject call() throws RandomOrgSendTimeoutException, RandomOrgKeyNotRunningError, RandomOrgInsufficientRequestsError, RandomOrgInsufficientBitsError, RandomOrgBadHTTPResponseException, RandomOrgRANDOMORGError, RandomOrgJSONRPCError, MalformedURLException, IOException {
1399                 return RandomOrgClient.this.sendRequest(this.input);
1400             }
1401         }, new JsonObjectInputCallable<double[]>() {
1402             @Override
1403             public double[] call() {
1404                 return RandomOrgClient.this.extractDoubles(this.input);
1405             }
1406         },
1407                 request, cacheSize, bulkN, n, maxRequestSize);
1408     }
1409 
1410     /**
1411      * Get a RandomOrgCache to obtain random numbers. The RandomOrgCache can be
1412      * polled for new results * conforming to the output format of the input
1413      * request. RandomOrgCache type is same as expected return value. * * @param
1414      * n how many random numbers you need. Must be within the [1,1e4] range.
1415      *
1416      ** @param mean the distribution's mean. Must be within the [-1e6,1e6]
1417      * range.
1418      ** @param standardDeviation the distribution's standard deviation. Must be
1419      * within the [-1e6,1e6] range.
1420      ** @param significantDigits the number of significant digits to use. Must
1421      * be within the [2,20] range. * * @return RandomOrgCache<double[]>
1422      *
1423      */
1424     public RandomOrgCache<double[]> createGaussianCache(int n, double mean, double standardDeviation, int significantDigits) {
1425         return this.createGaussianCache(n, mean, standardDeviation, significantDigits, 20);
1426     }
1427 
1428     /**
1429      * Get a RandomOrgCache to obtain random numbers. The RandomOrgCache can be
1430      * polled for new results * conforming to the output format of the input
1431      * request. RandomOrgCache type is same as expected return value. * * @param
1432      * n how many random numbers you need. Must be within the [1,1e4] range.
1433      *
1434      ** @param mean the distribution's mean. Must be within the [-1e6,1e6]
1435      * range.
1436      ** @param standardDeviation the distribution's standard deviation. Must be
1437      * within the [-1e6,1e6] range.
1438      ** @param significantDigits the number of significant digits to use. Must
1439      * be within the [2,20] range.
1440      ** @param cacheSize number of result-sets for the cache to try to maintain
1441      * at any given time (default 20, minimum 2). * * @return
1442      * RandomOrgCache<double[]>
1443      *
1444      */
1445     public RandomOrgCache<double[]> createGaussianCache(int n, double mean, double standardDeviation, int significantDigits, int cacheSize) {
1446         if (cacheSize < 2) {
1447             cacheSize = 2;
1448         }
1449 
1450         JsonObject request = new JsonObject();
1451 
1452         request.addProperty("mean", mean);
1453         request.addProperty("standardDeviation", standardDeviation);
1454         request.addProperty("significantDigits", significantDigits);
1455 
1456         int bulkN = 0;
1457 
1458         // make requests more efficient by bulk-ordering from the server.
1459         // initially set at cache_size/2, but cache will auto-shrink bulk request size if requests can't be fulfilled.
1460         bulkN = cacheSize / 2;
1461         request.addProperty("n", bulkN * n);
1462 
1463         // get the request object for use in all requests from this cache
1464         request = this.generateKeyedRequest(request, GAUSSIAN_METHOD);
1465 
1466         // max single request size, in bits, for adjusting bulk requests later
1467         int maxRequestSize = (int) Math.ceil(Math.log(Math.pow(10, significantDigits)) / Math.log(2) * n);
1468 
1469         return new RandomOrgCache<double[]>(
1470                 new JsonObjectInputCallable<JsonObject>() {
1471             @Override
1472             public JsonObject call() throws RandomOrgSendTimeoutException, RandomOrgKeyNotRunningError, RandomOrgInsufficientRequestsError, RandomOrgInsufficientBitsError, RandomOrgBadHTTPResponseException, RandomOrgRANDOMORGError, RandomOrgJSONRPCError, MalformedURLException, IOException {
1473                 return RandomOrgClient.this.sendRequest(this.input);
1474             }
1475         }, new JsonObjectInputCallable<double[]>() {
1476             @Override
1477             public double[] call() {
1478                 return RandomOrgClient.this.extractDoubles(this.input);
1479             }
1480         },
1481                 request, cacheSize, bulkN, n, maxRequestSize);
1482     }
1483 
1484     /**
1485      * Get a RandomOrgCache to obtain random strings. The RandomOrgCache can be
1486      * polled for new results * conforming to the output format of the input
1487      * request. RandomOrgCache type is same as expected return value. * * @param
1488      * n how many random strings you need. Must be within the [1,1e4] range.
1489      *
1490      ** @param length the length of each string. Must be within the [1,20]
1491      * range. All strings will be of the same length.
1492      ** @param characters a string that contains the set of characters that are
1493      * allowed to occur in the random strings. *	The maximum number of
1494      * characters is 80. * * @return RandomOrgCache<String[]>
1495      *
1496      */
1497     public RandomOrgCache<String[]> createStringCache(int n, int length, String characters) {
1498         return this.createStringCache(n, length, characters, true, 20);
1499     }
1500 
1501     /**
1502      * Get a RandomOrgCache to obtain random strings. The RandomOrgCache can be
1503      * polled for new results * conforming to the output format of the input
1504      * request. RandomOrgCache type is same as expected return value. * * @param
1505      * n how many random strings you need. Must be within the [1,1e4] range.
1506      *
1507      ** @param length the length of each string. Must be within the [1,20]
1508      * range. All strings will be of the same length.
1509      ** @param characters a string that contains the set of characters that are
1510      * allowed to occur in the random strings. *	The maximum number of
1511      * characters is 80.
1512      ** @param replacement specifies whether the random strings should be
1513      * picked with replacement. If True the resulting *	list of strings may
1514      * contain duplicates, otherwise the strings will all be unique (default
1515      * True).
1516      ** @param cacheSize number of result-sets for the cache to try to maintain
1517      * at any given time (default 20, minimum 2). * * @return
1518      * RandomOrgCache<String[]>
1519      *
1520      */
1521     public RandomOrgCache<String[]> createStringCache(int n, int length, String characters, boolean replacement, int cacheSize) {
1522         if (cacheSize < 2) {
1523             cacheSize = 2;
1524         }
1525 
1526         JsonObject request = new JsonObject();
1527 
1528         request.addProperty("length", length);
1529         request.addProperty("characters", characters);
1530         request.addProperty("replacement", replacement);
1531 
1532         int bulkN = 0;
1533 
1534         // If possible, make requests more efficient by bulk-ordering from the server.
1535         // initially set at cache_size/2, but cache will auto-shrink bulk request size if requests can't be fulfilled.
1536         if (replacement) {
1537             bulkN = cacheSize / 2;
1538             request.addProperty("n", bulkN * n);
1539 
1540             // not possible to make the request more efficient
1541         } else {
1542             request.addProperty("n", n);
1543         }
1544 
1545         // get the request object for use in all requests from this cache
1546         request = this.generateKeyedRequest(request, STRING_METHOD);
1547 
1548         // max single request size, in bits, for adjusting bulk requests later
1549         int maxRequestSize = (int) Math.ceil(Math.log(characters.length()) / Math.log(2) * length * n);
1550 
1551         return new RandomOrgCache<String[]>(
1552                 new JsonObjectInputCallable<JsonObject>() {
1553             @Override
1554             public JsonObject call() throws RandomOrgSendTimeoutException, RandomOrgKeyNotRunningError, RandomOrgInsufficientRequestsError, RandomOrgInsufficientBitsError, RandomOrgBadHTTPResponseException, RandomOrgRANDOMORGError, RandomOrgJSONRPCError, MalformedURLException, IOException {
1555                 return RandomOrgClient.this.sendRequest(this.input);
1556             }
1557         }, new JsonObjectInputCallable<String[]>() {
1558             @Override
1559             public String[] call() {
1560                 return RandomOrgClient.this.extractStrings(this.input);
1561             }
1562         },
1563                 request, cacheSize, bulkN, n, maxRequestSize);
1564     }
1565 
1566     /**
1567      * Get a RandomOrgCache to obtain UUIDs. The RandomOrgCache can be polled
1568      * for new results conforming to the * output format of the input request.
1569      * RandomOrgCache type is same as expected return value. * * @param n how
1570      * many random UUIDs you need. Must be within the [1,1e3] range. * * @return
1571      * RandomOrgCache<UUID[]>
1572      *
1573      */
1574     public RandomOrgCache<UUID[]> createUUIDCache(int n) {
1575         return this.createUUIDCache(n, 10);
1576     }
1577 
1578     /**
1579      * Get a RandomOrgCache to obtain UUIDs. The RandomOrgCache can be polled
1580      * for new results conforming to the * output format of the input request.
1581      * RandomOrgCache type is same as expected return value. * * @param n how
1582      * many random UUIDs you need. Must be within the [1,1e3] range.
1583      *
1584      ** @param cacheSize number of result-sets for the cache to try to maintain
1585      * at any given time (default 10, minimum 2). * * @return
1586      * RandomOrgCache<UUID[]>
1587      *
1588      */
1589     public RandomOrgCache<UUID[]> createUUIDCache(int n, int cacheSize) {
1590         if (cacheSize < 2) {
1591             cacheSize = 2;
1592         }
1593 
1594         JsonObject request = new JsonObject();
1595 
1596         int bulkN = 0;
1597 
1598         // make requests more efficient by bulk-ordering from the server.
1599         // initially set at cache_size/2, but cache will auto-shrink bulk request size if requests can't be fulfilled.
1600         bulkN = cacheSize / 2;
1601         request.addProperty("n", bulkN * n);
1602 
1603         // get the request object for use in all requests from this cache
1604         request = this.generateKeyedRequest(request, UUID_METHOD);
1605 
1606         // max single request size, in bits, for adjusting bulk requests later
1607         int maxRequestSize = n * UUID_SIZE;
1608 
1609         return new RandomOrgCache<UUID[]>(
1610                 new JsonObjectInputCallable<JsonObject>() {
1611             @Override
1612             public JsonObject call() throws RandomOrgSendTimeoutException, RandomOrgKeyNotRunningError, RandomOrgInsufficientRequestsError, RandomOrgInsufficientBitsError, RandomOrgBadHTTPResponseException, RandomOrgRANDOMORGError, RandomOrgJSONRPCError, MalformedURLException, IOException {
1613                 return RandomOrgClient.this.sendRequest(this.input);
1614             }
1615         }, new JsonObjectInputCallable<UUID[]>() {
1616             @Override
1617             public UUID[] call() {
1618                 return RandomOrgClient.this.extractUUIDs(this.input);
1619             }
1620         },
1621                 request, cacheSize, bulkN, n, maxRequestSize);
1622     }
1623 
1624     /**
1625      * Get a RandomOrgCache to obtain random blobs. The RandomOrgCache can be
1626      * polled for new results conforming * to the output format of the input
1627      * request. RandomOrgCache type is same as expected return value. * * @param
1628      * n how many random blobs you need. Must be within the [1,100] range.
1629      *
1630      ** @param size the size of each blob, measured in bits. Must be within the
1631      * [1,1048576] range and must be divisible by 8. * * @return
1632      * RandomOrgCache<String[]>
1633      *
1634      */
1635     public RandomOrgCache<String[]> createBlobCache(int n, int size) {
1636         return this.createBlobCache(n, size, RandomOrgClient.BLOB_FORMAT_BASE64, 10);
1637     }
1638 
1639     /**
1640      * Get a RandomOrgCache to obtain random blobs. The RandomOrgCache can be
1641      * polled for new results conforming * to the output format of the input
1642      * request. RandomOrgCache type is same as expected return value. * * @param
1643      * n how many random blobs you need. Must be within the [1,100] range.
1644      *
1645      ** @param size the size of each blob, measured in bits. Must be within the
1646      * [1,1048576] range and must be divisible by 8.
1647      ** @param format specifies the format in which the blobs will be returned.
1648      * Values allowed are * BLOB_FORMAT_BASE64 and BLOB_FORMAT_HEX (default
1649      * BLOB_FORMAT_BASE64).
1650      ** @param cacheSize number of result-sets for the cache to try to maintain
1651      * at any given time (default 10, minimum 2). * * @return
1652      * RandomOrgCache<String[]>
1653      *
1654      */
1655     public RandomOrgCache<String[]> createBlobCache(int n, int size, String format, int cacheSize) {
1656         if (cacheSize < 2) {
1657             cacheSize = 2;
1658         }
1659 
1660         JsonObject request = new JsonObject();
1661 
1662         request.addProperty("size", size);
1663         request.addProperty("format", format);
1664 
1665         int bulkN = 0;
1666 
1667         // make requests more efficient by bulk-ordering from the server.
1668         // initially set at cache_size/2, but cache will auto-shrink bulk request size if requests can't be fulfilled.
1669         bulkN = cacheSize / 2;
1670         request.addProperty("n", bulkN * n);
1671 
1672         // get the request object for use in all requests from this cache
1673         request = this.generateKeyedRequest(request, BLOB_METHOD);
1674 
1675         // max single request size, in bits, for adjusting bulk requests later
1676         int maxRequestSize = n * size;
1677 
1678         return new RandomOrgCache<String[]>(
1679                 new JsonObjectInputCallable<JsonObject>() {
1680             @Override
1681             public JsonObject call() throws RandomOrgSendTimeoutException, RandomOrgKeyNotRunningError, RandomOrgInsufficientRequestsError, RandomOrgInsufficientBitsError, RandomOrgBadHTTPResponseException, RandomOrgRANDOMORGError, RandomOrgJSONRPCError, MalformedURLException, IOException {
1682                 return RandomOrgClient.this.sendRequest(this.input);
1683             }
1684         }, new JsonObjectInputCallable<String[]>() {
1685             @Override
1686             public String[] call() {
1687                 return RandomOrgClient.this.extractStrings(this.input);
1688             }
1689         },
1690                 request, cacheSize, bulkN, n, maxRequestSize);
1691     }
1692 
1693     // Methods for accessing server usage statistics.
1694     /**
1695      * Return the (estimated) number of remaining API requests available to the
1696      * client. If cached * usage info is older than
1697      * ALLOWANCE_STATE_REFRESH_SECONDS fresh info is obtained from server. * If
1698      * fresh info has to be obtained the following exceptions can be raised.
1699      *
1700      * *
1701      * @return number of requests remaining. * * @throws
1702      * RandomOrgSendTimeoutException blocking timeout is exceeded before the
1703      * request can be sent.
1704      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
1705      ** @throws RandomOrgInsufficientRequestsError API key's server requests
1706      * allowance has been exceeded.
1707      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
1708      * has been exceeded.
1709      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
1710      * received.
1711      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
1712      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
1713      ** @throws MalformedURLException in the unlikely event something goes
1714      * wrong with URL creation. @see java.net.MalformedURLException
1715      ** @throws IOException @see java.io.IOException
1716      *
1717      */
1718     public int getRequestsLeft() throws RandomOrgSendTimeoutException,
1719             RandomOrgKeyNotRunningError,
1720             RandomOrgInsufficientRequestsError,
1721             RandomOrgInsufficientBitsError,
1722             RandomOrgBadHTTPResponseException,
1723             RandomOrgRANDOMORGError,
1724             RandomOrgJSONRPCError,
1725             MalformedURLException,
1726             IOException {
1727         if (this.requestsLeft < 0 || System.currentTimeMillis() > (this.lastResponseReceivedTime + RandomOrgClient.ALLOWANCE_STATE_REFRESH_SECONDS)) {
1728             this.getUsage();
1729         }
1730         return this.requestsLeft;
1731     }
1732 
1733     /**
1734      * Return the (estimated) number of remaining true random bits available to
1735      * the client. If cached * usage info is older than
1736      * ALLOWANCE_STATE_REFRESH_SECONDS fresh info is obtained from server. * If
1737      * fresh info has to be obtained the following exceptions can be raised.
1738      *
1739      * *
1740      * @return number of bits remaining. * * @throws
1741      * RandomOrgSendTimeoutException blocking timeout is exceeded before the
1742      * request can be sent.
1743      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
1744      ** @throws RandomOrgInsufficientRequestsError API key's server requests
1745      * allowance has been exceeded.
1746      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
1747      * has been exceeded.
1748      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
1749      * received.
1750      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
1751      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
1752      ** @throws MalformedURLException in the unlikely event something goes
1753      * wrong with URL creation. @see java.net.MalformedURLException
1754      ** @throws IOException @see java.io.IOException
1755      *
1756      */
1757     public int getBitsLeft() throws RandomOrgSendTimeoutException,
1758             RandomOrgKeyNotRunningError,
1759             RandomOrgInsufficientRequestsError,
1760             RandomOrgInsufficientBitsError,
1761             RandomOrgBadHTTPResponseException,
1762             RandomOrgRANDOMORGError,
1763             RandomOrgJSONRPCError,
1764             MalformedURLException,
1765             IOException {
1766         if (this.bitsLeft < 0 || System.currentTimeMillis() > (this.lastResponseReceivedTime + RandomOrgClient.ALLOWANCE_STATE_REFRESH_SECONDS)) {
1767             this.getUsage();
1768         }
1769         return this.bitsLeft;
1770     }
1771 
1772     // Server communications & helper functions.
1773     /**
1774      * Issue a getUsage request to update bits and requests left. * * @throws
1775      * RandomOrgSendTimeoutException blocking timeout is exceeded before the
1776      * request can be sent.
1777      *
1778      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
1779      ** @throws RandomOrgInsufficientRequestsError API key's server requests
1780      * allowance has been exceeded.
1781      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
1782      * has been exceeded.
1783      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
1784      * received.
1785      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
1786      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
1787      ** @throws MalformedURLException in the unlikely event something goes
1788      * wrong with URL creation. @see java.net.MalformedURLException
1789      ** @throws IOException @see java.io.IOException
1790      *
1791      */
1792     private void getUsage() throws RandomOrgSendTimeoutException,
1793             RandomOrgKeyNotRunningError,
1794             RandomOrgInsufficientRequestsError,
1795             RandomOrgInsufficientBitsError,
1796             RandomOrgBadHTTPResponseException,
1797             RandomOrgRANDOMORGError,
1798             RandomOrgJSONRPCError,
1799             MalformedURLException,
1800             IOException {
1801 
1802         JsonObject request = new JsonObject();
1803 
1804         request = this.generateKeyedRequest(request, GET_USAGE_METHOD);
1805 
1806         this.sendRequest(request);
1807     }
1808 
1809     /**
1810      * Add generic request parameters and API key to custom request. * * @param
1811      * params custom parameters to generate request around.
1812      *
1813      ** @param method to send request to. * * @return fleshed out JSON request.
1814      *
1815      */
1816     private JsonObject generateKeyedRequest(JsonObject params, String method) {
1817 
1818         params.addProperty("apiKey", this.apiKey);
1819 
1820         JsonObject request = new JsonObject();
1821 
1822         request.addProperty("jsonrpc", "2.0");
1823         request.addProperty("method", method);
1824         request.add("params", params);
1825         request.addProperty("id", UUID.randomUUID().toString());
1826 
1827         return request;
1828     }
1829 
1830     /**
1831      * Add generic request parameters to custom request. * * @param params
1832      * custom parameters to generate request around.
1833      *
1834      ** @param method to send request to. * * @return fleshed out JSON request.
1835      *
1836      */
1837     private JsonObject generateRequest(JsonObject params, String method) {
1838 
1839         JsonObject request = new JsonObject();
1840 
1841         request.addProperty("jsonrpc", "2.0");
1842         request.addProperty("method", method);
1843         request.add("params", params);
1844         request.addProperty("id", UUID.randomUUID().toString());
1845 
1846         return request;
1847     }
1848 
1849     /**
1850      * Extracts int[] from JSON response. * * @param response JSON from which to
1851      * extract data. * * @return extracted int[].
1852      *
1853      */
1854     protected int[] extractInts(JsonObject response) {
1855 
1856         JsonArray data = this.extractResponse(response);
1857         int[] randoms = new int[data.size()];
1858 
1859         for (int i = 0; i < randoms.length; i++) {
1860             randoms[i] = data.get(i).getAsInt();
1861         }
1862 
1863         return randoms;
1864     }
1865 
1866     /**
1867      * Extracts double[] from JSON response. * * @param response JSON from which
1868      * to extract data. * * @return extracted double[].
1869      *
1870      */
1871     protected double[] extractDoubles(JsonObject response) {
1872 
1873         JsonArray data = this.extractResponse(response);
1874         double[] randoms = new double[data.size()];
1875 
1876         for (int i = 0; i < randoms.length; i++) {
1877             randoms[i] = data.get(i).getAsDouble();
1878         }
1879 
1880         return randoms;
1881     }
1882 
1883     /**
1884      * Extracts String[] from JSON response. * * @param response JSON from which
1885      * to extract data. * * @return extracted String[].
1886      *
1887      */
1888     protected String[] extractStrings(JsonObject response) {
1889 
1890         JsonArray data = this.extractResponse(response);
1891         String[] randoms = new String[data.size()];
1892 
1893         for (int i = 0; i < randoms.length; i++) {
1894             randoms[i] = data.get(i).getAsString();
1895         }
1896 
1897         return randoms;
1898     }
1899 
1900     /**
1901      * Extracts UUID[] from JSON response. * * @param response JSON from which
1902      * to extract data. * * @return extracted UUID[].
1903      *
1904      */
1905     protected UUID[] extractUUIDs(JsonObject response) {
1906 
1907         JsonArray data = this.extractResponse(response);
1908         UUID[] randoms = new UUID[data.size()];
1909 
1910         for (int i = 0; i < randoms.length; i++) {
1911             randoms[i] = UUID.fromString(data.get(i).getAsString());
1912         }
1913 
1914         return randoms;
1915     }
1916 
1917     /**
1918      * Gets random data as separate from response JSON. * * @param response JSON
1919      * from which to extract data. * * @return JsonArray of random data.
1920      *
1921      */
1922     private JsonArray extractResponse(JsonObject response) {
1923         return response.get("result").getAsJsonObject().get("random").getAsJsonObject().get("data").getAsJsonArray();
1924     }
1925 
1926     /**
1927      * Gets signing data from response JSON and add to result HashMap.
1928      *
1929      * *
1930      * @param response JSON from which to extract data.
1931      ** @param result to add signing data to. * * @return the passed in result
1932      * HasMap.
1933      *
1934      */
1935     private HashMap<String, Object> extractSignedResponse(JsonObject response, HashMap<String, Object> result) {
1936         result.put("random", response.get("result").getAsJsonObject().get("random").getAsJsonObject());
1937         result.put("signature", response.get("result").getAsJsonObject().get("signature").getAsString());
1938 
1939         return result;
1940     }
1941 
1942     /**
1943      * Gets verification response as separate from response JSON. * * @param
1944      * response JSON from which to extract verification response. * * @return
1945      * verification success.
1946      *
1947      */
1948     private boolean extractVerificationResponse(JsonObject response) {
1949 
1950         return response.get("result").getAsJsonObject().get("authenticity").getAsBoolean();
1951     }
1952 
1953     /**
1954      * Send request as determined by serialized boolean. * * @param request JSON
1955      * to send. * * @return JsonObject response. * * @throws
1956      * RandomOrgSendTimeoutException blocking timeout is exceeded before the
1957      * request can be sent.
1958      *
1959      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
1960      ** @throws RandomOrgInsufficientRequestsError API key's server requests
1961      * allowance has been exceeded.
1962      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
1963      * has been exceeded.
1964      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
1965      * received.
1966      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
1967      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
1968      ** @throws MalformedURLException in the unlikely event something goes
1969      * wrong with URL creation. @see java.net.MalformedURLException
1970      ** @throws IOException @see java.io.IOException
1971      *
1972      */
1973     protected JsonObject sendRequest(JsonObject request) throws RandomOrgSendTimeoutException,
1974             RandomOrgKeyNotRunningError,
1975             RandomOrgInsufficientRequestsError,
1976             RandomOrgInsufficientBitsError,
1977             RandomOrgBadHTTPResponseException,
1978             RandomOrgRANDOMORGError,
1979             RandomOrgJSONRPCError,
1980             MalformedURLException,
1981             IOException {
1982 
1983         return this.serialized ? this.sendSerializedRequest(request) : this.sendUnserializedRequest(request);
1984     }
1985 
1986     /**
1987      * Immediate call to server. Networking is run on a separate thread as
1988      * Android platform disallows networking on the main thread. * * @param
1989      * request JSON to send. * * @return JsonObject response. * * @throws
1990      * RandomOrgSendTimeoutException blocking timeout is exceeded before the
1991      * request can be sent.
1992      *
1993      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
1994      ** @throws RandomOrgInsufficientRequestsError API key's server requests
1995      * allowance has been exceeded.
1996      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
1997      * has been exceeded.
1998      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
1999      * received.
2000      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
2001      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
2002      ** @throws MalformedURLException in the unlikely event something goes
2003      * wrong with URL creation. @see java.net.MalformedURLException
2004      ** @throws IOException @see java.io.IOException
2005      *
2006      */
2007     private JsonObject sendUnserializedRequest(JsonObject request) throws RandomOrgSendTimeoutException,
2008             RandomOrgKeyNotRunningError,
2009             RandomOrgInsufficientRequestsError,
2010             RandomOrgInsufficientBitsError,
2011             RandomOrgBadHTTPResponseException,
2012             RandomOrgRANDOMORGError,
2013             RandomOrgJSONRPCError,
2014             MalformedURLException,
2015             IOException {
2016 
2017         // Send request immediately.
2018         UnserializedRunnable r = new UnserializedRunnable(request);
2019         new Thread(r).start();
2020 
2021         // Wait for response to arrive.
2022         while (r.getData() == null) {
2023             try {
2024                 Thread.sleep(50);
2025             } catch (InterruptedException e) {
2026                 LOGGER.log(Level.INFO, "Client interrupted while waiting for server to return a response.");
2027             }
2028         }
2029 
2030         // Raise any thrown exceptions.
2031         if (r.getData().containsKey("exception")) {
2032             this.throwException((Exception) r.getData().get("exception"));
2033         }
2034 
2035         // Return response.
2036         return (JsonObject) r.getData().get("response");
2037     }
2038 
2039     /**
2040      * Add request to queue to be executed by networking thread one-by-one. *
2041      * Method blocks until this request receives a response or times out.
2042      *
2043      * *
2044      * @param request JSON to send. * * @return JsonObject response. * * @throws
2045      * RandomOrgSendTimeoutException blocking timeout is exceeded before the
2046      * request can be sent.
2047      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
2048      ** @throws RandomOrgInsufficientRequestsError API key's server requests
2049      * allowance has been exceeded.
2050      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
2051      * has been exceeded.
2052      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
2053      * received.
2054      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
2055      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
2056      ** @throws MalformedURLException in the unlikely event something goes
2057      * wrong with URL creation. @see java.net.MalformedURLException
2058      ** @throws IOException @see java.io.IOException
2059      *
2060      */
2061     private JsonObject sendSerializedRequest(JsonObject request) throws RandomOrgSendTimeoutException,
2062             RandomOrgKeyNotRunningError,
2063             RandomOrgInsufficientRequestsError,
2064             RandomOrgInsufficientBitsError,
2065             RandomOrgBadHTTPResponseException,
2066             RandomOrgRANDOMORGError,
2067             RandomOrgJSONRPCError,
2068             MalformedURLException,
2069             IOException {
2070 
2071         // Add request to the queue with it's own lock.
2072         Object requestLock = new Object();
2073 
2074         HashMap<String, Object> data = new HashMap<String, Object>();
2075         data.put("lock", requestLock);
2076         data.put("request", request);
2077         data.put("response", null);
2078         data.put("exception", null);
2079 
2080         synchronized (this.serializedQueue) {
2081             this.serializedQueue.offer(data);
2082             this.serializedQueue.notify();
2083         }
2084 
2085         // Wait on the lock for the specified blocking timeout.
2086         synchronized (requestLock) {
2087             try {
2088                 if (this.blockingTimeout == -1) {
2089                     requestLock.wait();
2090                 } else {
2091                     requestLock.wait(this.blockingTimeout);
2092                 }
2093             } catch (InterruptedException e) {
2094                 LOGGER.log(Level.INFO, "Client interrupted while waiting for request to be sent.");
2095             }
2096 
2097             // Lock has now either been notified or timed out. Examine data to determine which and react accordingly.
2098             // Request wasn't sent in time, cancel and raise exception.
2099             if (data.get("response") == null && data.get("exception") == null) {
2100                 data.put("request", null);
2101                 throw new RandomOrgSendTimeoutException("The maximum allowed blocking time of " + this.blockingTimeout
2102                         + "millis has been exceeded while waiting for a synchronous request to send.");
2103             }
2104 
2105             // Exception on sending request.
2106             if (data.get("exception") != null) {
2107                 this.throwException((Exception) data.get("exception"));
2108             }
2109 
2110             // Request was successful.
2111             return (JsonObject) data.get("response");
2112         }
2113     }
2114 
2115     /**
2116      * Thread to synchronously send requests in queue.
2117      */
2118     protected void threadedRequestSending() {
2119 
2120         // Thread to execute queued requests.
2121         while (true) {
2122 
2123             HashMap<String, Object> request;
2124 
2125             synchronized (this.serializedQueue) {
2126                 // Block and wait for a request.
2127                 if (this.serializedQueue.isEmpty()) {
2128                     try {
2129                         this.serializedQueue.wait();
2130                     } catch (InterruptedException e) {
2131                         LOGGER.log(Level.INFO, "Client thread interrupted while waiting for a request to send.");
2132                     }
2133                 }
2134 
2135                 request = this.serializedQueue.pop();
2136             }
2137 
2138             // Get the request's lock to indicate request in progress.
2139             synchronized (request.get("lock")) {
2140 
2141                 // If request still exists it hasn't been cancelled.
2142                 if (request.get("request") != null) {
2143 
2144                     // Send request.
2145                     HashMap<String, Object> data = this.sendRequestCore((JsonObject) request.get("request"));
2146 
2147                     // Set result.
2148                     if (data.containsKey("exception")) {
2149                         request.put("exception", data.get("exception"));
2150                     } else {
2151                         request.put("response", data.get("response"));
2152                     }
2153                 }
2154 
2155                 // Notify completion and return
2156                 request.get("lock").notify();
2157             }
2158         }
2159     }
2160 
2161     /**
2162      * Throw specific Exception types. * * @param e exception to throw.
2163      *
2164      * *
2165      * @throws RandomOrgSendTimeoutException blocking timeout is exceeded before
2166      * the request can be sent.
2167      ** @throws RandomOrgKeyNotRunningError API key has been stopped.
2168      ** @throws RandomOrgInsufficientRequestsError API key's server requests
2169      * allowance has been exceeded.
2170      ** @throws RandomOrgInsufficientBitsError API key's server bits allowance
2171      * has been exceeded.
2172      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
2173      * received.
2174      ** @throws RandomOrgRANDOMORGError server returns a RANDOM.ORG Error.
2175      ** @throws RandomOrgJSONRPCError server returns a JSON-RPC Error.
2176      ** @throws MalformedURLException in the unlikely event something goes
2177      * wrong with URL creation. @see java.net.MalformedURLException
2178      ** @throws IOException @see java.io.IOException
2179      *
2180      */
2181     private void throwException(Exception e) throws RandomOrgSendTimeoutException,
2182             RandomOrgKeyNotRunningError,
2183             RandomOrgInsufficientRequestsError,
2184             RandomOrgInsufficientBitsError,
2185             RandomOrgBadHTTPResponseException,
2186             RandomOrgRANDOMORGError,
2187             RandomOrgJSONRPCError,
2188             MalformedURLException,
2189             IOException {
2190 
2191         if (e.getClass() == RandomOrgSendTimeoutException.class) {
2192             throw (RandomOrgSendTimeoutException) e;
2193         } else if (e.getClass() == RandomOrgKeyNotRunningError.class) {
2194             throw (RandomOrgKeyNotRunningError) e;
2195         } else if (e.getClass() == RandomOrgInsufficientRequestsError.class) {
2196             throw (RandomOrgInsufficientRequestsError) e;
2197         } else if (e.getClass() == RandomOrgInsufficientBitsError.class) {
2198             throw (RandomOrgInsufficientBitsError) e;
2199         } else if (e.getClass() == RandomOrgBadHTTPResponseException.class) {
2200             throw (RandomOrgBadHTTPResponseException) e;
2201         } else if (e.getClass() == RandomOrgRANDOMORGError.class) {
2202             throw (RandomOrgRANDOMORGError) e;
2203         } else if (e.getClass() == RandomOrgJSONRPCError.class) {
2204             throw (RandomOrgJSONRPCError) e;
2205         } else if (e.getClass() == MalformedURLException.class) {
2206             throw (MalformedURLException) e;
2207         } else if (e.getClass() == IOException.class) {
2208             throw (IOException) e;
2209         }
2210     }
2211 
2212     /**
2213      * Core send request function. * * @param request JSON to send. * * @return
2214      * info on request success/response in a HashMap with one or other of the
2215      * following entries: *	"exception" : Exception - exception thrown, possible
2216      * exception types: *	RandomOrgSendTimeoutException *
2217      * RandomOrgKeyNotRunningError *	RandomOrgInsufficientRequestsError *
2218      * RandomOrgInsufficientBitsError * RandomOrgBadHTTPResponseException *
2219      * RandomOrgRANDOMORGError * RandomOrgJSONRPCError * MalformedURLException *
2220      * IOException *	"response"	: JsonObject - response
2221      *
2222      */
2223     protected HashMap<String, Object> sendRequestCore(JsonObject request) {
2224 
2225         HashMap<String, Object> ret = new HashMap<String, Object>();
2226 
2227         // If a back-off is set, no more requests can be issued until the required back-off time is up.
2228         if (this.backoff != -1) {
2229 
2230             // Time not yet up, throw exception.
2231             if (System.currentTimeMillis() < this.backoff) {
2232                 ret.put("exception", new RandomOrgInsufficientRequestsError(this.backoffError));
2233                 return ret;
2234                 // Time is up, clear back-off.
2235             } else {
2236                 this.backoff = -1;
2237                 this.backoffError = null;
2238             }
2239         }
2240 
2241         long wait = 0;
2242 
2243         // Check server advisory delay.
2244         synchronized (this.advisoryDelayLock) {
2245             wait = this.advisoryDelay - (System.currentTimeMillis() - this.lastResponseReceivedTime);
2246         }
2247 
2248         // Wait the specified delay if necessary and if wait time is not longer than the set blocking timeout.
2249         if (wait > 0) {
2250             if (this.blockingTimeout != -1 && wait > this.blockingTimeout) {
2251                 ret.put("exception", new RandomOrgSendTimeoutException("The server advisory delay of " + wait
2252                         + "millis is greater than the defined maximum allowed blocking time of " + this.blockingTimeout + "millis."));
2253                 return ret;
2254             }
2255             try {
2256                 Thread.sleep(wait);
2257             } catch (InterruptedException e) {
2258                 LOGGER.log(Level.INFO, "Client interrupted while waiting for server mandated blocking time.");
2259             }
2260         }
2261 
2262         JsonObject response;
2263 
2264         // Send the request
2265         try {
2266             response = this.post(request);
2267         } catch (MalformedURLException e) {
2268             ret.put("exception", e);
2269             return ret;
2270         } catch (RandomOrgBadHTTPResponseException e) {
2271             ret.put("exception", e);
2272             return ret;
2273         } catch (IOException e) {
2274             ret.put("exception", e);
2275             return ret;
2276         }
2277 
2278         // Parse the response.
2279         // Has error?
2280         if (response.has("error")) {
2281             JsonObject error = response.get("error").getAsJsonObject();
2282 
2283             int code = error.get("code").getAsInt();
2284             String message = error.get("message").getAsString();
2285 
2286             // RandomOrgAllowanceExceededError, API key not running, backoff until midnight UTC,
2287             // from RANDOM.ORG Errors: https://api.random.org/json-rpc/1/error-codes
2288             if (code == 402) {
2289 
2290                 Calendar date = new GregorianCalendar();
2291                 date.set(Calendar.HOUR_OF_DAY, 0);
2292                 date.set(Calendar.MINUTE, 0);
2293                 date.set(Calendar.SECOND, 0);
2294                 date.set(Calendar.MILLISECOND, 0);
2295                 date.add(Calendar.DAY_OF_MONTH, 1);
2296 
2297                 this.backoff = date.getTimeInMillis();
2298                 this.backoffError = "Error " + code + ": " + message;
2299                 ret.put("exception", new RandomOrgInsufficientRequestsError(this.backoffError));
2300                 return ret;
2301             } else if (code == 401) {
2302                 ret.put("exception", new RandomOrgKeyNotRunningError("Error " + code + ": " + message));
2303                 return ret;
2304 
2305             } else if (code == 403) {
2306                 ret.put("exception", new RandomOrgInsufficientBitsError("Error " + code + ": " + message, this.bitsLeft));
2307                 return ret;
2308 
2309                 // RandomOrgRANDOMORGError from RANDOM.ORG Errors: https://api.random.org/json-rpc/1/error-codes
2310             } else if (RandomOrgClient.randomOrgErrors.contains(code)) {
2311                 ret.put("exception", new RandomOrgRANDOMORGError("Error " + code + ": " + message));
2312                 return ret;
2313 
2314                 // RandomOrgJSONRPCError from JSON-RPC Errors: https://api.random.org/json-rpc/1/error-codes
2315             } else {
2316                 ret.put("exception", new RandomOrgJSONRPCError("Error " + code + ": " + message));
2317                 return ret;
2318             }
2319         }
2320 
2321         JsonObject result = response.get("result").getAsJsonObject();
2322 
2323         // Update usage statistics
2324         if (result.has("requestsLeft")) {
2325             this.requestsLeft = result.get("requestsLeft").getAsInt();
2326             this.bitsLeft = result.get("bitsLeft").getAsInt();
2327         }
2328 
2329         // Set new server advisory delay
2330         synchronized (this.advisoryDelayLock) {
2331             if (result.has("advisoryDelay")) {
2332                 this.advisoryDelay = result.get("advisoryDelay").getAsInt();
2333             } else {
2334                 // Use default if none from server.
2335                 this.advisoryDelay = RandomOrgClient.DEFAULT_DELAY;
2336             }
2337 
2338             this.lastResponseReceivedTime = System.currentTimeMillis();
2339         }
2340 
2341         ret.put("response", response);
2342         return ret;
2343     }
2344 
2345     /**
2346      * POST JSON to server and return JSON response. * * @param json request to
2347      * post. * * @return JSON response. * * @throws IOException @see
2348      * java.io.IOException
2349      *
2350      ** @throws MalformedURLException in the unlikely event something goes
2351      * wrong with URL creation. @see java.net.MalformedURLException
2352      ** @throws RandomOrgBadHTTPResponseException if a HTTP 200 OK response not
2353      * received.
2354      *
2355      */
2356     private JsonObject post(JsonObject json) throws IOException, MalformedURLException, RandomOrgBadHTTPResponseException {
2357 
2358         HttpsURLConnection con = (HttpsURLConnection) new URL("https://api.random.org/json-rpc/1/invoke").openConnection();
2359         con.setConnectTimeout(this.httpTimeout);
2360 
2361         // headers
2362         con.setRequestMethod("POST");
2363         con.setRequestProperty("Content-Type", "application/json");
2364 
2365         // send JSON
2366         con.setDoOutput(true);
2367         DataOutputStream dos = new DataOutputStream(con.getOutputStream());
2368         dos.writeBytes(json.toString());
2369         dos.flush();
2370         dos.close();
2371 
2372         // check response
2373         int responseCode = con.getResponseCode();
2374 
2375         // return JSON...
2376         if (responseCode == HttpsURLConnection.HTTP_OK) {
2377             BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
2378             String inputLine;
2379             StringBuilder response = new StringBuilder();
2380 
2381             while ((inputLine = in.readLine()) != null) {
2382                 response.append(inputLine);
2383             }
2384             in.close();
2385 
2386             return new JsonParser().parse(response.toString()).getAsJsonObject();
2387 
2388             // ...or throw error
2389         } else {
2390             throw new RandomOrgBadHTTPResponseException("Error " + responseCode + ": " + con.getResponseMessage());
2391         }
2392     }
2393 
2394     /**
2395      * Runnable for unserialized network calls. *
2396      */
2397     private class UnserializedRunnable implements Runnable {
2398 
2399         private JsonObject request;
2400         private HashMap<String, Object> data;
2401 
2402         /**
2403          * @param request object to send to server.
2404          */
2405         UnserializedRunnable(JsonObject request) {
2406             super();
2407             this.request = request;
2408         }
2409 
2410         /* @see java.lang.Runnable#run() */
2411         @Override
2412         public void run() {
2413             this.data = RandomOrgClient.this.sendRequestCore(this.request);
2414         }
2415 
2416         /**
2417          * @return data returned by network request - or null if not yet
2418          * arrived.
2419          */
2420         public HashMap<String, Object> getData() {
2421             return this.data;
2422         }
2423     }
2424 }
2425