Return-Path: Delivered-To: apmail-jackrabbit-commits-archive@www.apache.org Received: (qmail 84763 invoked from network); 11 Jul 2009 15:17:18 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 11 Jul 2009 15:17:18 -0000 Received: (qmail 46593 invoked by uid 500); 11 Jul 2009 15:17:28 -0000 Delivered-To: apmail-jackrabbit-commits-archive@jackrabbit.apache.org Received: (qmail 46513 invoked by uid 500); 11 Jul 2009 15:17:27 -0000 Mailing-List: contact commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@jackrabbit.apache.org Delivered-To: mailing list commits@jackrabbit.apache.org Received: (qmail 46504 invoked by uid 99); 11 Jul 2009 15:17:27 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 11 Jul 2009 15:17:27 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 11 Jul 2009 15:17:23 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 267782388877; Sat, 11 Jul 2009 15:17:02 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r793180 - in /jackrabbit/trunk/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/id/ test/java/org/apache/jackrabbit/core/id/ Date: Sat, 11 Jul 2009 15:17:01 -0000 To: commits@jackrabbit.apache.org From: jukka@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20090711151702.267782388877@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: jukka Date: Sat Jul 11 15:17:01 2009 New Revision: 793180 URL: http://svn.apache.org/viewvc?rev=793180&view=rev Log: JCR-1232: Merge UUID to NodeId Merged all remaining UUID functionality to NodeId. Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/id/SeededSecureRandom.java jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/id/ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/id/NodeIdTest.java jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/id/TestAll.java Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/id/NodeId.java Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/id/NodeId.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/id/NodeId.java?rev=793180&r1=793179&r2=793180&view=diff ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/id/NodeId.java (original) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/id/NodeId.java Sat Jul 11 15:17:01 2009 @@ -16,85 +16,224 @@ */ package org.apache.jackrabbit.core.id; -import org.apache.jackrabbit.uuid.UUID; +import java.util.Random; +import java.util.UUID; /** - * Node identifier. An instance of this class identifies a node using its UUID. - * Once created a node identifier instance is immutable. + * Node identifier, i.e. an immutable 128 bit UUID. */ -public class NodeId extends UUID implements ItemId { +public class NodeId implements ItemId, Comparable { /** * The serial version UID. */ - private static final long serialVersionUID = 7348217305215708805L; + private static final long serialVersionUID = 5773949574212570258L; /** - * Creates a new randomly generated node identifier. + * Returns a node identifier that is represented by the given UUID string. + * + * @param uuid the UUID string + * @return the node identifier + * @throws IllegalArgumentException if the given string is null + * or not a valid UUID + */ + public static NodeId valueOf(String uuid) throws IllegalArgumentException { + if (uuid != null) { + return new NodeId(uuid); + } else { + throw new IllegalArgumentException("NodeId.valueOf(null)"); + } + } + + /** + * The most significant 64 bits (bytes 0-7) of the UUID. */ - public NodeId() { - this(UUID.randomUUID()); + private final long msb; + + /** + * The least significant 64 bits (bytes 8-15) of the UUID. + */ + private final long lsb; + + /** + * Creates a node identifier from the given 128 bits. + * + * @param msb most significant 64 bits + * @param lsb least significant 64 bits + */ + public NodeId(long msb, long lsb) { + this.msb = msb; + this.lsb = lsb; } - private NodeId(UUID uuid) { - super(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits()); + /** + * Creates a node identifier from the given 16 bytes. + * + * @param bytes array of 16 bytes + * @throws NullPointerException if the given array is null + * @throws ArrayIndexOutOfBoundsException + * if the given array is less than 16 bytes long + */ + public NodeId(byte[] bytes) + throws NullPointerException, ArrayIndexOutOfBoundsException { + this( // Most significant 64 bits + ((((long) bytes[0]) & 0xFF) << 56) + + ((((long) bytes[1]) & 0xFF) << 48) + + ((((long) bytes[2]) & 0xFF) << 40) + + ((((long) bytes[3]) & 0xFF) << 32) + + ((((long) bytes[4]) & 0xFF) << 24) + + ((((long) bytes[5]) & 0xFF) << 16) + + ((((long) bytes[6]) & 0xFF) << 8) + + ((((long) bytes[7]) & 0xFF)), + // Least significant 64 bits + ((((long) bytes[8]) & 0xFF) << 56) + + ((((long) bytes[9]) & 0xFF) << 48) + + ((((long) bytes[10]) & 0xFF) << 40) + + ((((long) bytes[11]) & 0xFF) << 32) + + ((((long) bytes[12]) & 0xFF) << 24) + + ((((long) bytes[13]) & 0xFF) << 16) + + ((((long) bytes[14]) & 0xFF) << 8) + + ((((long) bytes[15]) & 0xFF))); + } + + /** + * Creates a node identifier from the given UUID. + * + * @param uuid UUID + */ + public NodeId(UUID uuid) { + this(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits()); } /** * Creates a node identifier from the given UUID string. * + * @see UUID#fromString(String) * @param uuid UUID string * @throws IllegalArgumentException if the UUID string is invalid */ public NodeId(String uuid) throws IllegalArgumentException { - super(uuid); + this(UUID.fromString(uuid)); } - public NodeId(byte[] bytes) { - super(bytes); + /** + * Creates a node identifier using the given random number generator. + * + * @param random random number generator + */ + public NodeId(Random random) { + this( // Most significant 64 bits, with version field set to 4 + random.nextLong() & 0xFfffFfffFfff0fffL | 0x0000000000004000L, + // Least significant 64 bits, with variant field set to IETF + random.nextLong() & 0x3fffFfffFfffFfffL | 0x8000000000000000L); } - public NodeId(long msb, long lsb) { - super(msb, lsb); + /** + * Creates a random node identifier using a secure random number generator. + */ + public NodeId() { + this(SeededSecureRandom.getInstance()); } /** - * Returns true as this class represents a node identifier, - * not a property identifier. + * Returns the 64 most significant bits of this identifier. + * + * @return 64 most significant bits + */ + public long getMostSignificantBits() { + return msb; + } + + /** + * Returns the 64 least significant bits of this identifier. + * + * @return 64 least significant bits + */ + public long getLeastSignificantBits() { + return lsb; + } + + /** + * Returns the 16 bytes of this identifier. + * + * @return newly allocated array of 16 bytes + */ + public byte[] getRawBytes() { + return new byte[] { + (byte) (msb >> 56), (byte) (msb >> 48), (byte) (msb >> 40), + (byte) (msb >> 32), (byte) (msb >> 24), (byte) (msb >> 16), + (byte) (msb >> 8), (byte) msb, + (byte) (lsb >> 56), (byte) (lsb >> 48), (byte) (lsb >> 40), + (byte) (lsb >> 32), (byte) (lsb >> 24), (byte) (lsb >> 16), + (byte) (lsb >> 8), (byte) lsb + }; + } + + //--------------------------------------------------------------< ItemId > + + /** + * Returns true to indicate that this is a node identifier. * * @return always true - * @see ItemId#denotesNode() */ public boolean denotesNode() { return true; } + //----------------------------------------------------------< Comparable > + + /** + * Compares this identifier to the given other one. + * + * @param that other identifier + * @return -1, 0 or +1 if this identifier is less than, equal to, + * or greater than the given other identifier + */ + public int compareTo(NodeId that) { + // This is not a 128 bit integer comparison! See also JCR-687. + if (msb < that.msb) { + return -1; + } else if (msb > that.msb) { + return 1; + } else if (lsb < that.lsb) { + return -1; + } else if (lsb > that.lsb) { + return 1; + } else { + return 0; + } + } + + //--------------------------------------------------------------< Object > + /** - * Returns the UUID of the identified node. + * Returns the UUID string representation of this identifier. * - * @return node UUID + * @see UUID#toString() + * @return UUID string */ - public UUID getUUID() { - return this; + public String toString() { + return new UUID(msb, lsb).toString(); } /** - * Returns a NodeId holding the value of the specified - * string. The string must be in the format returned by the - * NodeId.toString() method. + * Compares two UUID for equality. * - * @param s a String containing the NodeId - * representation to be parsed. - * @return the NodeId represented by the argument - * @throws IllegalArgumentException if the specified string can not be parsed - * as a NodeId. - * @see #toString() + * @see Object#equals(Object) */ - public static NodeId valueOf(String s) throws IllegalArgumentException { - if (s == null) { - throw new IllegalArgumentException("invalid NodeId literal"); - } - return new NodeId(s); + public boolean equals(Object that) { + return that instanceof NodeId + && msb == ((NodeId) that).msb + && lsb == ((NodeId) that).lsb; + } + + /** + * Returns a hash code of this identifier. + * + * @return hash code + */ + public int hashCode() { + return (int) ((msb >>> 32) ^ msb ^ (lsb >>> 32) ^ lsb); } } Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/id/SeededSecureRandom.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/id/SeededSecureRandom.java?rev=793180&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/id/SeededSecureRandom.java (added) +++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/id/SeededSecureRandom.java Sat Jul 11 15:17:01 2009 @@ -0,0 +1,98 @@ +/* + * 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.jackrabbit.core.id; + +import java.security.SecureRandom; +import java.util.Random; + +/** + * Automatically seeded singleton secure random number generator. + * + * @see JCR-1206: + * UUID generation: SecureRandom should be used by default + */ +class SeededSecureRandom extends SecureRandom implements Runnable { + + /** + * Maximum number of milliseconds to wait for the seeding. + */ + private static final int MAX_SEED_TIME = 1000; + + /** + * Singleton instance of this class. Initialized when first accessed. + */ + private static volatile Random instance = null; + + /** + * Returns the singleton instance of this class. The instance is + * created and seeded when this method is first called. + * + * @return seeded secure random number generator + */ + public static Random getInstance() { + if (instance == null) { + instance = new SeededSecureRandom(); + } + return instance; + } + + /** + * Flag to indicate whether seeding is complete. + */ + private volatile boolean seeded = false; + + /** + * Creates and seeds a secure random number generator. + */ + private SeededSecureRandom() { + // Can not do that in a static initializer block, because + // threads are not started after the initializer block exits + Thread thread = new Thread(this, "SeededSecureRandom"); + thread.start(); + try { + thread.join(MAX_SEED_TIME); + } catch (InterruptedException e) { + // ignore + } + + if (!seeded) { + // Alternative seed algorithm if the default is very slow + setSeed(System.currentTimeMillis()); + // Thread timing (a second thread is already running) + for (int j = 0; j < 16; j++) { + int i = 0; + long start = System.currentTimeMillis(); + while (start == System.currentTimeMillis()) { + i++; + } + // Supplement the existing seed + setSeed(i); + } + } + } + + /** + * Seeds this random number generator with 32 bytes of random data. + * Run in an initializer thread as this may be slow on some systems, see + * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6202721. + */ + public void run() { + setSeed(generateSeed(32)); + seeded = true; + } + +} Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/id/NodeIdTest.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/id/NodeIdTest.java?rev=793180&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/id/NodeIdTest.java (added) +++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/id/NodeIdTest.java Sat Jul 11 15:17:01 2009 @@ -0,0 +1,81 @@ +/* + * 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.jackrabbit.core.id; + +import junit.framework.TestCase; + +public class NodeIdTest extends TestCase { + + private static final NodeId[] ids = { + new NodeId(), // random id + new NodeId(0, 0), + new NodeId(-1, -1), + new NodeId("cafebabe-cafe-babe-cafe-babecafebabe") + }; + + public void testDenotesNode() { + for (NodeId id : ids) { + assertTrue(id.denotesNode()); + } + } + + public void testGetMostAndLeastSignificantBits() { + for (NodeId id : ids) { + long msb = id.getMostSignificantBits(); + long lsb = id.getLeastSignificantBits(); + assertEquals(id, new NodeId(msb, lsb)); + } + } + + public void testGetRawBytes() { + for (NodeId id : ids) { + assertEquals(id, new NodeId(id.getRawBytes())); + } + } + + public void testToString() { + for (NodeId id : ids) { + assertEquals(id, new NodeId(id.toString())); + } + } + + public void testCompareTo() { + for (NodeId id : ids) { + assertEquals(0, id.compareTo(id)); + } + + NodeId[] ordered = { + new NodeId(-1, -1), + new NodeId(-1, 0), + new NodeId(0, -1), + new NodeId(0, 0), + new NodeId(0, 1), + new NodeId(1, 0), + new NodeId(1, 1) + }; + for (int i = 0; i < ordered.length; i++) { + for (int j = 0; j < i; j++) { + assertEquals(1, ordered[i].compareTo(ordered[j])); + } + assertEquals(0, ordered[i].compareTo(ordered[i])); + for (int j = i + 1; j < ordered.length; j++) { + assertEquals(-1, ordered[i].compareTo(ordered[j])); + } + } + } + +} Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/id/TestAll.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/id/TestAll.java?rev=793180&view=auto ============================================================================== --- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/id/TestAll.java (added) +++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/id/TestAll.java Sat Jul 11 15:17:01 2009 @@ -0,0 +1,38 @@ +/* + * 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.jackrabbit.core.id; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Test suite that includes all test cases for the id module. + */ +public class TestAll extends TestCase { + + /** + * Returns a test suite that executes all tests inside this package. + * + * @return a test suite that executes all tests inside this package + */ + public static Test suite() { + TestSuite suite = new TestSuite("Identifier tests"); + suite.addTestSuite(NodeIdTest.class); + return suite; + } +}