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>
32   * Legacy applications coded against the JDK's API could use this subclass
33   * of {@link Random} in order to replace its linear congruential generator
34   * by any {@link RandomSource}.
35   * </p>
36   *
37   * Caveat: Use of this class is <em>not</em> recommended for new applications.
38   * In particular, there is no guarantee that the serialized form of this class
39   * will be compatible across (even <em>minor</em>) releases of the library.
40   *
41   * @since 1.0
42   */
43  public final class JDKRandomBridge extends Random {
44      /** Serializable version identifier. */
45      private static final long serialVersionUID = 20161107L;
46      /** Source. */
47      private final RandomSource source;
48      /** Delegate. */
49      private transient RestorableUniformRandomProvider delegate;
50      /** Workaround JDK's "Random" bug: https://bugs.openjdk.java.net/browse/JDK-8154225. */
51      private final transient boolean isInitialized;
52  
53      /**
54       * Creates a new instance.
55       *
56       * @param source Source of randomness.
57       * @param seed Seed.  Can be {@code null}.
58       */
59      public JDKRandomBridge(RandomSource source,
60                             Object seed) {
61          this.source = source;
62          delegate = RandomSource.create(source, seed);
63          isInitialized = true;
64      }
65  
66      /** {@inheritDoc} */
67      @Override
68      public synchronized void setSeed(long seed) {
69          if (isInitialized) {
70              delegate = RandomSource.create(source, seed);
71  
72              // Force the clearing of the "haveNextNextGaussian" flag
73              // (cf. Javadoc of the base class); the value passed here
74              // is irrelevant (since it will not be used).
75              super.setSeed(0L);
76          }
77      }
78  
79      /**
80       * Delegates the generation of 32 random bits to the
81       * {@code RandomSource} argument provided at
82       * {@link #JDKRandomBridge(RandomSource,Object) construction}.
83       * The returned value is such that if the source of randomness is
84       * {@link RandomSource#JDK}, all the generated values will be identical
85       * to those produced by the same sequence of calls on a {@link Random}
86       * instance initialized with the same seed.
87       *
88       * @param n Number of random bits which the requested value must contain.
89       * @return the value represented by the {@code n} high-order bits of a
90       * pseudo-random 32-bits integer.
91       */
92      @Override
93      protected int next(int n) {
94          synchronized (this) {
95              return delegate.nextInt() >>> (32 - n);
96          }
97      }
98  
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      * @param input Input stream.
121      * @throws IOException if an error occurs.
122      * @throws ClassNotFoundException if an error occurs.
123      */
124     private void readObject(ObjectInputStream input)
125         throws IOException,
126                ClassNotFoundException {
127         // Read non-transient fields.
128         input.defaultReadObject();
129 
130         // Recreate the "delegate" from serialized info.
131         delegate = RandomSource.create(source);
132         // And restore its state.
133         // Avoid the use of input.readObject() to deserialize by manually reading the byte[].
134         // Note: ObjectInputStream.readObject() will execute the readObject() method of the named
135         // class in the stream which may contain potentially malicious code.
136         final int size = input.readInt();
137         final byte[] state = new byte[size];
138         input.readFully(state);
139         delegate.restoreState(new RandomProviderDefaultState(state));
140     }
141 }