OPENJPA-545 committing patch provided by Jeremy Bauer

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@671319 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Michael Dick 2008-06-24 19:48:23 +00:00
parent 37cb4af35d
commit fb78813259
16 changed files with 455 additions and 32 deletions

View File

@ -3545,6 +3545,10 @@ public class BrokerImpl
return UUIDHexSeq.getInstance();
case ValueStrategies.UUID_STRING:
return UUIDStringSeq.getInstance();
case ValueStrategies.UUID_TYPE4_HEX:
return UUIDType4HexSeq.getInstance();
case ValueStrategies.UUID_TYPE4_STRING:
return UUIDType4StringSeq.getInstance();
case ValueStrategies.SEQUENCE:
SequenceMetaData smd = (fmd == null)
? meta.getIdentitySequenceMetaData()

View File

@ -50,7 +50,7 @@ public class UUIDHexSeq
}
public synchronized Object next(StoreContext ctx, ClassMetaData meta) {
_last = UUIDGenerator.nextHex();
_last = UUIDGenerator.nextHex(UUIDGenerator.TYPE1);
return _last;
}

View File

@ -50,7 +50,7 @@ public class UUIDStringSeq
}
public synchronized Object next(StoreContext ctx, ClassMetaData meta) {
_last = UUIDGenerator.nextString();
_last = UUIDGenerator.nextString(UUIDGenerator.TYPE1);
return _last;
}

View File

@ -0,0 +1,66 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.kernel;
import org.apache.openjpa.lib.util.UUIDGenerator;
import org.apache.openjpa.meta.ClassMetaData;
/**
* Sequence for generating 32-character hex Type 4 UUID strings.
*
* @author Jeremy Bauer
*/
public class UUIDType4HexSeq
implements Seq {
private static final UUIDType4HexSeq _instance = new UUIDType4HexSeq();
private String _last = null;
/**
* Return the singleton instance.
*/
public static UUIDType4HexSeq getInstance() {
return _instance;
}
/**
* Hide constructor.
*/
private UUIDType4HexSeq() {
}
public void setType(int type) {
}
public synchronized Object next(StoreContext ctx, ClassMetaData meta) {
_last = UUIDGenerator.nextHex(UUIDGenerator.TYPE4);
return _last;
}
public synchronized Object current(StoreContext ctx, ClassMetaData meta) {
return _last;
}
public void allocate(int additional, StoreContext ctx, ClassMetaData meta) {
}
public void close() {
}
}

View File

@ -0,0 +1,66 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.kernel;
import org.apache.openjpa.lib.util.UUIDGenerator;
import org.apache.openjpa.meta.ClassMetaData;
/**
* Sequence for generating 16-character UUID strings.
*
* @author Jeremy Bauer
*/
public class UUIDType4StringSeq
implements Seq {
private static final UUIDType4StringSeq _instance = new UUIDType4StringSeq();
private String _last = null;
/**
* Return the singleton instance.
*/
public static UUIDType4StringSeq getInstance() {
return _instance;
}
/**
* Hide constructor.
*/
private UUIDType4StringSeq() {
}
public void setType(int type) {
}
public synchronized Object next(StoreContext ctx, ClassMetaData meta) {
_last = UUIDGenerator.nextString(UUIDGenerator.TYPE4);
return _last;
}
public synchronized Object current(StoreContext ctx, ClassMetaData meta) {
return _last;
}
public void allocate(int additional, StoreContext ctx, ClassMetaData meta) {
}
public void close() {
}
}

View File

@ -68,6 +68,16 @@ public class ValueStrategies {
*/
public static final int UUID_HEX = 6;
/**
* "uuid-type4-string" value strategy.
*/
public static final int UUID_TYPE4_STRING = 7;
/**
* "uuid-type4-hex" value strategy.
*/
public static final int UUID_TYPE4_HEX = 8;
private static final Localizer _loc = Localizer.forPackage
(ValueStrategies.class);
@ -82,6 +92,8 @@ public class ValueStrategies {
_map.put("increment", Numbers.valueOf(INCREMENT));
_map.put("uuid-string", Numbers.valueOf(UUID_STRING));
_map.put("uuid-hex", Numbers.valueOf(UUID_HEX));
_map.put("uuid-type4-string", Numbers.valueOf(UUID_TYPE4_STRING));
_map.put("uuid-type4-hex", Numbers.valueOf(UUID_TYPE4_HEX));
}
/**

View File

@ -160,9 +160,13 @@ public class ImplHelper {
return JavaTypes.convert(smd.getInstance(ctx.getClassLoader()).
next(ctx, meta), typeCode);
case ValueStrategies.UUID_STRING:
return UUIDGenerator.nextString();
return UUIDGenerator.nextString(UUIDGenerator.TYPE1);
case ValueStrategies.UUID_HEX:
return UUIDGenerator.nextHex();
return UUIDGenerator.nextHex(UUIDGenerator.TYPE1);
case ValueStrategies.UUID_TYPE4_STRING:
return UUIDGenerator.nextString(UUIDGenerator.TYPE4);
case ValueStrategies.UUID_TYPE4_HEX:
return UUIDGenerator.nextHex(UUIDGenerator.TYPE4);
default:
return null;
}

View File

@ -22,16 +22,18 @@ import java.io.IOException;
import java.net.InetAddress;
import java.security.SecureRandom;
import java.util.Random;
import java.util.UUID;
import org.apache.commons.lang.exception.NestableRuntimeException;
/**
* UUID value generator. Based on the time-based generator in the Apache
* Commons Id project: http://jakarta.apache.org/commons/sandbox/id/uuid.html
* UUID value generator. Type 1 generator is based on the time-based generator
* in the Apache Commons Id project: http://jakarta.apache.org/commons/sandbox
* /id/uuid.html The type 4 generator uses the standard Java UUID generator.
*
* The code has been vastly simplified and modified to replace the ethernet
* address of the host machine with the IP, since we do not want to require
* native libs and Java cannot access the MAC address directly.
* The type 1 code has been vastly simplified and modified to replace the
* ethernet address of the host machine with the IP, since we do not want to
* require native libs and Java cannot access the MAC address directly.
*
* In spirit, implements the IETF UUID draft specification, found here:<br />
* http://www1.ics.uci.edu/~ejw/authoring/uuid-guid/draft-leach-uuids-guids-01
@ -43,6 +45,10 @@ import org.apache.commons.lang.exception.NestableRuntimeException;
*/
public class UUIDGenerator {
// supported UUID types
public static final int TYPE1 = 1;
public static final int TYPE4 = 4;
// indexes within the uuid array for certain boundaries
private static final byte IDX_TIME_HI = 6;
private static final byte IDX_TYPE = 6; // multiplexed
@ -68,13 +74,12 @@ public class UUIDGenerator {
private final static byte TYPE_TIME_BASED = 0x10;
// random number generator used to reduce conflicts with other JVMs, and
// hasher for strings. note that secure random is very slow the first time
// it is used; consider switching to a standard random
private static final Random RANDOM = new SecureRandom();
// hasher for strings.
private static Random RANDOM;
// 4-byte IP address + 2 random bytes to compensate for the fact that
// the MAC address is usually 6 bytes
private static final byte[] IP;
private static byte[] IP;
// counter is initialized to 0 and is incremented for each uuid request
// within the same timestamp window.
@ -88,12 +93,21 @@ public class UUIDGenerator {
// when it overflows
private static long _lastMillis = 0L;
private static final int MAX_14BIT = 0x3FFF;
private static short _seq = (short) RANDOM.nextInt(MAX_14BIT);
private static short _seq = 0;
/*
* Static initializer to get the IP address of the host machine.
* Initializer for type 1 UUIDs. Creates random generator and genenerates
* the node portion of the UUID using the IP address.
*/
static {
private static synchronized void initializeForType1()
{
if (RANDOM != null)
return;
// note that secure random is very slow the first time
// it is used; consider switching to a standard random
RANDOM = new SecureRandom();
_seq = (short) RANDOM.nextInt(MAX_14BIT);
byte[] ip = null;
try {
ip = InetAddress.getLocalHost().getAddress();
@ -103,13 +117,25 @@ public class UUIDGenerator {
IP = new byte[6];
RANDOM.nextBytes(IP);
System.arraycopy(ip, 0, IP, 2, ip.length);
System.arraycopy(ip, 0, IP, 2, ip.length);
}
/**
* Return a unique UUID value.
*/
public static byte[] next() {
public static byte[] next(int type) {
if (type == TYPE4) {
return createType4();
}
return createType1();
}
/*
* Creates a type 1 UUID
*/
public static byte[] createType1() {
if (RANDOM == null)
initializeForType1();
// set ip addr
byte[] uuid = new byte[16];
System.arraycopy(IP, 0, uuid, 10, IP.length);
@ -147,11 +173,32 @@ public class UUIDGenerator {
return uuid;
}
/*
* Creates a type 4 UUID
*/
private static byte[] createType4() {
UUID type4 = UUID.randomUUID();
byte[] uuid = new byte[16];
longToBytes(type4.getMostSignificantBits(), uuid, 0);
longToBytes(type4.getLeastSignificantBits(), uuid, 8);
return uuid;
}
/*
* Converts a long to byte values, setting them in a byte array
* at a given starting position.
*/
private static void longToBytes(long longVal, byte[] buf, int sPos) {
sPos += 7;
for(int i = 0; i < 8; i++)
buf[sPos-i] = (byte)(longVal >>> (i * 8));
}
/**
* Return the next unique uuid value as a 16-character string.
*/
public static String nextString() {
byte[] bytes = next();
public static String nextString(int type) {
byte[] bytes = next(type);
try {
return new String(bytes, "ISO-8859-1");
} catch (Exception e) {
@ -162,8 +209,8 @@ public class UUIDGenerator {
/**
* Return the next unique uuid value as a 32-character hex string.
*/
public static String nextHex() {
return Base16Encoder.encode(next());
public static String nextHex(int type) {
return Base16Encoder.encode(next(type));
}
/**
@ -174,6 +221,8 @@ public class UUIDGenerator {
*/
// package-visibility for testing
static long getTime() {
if (RANDOM == null)
initializeForType1();
long newTime = getUUIDTime();
if (newTime <= _lastMillis) {
incrementSequence();

View File

@ -33,13 +33,39 @@ public class TestUUIDGenerator extends TestCase {
public void testUniqueString() {
Set seen = new HashSet();
for (int i = 0; i < 10000; i++)
assertTrue(seen.add(UUIDGenerator.nextString()));
assertTrue(seen.add(
UUIDGenerator.nextString(UUIDGenerator.TYPE1)));
}
public void testUniqueHex() {
Set seen = new HashSet();
for (int i = 0; i < 10000; i++)
assertTrue(seen.add(UUIDGenerator.nextHex()));
assertTrue(seen.add(
UUIDGenerator.nextHex(UUIDGenerator.TYPE1)));
}
public void testUniqueType4String() {
Set seen = new HashSet();
for (int i = 0; i < 10000; i++)
assertTrue(seen.add(
UUIDGenerator.nextString(UUIDGenerator.TYPE4)));
}
public void testUniqueType4Hex() {
Set seen = new HashSet();
for (int i = 0; i < 10000; i++)
assertTrue(seen.add(
UUIDGenerator.nextHex(UUIDGenerator.TYPE4)));
}
public void testUniqueMixedTypesHex() {
Set seen = new HashSet();
for (int i = 0; i < 10000; i++) {
int type = (i % 2 == 0) ?
UUIDGenerator.TYPE4 : UUIDGenerator.TYPE1;
assertTrue(seen.add(
UUIDGenerator.nextHex(type)));
}
}
public void testGetTime() {

View File

@ -43,14 +43,31 @@ public class GeneratedValues {
sequenceName="org.apache.openjpa.persistence.generationtype.CustomSeq()")
private int customSeqWithIndirectionField;
@GeneratedValue(generator="uuid-hex")
private String uuidhex;
@GeneratedValue(generator="uuid-string")
private String uuidstring;
@GeneratedValue(generator="uuid-type4-hex")
private String uuidT4hex;
@GeneratedValue(generator="uuid-type4-string")
private String uuidT4string;
public GeneratedValues() {
super();
}
public GeneratedValues(int id, long field) {
public GeneratedValues(int id, long field, String uh, String us,
String ut4h, String ut4s) {
super();
this.id = id;
this.field = field;
this.uuidhex = uh;
this.uuidstring = us;
this.uuidT4hex = ut4h;
this.uuidT4string = ut4s;
}
public int getId() {
@ -76,4 +93,36 @@ public class GeneratedValues {
public int getCustomSeqWithIndirectionField() {
return customSeqWithIndirectionField;
}
public void setUuidhex(String uuidhex) {
this.uuidhex = uuidhex;
}
public String getUuidhex() {
return uuidhex;
}
public void setUuidstring(String uuidstring) {
this.uuidstring = uuidstring;
}
public String getUuidstring() {
return uuidstring;
}
public void setUuidT4hex(String uuidT4hex) {
this.uuidT4hex = uuidT4hex;
}
public String getUuidT4hex() {
return uuidT4hex;
}
public void setUuidT4string(String uuidT4string) {
this.uuidT4string = uuidT4string;
}
public String getUuidT4string() {
return uuidT4string;
}
}

View File

@ -46,12 +46,16 @@ public class TestGeneratedValues extends SingleEMFTestCase {
assertFalse(gv.getId() == gv2.getId());
assertFalse(gv.getField() == gv2.getField());
assertFalse(gv.getUuidstring().equals(gv2.getUuidstring()));
assertFalse(gv.getUuidhex().equals(gv2.getUuidhex()));
assertFalse(gv.getUuidT4hex().equals(gv2.getUuidT4hex()));
assertFalse(gv.getUuidT4string().equals(gv2.getUuidT4string()));
}
public void testInitialValues() {
EntityManager em = emf.createEntityManager();
GeneratedValues gv = new GeneratedValues(7, 9);
GeneratedValues gv = new GeneratedValues(7, 9, "a", "b", "c", "d");
try {
em.getTransaction().begin();
@ -133,4 +137,82 @@ public class TestGeneratedValues extends SingleEMFTestCase {
assertNotEquals(0, gv.getCustomSeqWithIndirectionField());
}
public void testUUIDGenerators() {
EntityManager em = emf.createEntityManager();
GeneratedValues gv = new GeneratedValues();
em.getTransaction().begin();
em.persist(gv);
em.getTransaction().commit();
int id = gv.getId();
assertTrue(isStringUUID(gv.getUuidT4string(), 4));
assertTrue(isStringUUID(gv.getUuidstring(), 1));
assertTrue(isHexUUID(gv.getUuidhex(), 1));
assertTrue(isHexUUID(gv.getUuidT4hex(), 4));
em.clear();
GeneratedValues gv2 = em.find(GeneratedValues.class, id);
assertNotNull(gv2);
// The string value could contain null values and such so length
// calculations may be non-deterministic. For string generators,
// simply ensure the fields are populated (not null).
assertNotNull(gv2.getUuidstring());
assertTrue(isHexUUID(gv2.getUuidhex(), 1));
assertNotNull(gv2.getUuidT4string());
assertTrue(isHexUUID(gv2.getUuidT4hex(), 4));
// Compare original hex values with new values. They should be equal.
// Note: UUID 'string' values are not compared. In most cases they will
// be the same, but in an environment where data is converted to
// a considerably different character encoding of the database (ex.
// Unicode -> EBCDIC) upon persistence, the uuid string returned by the
// database may not be equal to the original value. This is a common
// issue with string data, but even more likely for a uuids given that
// uuid strings are produced from pseudo-random byte arrays, which yield
// all sorts of variant characters.
assertTrue(gv.getId() == gv2.getId());
assertTrue(gv.getField() == gv2.getField());
assertTrue(gv.getUuidhex().equals(gv2.getUuidhex()));
assertTrue(gv.getUuidT4hex().equals(gv2.getUuidT4hex()));
}
/*
* Verify a uuid string is 16 characters long and is the expected type.
*/
private boolean isStringUUID(String value, int type) {
if (value.length() != 16)
return false;
byte version = (byte)(value.charAt(6) >>> 4);
if (type != version) return false;
return true;
}
/*
* Verify a uuid hex string value is 32 characters long, consists entirely
* of hex digits and is the correct version.
*/
private boolean isHexUUID(String value, int type) {
if (value.length() != 32)
return false;
char[] chArr = value.toCharArray();
for (int i = 0; i < 32; i++)
{
char ch = chArr[i];
if (!(Character.isDigit(ch) ||
(ch >= 'a' && ch <= 'f') ||
(ch >= 'A' && ch <= 'F')))
return false;
if (i == 12) {
if (type == 1 && ch != '1')
return false;
if (type == 4 && ch != '4')
return false;
}
}
return true;
}
}

View File

@ -41,6 +41,18 @@ public class ExtensionsEntity {
@GeneratedValue(generator = "uuid-hex")
@Column(name = "UUID_HEX")
private String uuid;
@GeneratedValue(generator = "uuid-string")
@Column(name = "UUID_STRING")
private String uuidString;
@GeneratedValue(generator = "uuid-type4-hex")
@Column(name = "UUIDT4_HEX")
private String uuidT4Hex;
@GeneratedValue(generator = "uuid-type4-string")
@Column(name = "UUIDT4_STRING")
private String uuidT4String;
@Basic(fetch = FetchType.LAZY)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "system")

View File

@ -72,6 +72,12 @@ public class TestExtensionAnnotations
public void testValueStrategy() {
assertEquals(ValueStrategies.UUID_HEX,
_mapping.getField("uuid").getValueStrategy());
assertEquals(ValueStrategies.UUID_STRING,
_mapping.getField("uuidString").getValueStrategy());
assertEquals(ValueStrategies.UUID_TYPE4_HEX,
_mapping.getField("uuidT4Hex").getValueStrategy());
assertEquals(ValueStrategies.UUID_TYPE4_STRING,
_mapping.getField("uuidT4String").getValueStrategy());
FieldMapping seq = _mapping.getFieldMapping("seq");
assertEquals(ValueStrategies.SEQUENCE, seq.getValueStrategy());
assertEquals("system", seq.getValueSequenceName());

View File

@ -1220,6 +1220,10 @@ public class AnnotationPersistenceMetaDataParser
return ValueStrategies.UUID_HEX;
if (Generator.UUID_STRING.equals(generator))
return ValueStrategies.UUID_STRING;
if (Generator.UUID_TYPE4_HEX.equals(generator))
return ValueStrategies.UUID_TYPE4_HEX;
if (Generator.UUID_TYPE4_STRING.equals(generator))
return ValueStrategies.UUID_TYPE4_STRING;
throw new MetaDataException(_loc.get("generator-bad-strategy",
context, generator));
}

View File

@ -29,6 +29,8 @@ public interface Generator {
public static final String UUID_HEX = "uuid-hex";
public static final String UUID_STRING = "uuid-string";
public static final String UUID_TYPE4_STRING = "uuid-type4-string";
public static final String UUID_TYPE4_HEX = "uuid-type4-hex";
/**
* The sequence name.

View File

@ -830,7 +830,7 @@ on any field, not just identity fields. Before using the <literal>IDENTITY
<xref linkend="ref_guide_pc_oid_pkgen_autoinc"/> in the Reference Guide.
</para>
<para>
OpenJPA also offers two additional generator strategies for non-numeric fields,
OpenJPA also offers additional generator strategies for non-numeric fields,
which you can access by setting <literal>strategy</literal> to <literal>AUTO
</literal> (the default), and setting the <literal>generator</literal> string
to:
@ -851,9 +851,9 @@ to:
uuid-string
</primary>
</indexterm>
<literal>uuid-string</literal>: OpenJPA will generate a 128-bit UUID unique
within the network, represented as a 16-character string. For more information
on UUIDs, see the IETF UUID draft specification at:
<literal>uuid-string</literal>: OpenJPA will generate a 128-bit type 1 UUID
unique within the network, represented as a 16-character string. For more
information on UUIDs, see the IETF UUID draft specification at:
<ulink url="http://www1.ics.uci.edu/~ejw/authoring/uuid-guid/">
http://www1.ics.uci.edu/~ejw/authoring/uuid-guid/</ulink>
</para>
@ -874,7 +874,48 @@ http://www1.ics.uci.edu/~ejw/authoring/uuid-guid/</ulink>
</primary>
</indexterm>
<literal>uuid-hex</literal>: Same as <literal> uuid-string</literal>, but
represents the UUID as a 32-character hexadecimal string.
represents the type 1 UUID as a 32-character hexadecimal string.
</para>
</listitem>
<listitem>
<para>
<indexterm>
<primary>
mapping metadata
</primary>
<secondary>
uuid-type4-string
</secondary>
</indexterm>
<indexterm>
<primary>
uuid-type4-string
</primary>
</indexterm>
<literal>uuid-type4-string</literal>: OpenJPA will generate a 128-bit type 4
pseudo-random UUID, represented as a 16-character string. For more
information on UUIDs, see the IETF UUID draft specification at:
<ulink url="http://www1.ics.uci.edu/~ejw/authoring/uuid-guid/">
http://www1.ics.uci.edu/~ejw/authoring/uuid-guid/</ulink>
</para>
</listitem>
<listitem>
<para>
<indexterm>
<primary>
mapping metadata
</primary>
<secondary>
uuid-type4-hex
</secondary>
</indexterm>
<indexterm>
<primary>
uuid-type4-hex
</primary>
</indexterm>
<literal>uuid-type4-hex</literal>: Same as <literal> uuid-type4-string</literal>
, but represents the type 4 UUID as a 32-character hexadecimal string.
</para>
</listitem>
</itemizedlist>