View Javadoc
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       * @param output Output stream.
99       * @throws IOException if an error occurs.
100      */
101     private void writeObject(ObjectOutputStream output)
102         throws IOException {
103         synchronized (this) {
104             // Write non-transient fields.
105             output.defaultWriteObject();
106 
107             // Save current state and size.
108             // Avoid the use of ObjectOutputStream.writeObject(Object) to save the state.
109             // This allows deserialization to avoid security issues in using readObject().
110             final byte[] state = ((RandomProviderDefaultState) delegate.saveState()).getState();
111             final int size = state.length;
112             output.writeInt(size);
113             output.write(state);
114         }
115     }
116 
117     /**
118      * @param input Input stream.
119      * @throws IOException if an error occurs.
120      * @throws ClassNotFoundException if an error occurs.
121      */
122     private void readObject(ObjectInputStream input)
123         throws IOException,
124                ClassNotFoundException {
125         // Read non-transient fields.
126         input.defaultReadObject();
127 
128         // Recreate the "delegate" from serialized info.
129         delegate = source.create();
130         // And restore its state.
131         // Avoid the use of input.readObject() to deserialize by manually reading the byte[].
132         // Note: ObjectInputStream.readObject() will execute the readObject() method of the named
133         // class in the stream which may contain potentially malicious code.
134         final int size = input.readInt();
135         final byte[] state = new byte[size];
136         input.readFully(state);
137         delegate.restoreState(new RandomProviderDefaultState(state));
138     }
139 }