JDKRandomBridge.java

  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. import java.io.IOException;
  19. import java.io.ObjectOutputStream;
  20. import java.io.ObjectInputStream;
  21. import java.util.Random;
  22. import org.apache.commons.rng.RestorableUniformRandomProvider;
  23. import org.apache.commons.rng.core.RandomProviderDefaultState;

  24. /**
  25.  * Subclass of {@link Random} that {@link #next(int) delegates} to a
  26.  * {@link RestorableUniformRandomProvider} instance but will otherwise rely
  27.  * on the base class for generating all the random types.
  28.  *
  29.  * <p>Legacy applications coded against the JDK's API could use this subclass
  30.  * of {@link Random} in order to replace its linear congruential generator
  31.  * by any {@link RandomSource}.</p>
  32.  *
  33.  * <p>Caveat: Use of this class is <em>not</em> recommended for new applications.
  34.  * In particular, there is no guarantee that the serialized form of this class
  35.  * will be compatible across (even <em>minor</em>) releases of the library.</p>
  36.  *
  37.  * @since 1.0
  38.  */
  39. public final class JDKRandomBridge extends Random {
  40.     /** Serializable version identifier. */
  41.     private static final long serialVersionUID = 20161107L;
  42.     /** Source. */
  43.     private final RandomSource source;
  44.     /** Delegate. */
  45.     private transient RestorableUniformRandomProvider delegate;
  46.     /** Workaround JDK's "Random" bug: https://bugs.openjdk.java.net/browse/JDK-8154225. */
  47.     private final transient boolean isInitialized;

  48.     /**
  49.      * Creates a new instance.
  50.      *
  51.      * @param source Source of randomness.
  52.      * @param seed Seed.  Can be {@code null}.
  53.      */
  54.     public JDKRandomBridge(RandomSource source,
  55.                            Object seed) {
  56.         this.source = source;
  57.         delegate = source.create(seed);
  58.         isInitialized = true;
  59.     }

  60.     /** {@inheritDoc} */
  61.     @Override
  62.     public synchronized void setSeed(long seed) {
  63.         if (isInitialized) {
  64.             delegate = source.create(seed);

  65.             // Force the clearing of the "haveNextNextGaussian" flag
  66.             // (cf. Javadoc of the base class); the value passed here
  67.             // is irrelevant (since it will not be used).
  68.             super.setSeed(0L);
  69.         }
  70.     }

  71.     /**
  72.      * Delegates the generation of 32 random bits to the
  73.      * {@code RandomSource} argument provided at
  74.      * {@link #JDKRandomBridge(RandomSource,Object) construction}.
  75.      * The returned value is such that if the source of randomness is
  76.      * {@link RandomSource#JDK}, all the generated values will be identical
  77.      * to those produced by the same sequence of calls on a {@link Random}
  78.      * instance initialized with the same seed.
  79.      *
  80.      * @param n Number of random bits which the requested value must contain.
  81.      * @return the value represented by the {@code n} high-order bits of a
  82.      * pseudo-random 32-bits integer.
  83.      */
  84.     @Override
  85.     protected int next(int n) {
  86.         synchronized (this) {
  87.             return delegate.nextInt() >>> (32 - n);
  88.         }
  89.     }

  90.     /**
  91.      * Serialization method.
  92.      *
  93.      * @param output Output stream.
  94.      * @throws IOException if an error occurs.
  95.      */
  96.     private void writeObject(ObjectOutputStream output)
  97.         throws IOException {
  98.         synchronized (this) {
  99.             // Write non-transient fields.
  100.             output.defaultWriteObject();

  101.             // Save current state and size.
  102.             // Avoid the use of ObjectOutputStream.writeObject(Object) to save the state.
  103.             // This allows deserialization to avoid security issues in using readObject().
  104.             final byte[] state = ((RandomProviderDefaultState) delegate.saveState()).getState();
  105.             final int size = state.length;
  106.             output.writeInt(size);
  107.             output.write(state);
  108.         }
  109.     }

  110.     /**
  111.      * Deserialization method.
  112.      *
  113.      * @param input Input stream.
  114.      * @throws IOException if an error occurs.
  115.      * @throws ClassNotFoundException if an error occurs.
  116.      */
  117.     private void readObject(ObjectInputStream input)
  118.         throws IOException,
  119.                ClassNotFoundException {
  120.         // Read non-transient fields.
  121.         input.defaultReadObject();

  122.         // Recreate the "delegate" from serialized info.
  123.         delegate = source.create();
  124.         // And restore its state.
  125.         // Avoid the use of input.readObject() to deserialize by manually reading the byte[].
  126.         // Note: ObjectInputStream.readObject() will execute the readObject() method of the named
  127.         // class in the stream which may contain potentially malicious code.
  128.         final int size = input.readInt();
  129.         final byte[] state = new byte[size];
  130.         input.readFully(state);
  131.         delegate.restoreState(new RandomProviderDefaultState(state));
  132.     }
  133. }