001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.rng.simple;
018
019import java.io.IOException;
020import java.io.ObjectOutputStream;
021import java.io.ObjectInputStream;
022import java.util.Random;
023import org.apache.commons.rng.RestorableUniformRandomProvider;
024import org.apache.commons.rng.core.RandomProviderDefaultState;
025
026/**
027 * Subclass of {@link Random} that {@link #next(int) delegates} to a
028 * {@link RestorableUniformRandomProvider} instance but will otherwise rely
029 * on the base class for generating all the random types.
030 *
031 * <p>Legacy applications coded against the JDK's API could use this subclass
032 * of {@link Random} in order to replace its linear congruential generator
033 * by any {@link RandomSource}.</p>
034 *
035 * <p>Caveat: Use of this class is <em>not</em> recommended for new applications.
036 * In particular, there is no guarantee that the serialized form of this class
037 * will be compatible across (even <em>minor</em>) releases of the library.</p>
038 *
039 * @since 1.0
040 */
041public final class JDKRandomBridge extends Random {
042    /** Serializable version identifier. */
043    private static final long serialVersionUID = 20161107L;
044    /** Source. */
045    private final RandomSource source;
046    /** Delegate. */
047    private transient RestorableUniformRandomProvider delegate;
048    /** Workaround JDK's "Random" bug: https://bugs.openjdk.java.net/browse/JDK-8154225. */
049    private final transient boolean isInitialized;
050
051    /**
052     * Creates a new instance.
053     *
054     * @param source Source of randomness.
055     * @param seed Seed.  Can be {@code null}.
056     */
057    public JDKRandomBridge(RandomSource source,
058                           Object seed) {
059        this.source = source;
060        delegate = source.create(seed);
061        isInitialized = true;
062    }
063
064    /** {@inheritDoc} */
065    @Override
066    public synchronized void setSeed(long seed) {
067        if (isInitialized) {
068            delegate = source.create(seed);
069
070            // Force the clearing of the "haveNextNextGaussian" flag
071            // (cf. Javadoc of the base class); the value passed here
072            // is irrelevant (since it will not be used).
073            super.setSeed(0L);
074        }
075    }
076
077    /**
078     * Delegates the generation of 32 random bits to the
079     * {@code RandomSource} argument provided at
080     * {@link #JDKRandomBridge(RandomSource,Object) construction}.
081     * The returned value is such that if the source of randomness is
082     * {@link RandomSource#JDK}, all the generated values will be identical
083     * to those produced by the same sequence of calls on a {@link Random}
084     * instance initialized with the same seed.
085     *
086     * @param n Number of random bits which the requested value must contain.
087     * @return the value represented by the {@code n} high-order bits of a
088     * pseudo-random 32-bits integer.
089     */
090    @Override
091    protected int next(int n) {
092        synchronized (this) {
093            return delegate.nextInt() >>> (32 - n);
094        }
095    }
096
097    /**
098     * Serialization method.
099     *
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}