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}