1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.rng.simple;
18
19 import java.io.IOException;
20 import java.io.ObjectOutputStream;
21 import java.io.ObjectInputStream;
22 import java.util.Random;
23 import org.apache.commons.rng.RestorableUniformRandomProvider;
24 import org.apache.commons.rng.core.RandomProviderDefaultState;
25
26 /**
27 * Subclass of {@link Random} that {@link #next(int) delegates} to a
28 * {@link RestorableUniformRandomProvider} instance but will otherwise rely
29 * on the base class for generating all the random types.
30 *
31 * <p>Legacy applications coded against the JDK's API could use this subclass
32 * of {@link Random} in order to replace its linear congruential generator
33 * by any {@link RandomSource}.</p>
34 *
35 * <p>Caveat: Use of this class is <em>not</em> recommended for new applications.
36 * In particular, there is no guarantee that the serialized form of this class
37 * will be compatible across (even <em>minor</em>) releases of the library.</p>
38 *
39 * @since 1.0
40 */
41 public final class JDKRandomBridge extends Random {
42 /** Serializable version identifier. */
43 private static final long serialVersionUID = 20161107L;
44 /** Source. */
45 private final RandomSource source;
46 /** Delegate. */
47 private transient RestorableUniformRandomProvider delegate;
48 /** Workaround JDK's "Random" bug: https://bugs.openjdk.java.net/browse/JDK-8154225. */
49 private final transient boolean isInitialized;
50
51 /**
52 * Creates a new instance.
53 *
54 * @param source Source of randomness.
55 * @param seed Seed. Can be {@code null}.
56 */
57 public JDKRandomBridge(RandomSource source,
58 Object seed) {
59 this.source = source;
60 delegate = source.create(seed);
61 isInitialized = true;
62 }
63
64 /** {@inheritDoc} */
65 @Override
66 public synchronized void setSeed(long seed) {
67 if (isInitialized) {
68 delegate = source.create(seed);
69
70 // Force the clearing of the "haveNextNextGaussian" flag
71 // (cf. Javadoc of the base class); the value passed here
72 // is irrelevant (since it will not be used).
73 super.setSeed(0L);
74 }
75 }
76
77 /**
78 * Delegates the generation of 32 random bits to the
79 * {@code RandomSource} argument provided at
80 * {@link #JDKRandomBridge(RandomSource,Object) construction}.
81 * The returned value is such that if the source of randomness is
82 * {@link RandomSource#JDK}, all the generated values will be identical
83 * to those produced by the same sequence of calls on a {@link Random}
84 * instance initialized with the same seed.
85 *
86 * @param n Number of random bits which the requested value must contain.
87 * @return the value represented by the {@code n} high-order bits of a
88 * pseudo-random 32-bits integer.
89 */
90 @Override
91 protected int next(int n) {
92 synchronized (this) {
93 return delegate.nextInt() >>> (32 - n);
94 }
95 }
96
97 /**
98 * Serialization method.
99 *
100 * @param output Output stream.
101 * @throws IOException if an error occurs.
102 */
103 private void writeObject(ObjectOutputStream output)
104 throws IOException {
105 synchronized (this) {
106 // Write non-transient fields.
107 output.defaultWriteObject();
108
109 // Save current state and size.
110 // Avoid the use of ObjectOutputStream.writeObject(Object) to save the state.
111 // This allows deserialization to avoid security issues in using readObject().
112 final byte[] state = ((RandomProviderDefaultState) delegate.saveState()).getState();
113 final int size = state.length;
114 output.writeInt(size);
115 output.write(state);
116 }
117 }
118
119 /**
120 * Deserialization method.
121 *
122 * @param input Input stream.
123 * @throws IOException if an error occurs.
124 * @throws ClassNotFoundException if an error occurs.
125 */
126 private void readObject(ObjectInputStream input)
127 throws IOException,
128 ClassNotFoundException {
129 // Read non-transient fields.
130 input.defaultReadObject();
131
132 // Recreate the "delegate" from serialized info.
133 delegate = source.create();
134 // And restore its state.
135 // Avoid the use of input.readObject() to deserialize by manually reading the byte[].
136 // Note: ObjectInputStream.readObject() will execute the readObject() method of the named
137 // class in the stream which may contain potentially malicious code.
138 final int size = input.readInt();
139 final byte[] state = new byte[size];
140 input.readFully(state);
141 delegate.restoreState(new RandomProviderDefaultState(state));
142 }
143 }