1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.id.uuid;
18
19 import org.apache.commons.id.IdentifierGenerator;
20
21 import java.security.NoSuchAlgorithmException;
22 import java.security.NoSuchProviderException;
23 import java.security.SecureRandom;
24 import java.util.Random;
25
26 /**
27 * <p>Class is responsible for generating version 4 UUID's per RFC 4122.
28 * This class attempts to use a java.security.SecureRandom with
29 * the following instantiation
30 * <code>SecureRandom.getInstance("SHA1PRNG", "SUN")</code>. If neither secure
31 * random implementation is avialable or an Exception is raised a java.util.Random
32 * is used.</p>
33 * <p>Note: Instantiation of SecureRandom is an expensive operation. The
34 * constructor therefore creates a static member to hold the SecureRandom.
35 * The first call to getInstance may take time; subsequent calls should return
36 * quickly.</p>
37 *
38 * @author Commons-Id team
39 * @version $Revision: 480488 $ $Date: 2006-11-29 08:57:26 +0000 (Wed, 29 Nov 2006) $
40 *
41 */
42 public final class VersionFourGenerator implements IdentifierGenerator, Constants {
43
44 /** Random used to generate UUID's */
45 private static final Random regularRandom = new Random();
46
47 /** SecureRandom used to generate UUID's */
48 private static Random secureRandom;
49
50 /** The pseudo-random number generator to use */
51 private static String usePRNG = "SHA1PRNG";
52
53 /** The pseudo-random number generator package name to use */
54 private static String usePRNGPackage = "SUN";
55
56 private static VersionFourGenerator generator;
57
58 /**
59 * <p>Constructs a new VersionFourGenerator.</p>
60 */
61 public VersionFourGenerator() {
62 super();
63 }
64
65 /**
66 * <p>Returns a singleton instance of the version four UUID generator.</p>
67 *
68 * @return the singleton instance of the version four UUID generator.
69 */
70 public static VersionFourGenerator getInstance() {
71 if (generator == null) {
72 generator = new VersionFourGenerator();
73 }
74 return generator;
75 }
76
77 /**
78 * <p>Returns a new version four UUID.</p>
79 *
80 * @return Object a new version 4 UUID.
81 */
82 public Object nextIdentifier() {
83 return nextUUID(false);
84 }
85
86 /**
87 * <p>Returns a new version four UUID.</p>
88 * <p>This overloaded method may produce both UUID's using a <code>SecureRandom</code> as well as using normal
89 * <code>Random</code>
90 * </p>
91 *
92 * @param secure indicates whether or not to use <code>SecureRandom</code> in generating the random bits.
93 * @return a new version four UUID that was generated by either a <code>Random</code> or <code>SecureRandom</code>.
94 */
95 public Object nextIdentifier(boolean secure) {
96 return nextUUID(secure);
97 }
98
99 /**
100 * <p>Returns a new version four UUID.</p>
101 *
102 * @return Object a new version 4 UUID.
103 */
104 public UUID nextUUID() {
105 //Call nextUUID with secure = false
106 return nextUUID(false);
107 }
108
109 /**
110 * <p>Returns a new version four UUID using either <code>SecureRandom</code> or <code>Random</code>.</p>
111 *
112 * @param secure boolean flag indicating whether to use <code>SecureRandom</code> or <code>Random</code>.
113 * @return a new version four UUID using either <code>SecureRandom</code> or <code>Random</code>.
114 */
115 private UUID nextUUID(boolean secure) {
116 byte[] raw = new byte[UUID_BYTE_LENGTH];
117 if (secure) {
118 //Initialize the secure random if null.
119 if (secureRandom == null) {
120 try {
121 if (usePRNGPackage != null) {
122 secureRandom = SecureRandom.getInstance(usePRNG, usePRNGPackage);
123 } else {
124 secureRandom = SecureRandom.getInstance(usePRNG);
125 }
126 } catch (NoSuchAlgorithmException nsae) {
127 secure = false; //Fail back to default PRNG/Random
128 } catch (NoSuchProviderException nspe) {
129 secure = false; //Fail back to default PRNG/Random
130 }
131 }
132 if (secureRandom != null) {
133 secureRandom.nextBytes(raw);
134 }
135 } else {
136 regularRandom.nextBytes(raw);
137 }
138
139 raw[TIME_HI_AND_VERSION_BYTE_6] &= 0x0F;
140 raw[TIME_HI_AND_VERSION_BYTE_6] |= (UUID.VERSION_FOUR << 4);
141
142 raw[CLOCK_SEQ_HI_AND_RESERVED_BYTE_8] &= 0x3F; //0011 1111
143 raw[CLOCK_SEQ_HI_AND_RESERVED_BYTE_8] |= 0x80; //1000 0000
144
145 return new UUID(raw);
146 }
147
148 /**
149 * <p>Allows clients to set the pseudo-random number generator implementation used when generating a version four uuid with
150 * the secure option. The secure option uses a <code>SecureRandom</code>. The packageName string may be null to specify
151 * no preferred package.</p>
152 *
153 * @param prngName the pseudo-random number generator implementation name. For example "SHA1PRNG".
154 * @param packageName the package name for the PRNG provider. For example "SUN".
155 */
156 public static void setPRNGProvider(String prngName, String packageName) {
157 VersionFourGenerator.usePRNG = prngName;
158 VersionFourGenerator.usePRNGPackage = packageName;
159 VersionFourGenerator.secureRandom = null;
160 }
161 }