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 java.util.concurrent.locks.ReentrantLock;
024import org.apache.commons.rng.RestorableUniformRandomProvider;
025import org.apache.commons.rng.core.RandomProviderDefaultState;
026
027/**
028 * Subclass of {@link Random} that {@link #next(int) delegates} to a
029 * {@link RestorableUniformRandomProvider} instance but will otherwise rely
030 * on the base class for generating all the random types.
031 *
032 * <p>Legacy applications coded against the JDK's API could use this subclass
033 * of {@link Random} in order to replace its linear congruential generator
034 * by any {@link RandomSource}.</p>
035 *
036 * <p>Caveat: Use of this class is <em>not</em> recommended for new applications.
037 * In particular, there is no guarantee that the serialized form of this class
038 * will be compatible across (even <em>minor</em>) releases of the library.</p>
039 *
040 * @since 1.0
041 */
042public final class JDKRandomBridge extends Random {
043    /** Serializable version identifier. */
044    private static final long serialVersionUID = 20161107L;
045    /** Source. */
046    private final RandomSource source;
047    /** Delegate. */
048    private transient RestorableUniformRandomProvider delegate;
049    /** Workaround JDK's "Random" bug: https://bugs.openjdk.java.net/browse/JDK-8154225. */
050    private transient boolean isInitialized;
051    /** An object to use for synchonized access to the delegate. */
052    private transient ReentrantLock instanceLock = new ReentrantLock();
053
054    /**
055     * Creates a new instance.
056     *
057     * @param source Source of randomness.
058     * @param seed Seed.  Can be {@code null}.
059     */
060    public JDKRandomBridge(RandomSource source,
061                           Object seed) {
062        this.source = source;
063        delegate = source.create(seed);
064        isInitialized = true;
065    }
066
067    /** {@inheritDoc} */
068    @Override
069    public synchronized void setSeed(long seed) {
070        if (isInitialized) {
071            try {
072                instanceLock.lock();
073                delegate = source.create(seed);
074
075                // Force the clearing of the "haveNextNextGaussian" flag
076                // (cf. Javadoc of the base class); the value passed here
077                // is irrelevant (since it will not be used).
078                super.setSeed(0L);
079            } finally {
080                instanceLock.unlock();
081            }
082        }
083    }
084
085    /**
086     * Delegates the generation of 32 random bits to the
087     * {@code RandomSource} argument provided at
088     * {@link #JDKRandomBridge(RandomSource,Object) construction}.
089     * The returned value is such that if the source of randomness is
090     * {@link RandomSource#JDK}, all the generated values will be identical
091     * to those produced by the same sequence of calls on a {@link Random}
092     * instance initialized with the same seed.
093     *
094     * @param n Number of random bits which the requested value must contain.
095     * @return the value represented by the {@code n} high-order bits of a
096     * pseudo-random 32-bits integer.
097     */
098    @Override
099    protected int next(int n) {
100        try {
101            instanceLock.lock();
102            return delegate.nextInt() >>> (32 - n);
103        } finally {
104            instanceLock.unlock();
105        }
106    }
107
108    /**
109     * Serialization method.
110     *
111     * @param output Output stream.
112     * @throws IOException if an error occurs.
113     */
114    private void writeObject(ObjectOutputStream output)
115        throws IOException {
116        try {
117            instanceLock.lock();
118            // Write non-transient fields.
119            output.defaultWriteObject();
120
121            // Save current state and size.
122            // Avoid the use of ObjectOutputStream.writeObject(Object) to save the state.
123            // This allows deserialization to avoid security issues in using readObject().
124            final byte[] state = ((RandomProviderDefaultState) delegate.saveState()).getState();
125            final int size = state.length;
126            output.writeInt(size);
127            output.write(state);
128        } finally {
129            instanceLock.unlock();
130        }
131    }
132
133    /**
134     * Deserialization method.
135     *
136     * @param input Input stream.
137     * @throws IOException if an error occurs.
138     * @throws ClassNotFoundException if an error occurs.
139     */
140    private void readObject(ObjectInputStream input)
141        throws IOException,
142               ClassNotFoundException {
143        // Read non-transient fields.
144        input.defaultReadObject();
145
146        // Recreate the "delegate" from serialized info.
147        instanceLock = new ReentrantLock();
148        delegate = source.create();
149        // And restore its state.
150        // Avoid the use of input.readObject() to deserialize by manually reading the byte[].
151        // Note: ObjectInputStream.readObject() will execute the readObject() method of the named
152        // class in the stream which may contain potentially malicious code.
153        final int size = input.readInt();
154        final byte[] state = new byte[size];
155        input.readFully(state);
156        delegate.restoreState(new RandomProviderDefaultState(state));
157        // Ensure support for setSeed(long) after deserialization
158        isInitialized = true;
159    }
160}