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     * @param output Output stream.
099     * @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}