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>
032 * 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}.
035 * </p>
036 *
037 * Caveat: Use of this class is <em>not</em> recommended for new applications.
038 * In particular, there is no guarantee that the serialized form of this class
039 * will be compatible across (even <em>minor</em>) releases of the library.
040 *
041 * @since 1.0
042 */
043public final class JDKRandomBridge extends Random {
044    /** Serializable version identifier. */
045    private static final long serialVersionUID = 20161107L;
046    /** Source. */
047    private final RandomSource source;
048    /** Delegate. */
049    private transient RestorableUniformRandomProvider delegate;
050    /** Workaround JDK's "Random" bug: https://bugs.openjdk.java.net/browse/JDK-8154225. */
051    private final transient boolean isInitialized;
052
053    /**
054     * Creates a new instance.
055     *
056     * @param source Source of randomness.
057     * @param seed Seed.  Can be {@code null}.
058     */
059    public JDKRandomBridge(RandomSource source,
060                           Object seed) {
061        this.source = source;
062        delegate = source.create(seed);
063        isInitialized = true;
064    }
065
066    /** {@inheritDoc} */
067    @Override
068    public synchronized void setSeed(long seed) {
069        if (isInitialized) {
070            delegate = source.create(seed);
071
072            // Force the clearing of the "haveNextNextGaussian" flag
073            // (cf. Javadoc of the base class); the value passed here
074            // is irrelevant (since it will not be used).
075            super.setSeed(0L);
076        }
077    }
078
079    /**
080     * Delegates the generation of 32 random bits to the
081     * {@code RandomSource} argument provided at
082     * {@link #JDKRandomBridge(RandomSource,Object) construction}.
083     * The returned value is such that if the source of randomness is
084     * {@link RandomSource#JDK}, all the generated values will be identical
085     * to those produced by the same sequence of calls on a {@link Random}
086     * instance initialized with the same seed.
087     *
088     * @param n Number of random bits which the requested value must contain.
089     * @return the value represented by the {@code n} high-order bits of a
090     * pseudo-random 32-bits integer.
091     */
092    @Override
093    protected int next(int n) {
094        synchronized (this) {
095            return delegate.nextInt() >>> (32 - n);
096        }
097    }
098
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     * @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 = source.create();
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}