QPID-7827: Delegate the creation of UUIDs to factory to avoid proliferation of UUID instances representing the same underlying UUID.
Cherry picked from bc58bc3093eba424f8bcd125399db0e9bc1e7fd6
diff --git a/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/AbstractBDBMessageStore.java b/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/AbstractBDBMessageStore.java
index 573d287..59ac2c2 100644
--- a/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/AbstractBDBMessageStore.java
+++ b/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/AbstractBDBMessageStore.java
@@ -71,6 +71,7 @@
import org.apache.qpid.server.store.handler.DistributedTransactionHandler;
import org.apache.qpid.server.store.handler.MessageHandler;
import org.apache.qpid.server.store.handler.MessageInstanceHandler;
+import org.apache.qpid.server.util.CachingUUIDFactory;
public abstract class AbstractBDBMessageStore implements MessageStore
@@ -616,9 +617,8 @@
{
DatabaseEntry key = new DatabaseEntry();
- QueueEntryBinding keyBinding = QueueEntryBinding.getInstance();
- QueueEntryKey dd = new QueueEntryKey(queue.getId(), messageId);
- keyBinding.objectToEntry(dd, key);
+ QueueEntryKey queueEntryKey = new QueueEntryKey(queue.getId(), messageId);
+ QueueEntryBinding.objectToEntry(queueEntryKey, key);
DatabaseEntry value = new DatabaseEntry();
value.setData(ENQUEUE_RECORD_VALUE, 0, 1);
@@ -658,10 +658,9 @@
{
DatabaseEntry key = new DatabaseEntry();
- QueueEntryBinding keyBinding = QueueEntryBinding.getInstance();
QueueEntryKey queueEntryKey = new QueueEntryKey(queueId, messageId);
UUID id = queueId;
- keyBinding.objectToEntry(queueEntryKey, key);
+ QueueEntryBinding.objectToEntry(queueEntryKey, key);
getLogger().debug("Dequeue message id {} from queue with id {}", messageId, id);
@@ -705,8 +704,7 @@
DatabaseEntry value = new DatabaseEntry();
PreparedTransaction preparedTransaction = new PreparedTransaction(enqueues, dequeues);
- PreparedTransactionBinding valueBinding = new PreparedTransactionBinding();
- valueBinding.objectToEntry(preparedTransaction, value);
+ PreparedTransactionBinding.objectToEntry(preparedTransaction, value);
for(org.apache.qpid.server.store.Transaction.EnqueueRecord enqueue : enqueues)
{
StoredMessage storedMessage = enqueue.getMessage().getStoredMessage();
@@ -1531,13 +1529,13 @@
DatabaseEntry value = new DatabaseEntry();
value.setPartial(0, 0, true);
- QueueEntryBinding keyBinding = QueueEntryBinding.getInstance();
- keyBinding.objectToEntry(new QueueEntryKey(queue.getId(), 0l), key);
+ CachingUUIDFactory uuidFactory = new CachingUUIDFactory();
+ QueueEntryBinding.objectToEntry(new QueueEntryKey(queue.getId(), 0L), key);
if (!searchCompletedSuccessfully && (searchCompletedSuccessfully =
cursor.getSearchKeyRange(key, value, LockMode.READ_UNCOMMITTED) == OperationStatus.SUCCESS))
{
- QueueEntryKey entry = keyBinding.entryToObject(key);
+ QueueEntryKey entry = QueueEntryBinding.entryToObject(uuidFactory, key);
if (entry.getQueueId().equals(queue.getId()))
{
entries.add(entry);
@@ -1548,7 +1546,7 @@
{
while (cursor.getNext(key, value, LockMode.READ_UNCOMMITTED) == OperationStatus.SUCCESS)
{
- QueueEntryKey entry = keyBinding.entryToObject(key);
+ QueueEntryKey entry = QueueEntryBinding.entryToObject(uuidFactory, key);
if (entry.getQueueId().equals(queue.getId()))
{
entries.add(entry);
@@ -1588,13 +1586,13 @@
try(Cursor cursor = getDeliveryDb().openCursor(null, null))
{
DatabaseEntry key = new DatabaseEntry();
- QueueEntryBinding keyBinding = QueueEntryBinding.getInstance();
+ CachingUUIDFactory uuidFactory = new CachingUUIDFactory();
DatabaseEntry value = new DatabaseEntry();
value.setPartial(0, 0, true);
while (cursor.getNext(key, value, LockMode.READ_UNCOMMITTED) == OperationStatus.SUCCESS)
{
- QueueEntryKey entry = keyBinding.entryToObject(key);
+ QueueEntryKey entry = QueueEntryBinding.entryToObject(uuidFactory, key);
entries.add(entry);
}
}
@@ -1622,15 +1620,15 @@
try(Cursor cursor = getXidDb().openCursor(null, null))
{
+ CachingUUIDFactory uuidFactory = new CachingUUIDFactory();
DatabaseEntry key = new DatabaseEntry();
XidBinding keyBinding = XidBinding.getInstance();
- PreparedTransactionBinding valueBinding = new PreparedTransactionBinding();
DatabaseEntry value = new DatabaseEntry();
while (cursor.getNext(key, value, LockMode.READ_UNCOMMITTED) == OperationStatus.SUCCESS)
{
Xid xid = keyBinding.entryToObject(key);
- PreparedTransaction preparedTransaction = valueBinding.entryToObject(value);
+ PreparedTransaction preparedTransaction = PreparedTransactionBinding.entryToObject(uuidFactory, value);
if (!handler.handle(new BDBStoredXidRecord(xid.getFormat(), xid.getGlobalId(), xid.getBranchId()),
preparedTransaction.getEnqueues(), preparedTransaction.getDequeues()))
{
diff --git a/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/PreparedTransactionBinding.java b/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/PreparedTransactionBinding.java
index 4d111e5..23e302a 100644
--- a/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/PreparedTransactionBinding.java
+++ b/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/PreparedTransactionBinding.java
@@ -26,6 +26,8 @@
import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.bind.tuple.TupleInput;
import com.sleepycat.bind.tuple.TupleOutput;
+import com.sleepycat.je.DatabaseEntry;
+
import org.apache.qpid.server.message.EnqueueableMessage;
import org.apache.qpid.server.store.MessageDurability;
import org.apache.qpid.server.store.MessageEnqueueRecord;
@@ -34,49 +36,53 @@
import org.apache.qpid.server.store.TransactionLogResource;
import org.apache.qpid.server.store.berkeleydb.AbstractBDBMessageStore;
import org.apache.qpid.server.store.berkeleydb.entry.PreparedTransaction;
+import org.apache.qpid.server.util.CachingUUIDFactory;
-public class PreparedTransactionBinding extends TupleBinding<PreparedTransaction>
+public class PreparedTransactionBinding
{
- @Override
- public PreparedTransaction entryToObject(TupleInput input)
+ private PreparedTransactionBinding()
{
- Transaction.EnqueueRecord[] enqueues = readEnqueueRecords(input);
+ }
- Transaction.DequeueRecord[] dequeues = readDequeueRecords(input);
-
+ public static PreparedTransaction entryToObject(final CachingUUIDFactory uuidFactory, final DatabaseEntry value)
+ {
+ TupleInput input = TupleBinding.entryToInput(value);
+ Transaction.EnqueueRecord[] enqueues = readEnqueueRecords(uuidFactory, input);
+ Transaction.DequeueRecord[] dequeues = readDequeueRecords(uuidFactory, input);
return new PreparedTransaction(enqueues, dequeues);
}
- private Transaction.EnqueueRecord[] readEnqueueRecords(TupleInput input)
+ private static Transaction.EnqueueRecord[] readEnqueueRecords(final CachingUUIDFactory uuidFactory, TupleInput input)
{
Transaction.EnqueueRecord[] records = new Transaction.EnqueueRecord[input.readInt()];
for(int i = 0; i < records.length; i++)
{
- records[i] = new EnqueueRecordImpl(new UUID(input.readLong(), input.readLong()), input.readLong());
+ UUID queueId = uuidFactory.createUuidFromBits(input.readLong(), input.readLong());
+ records[i] = new EnqueueRecordImpl(queueId, input.readLong());
}
return records;
}
- private Transaction.DequeueRecord[] readDequeueRecords(TupleInput input)
+ private static Transaction.DequeueRecord[] readDequeueRecords(final CachingUUIDFactory uuidFactory, TupleInput input)
{
Transaction.DequeueRecord[] records = new Transaction.DequeueRecord[input.readInt()];
for(int i = 0; i < records.length; i++)
{
- records[i] = new DequeueRecordImpl(new UUID(input.readLong(), input.readLong()), input.readLong());
+ UUID queueId = uuidFactory.createUuidFromBits(input.readLong(), input.readLong());
+ records[i] = new DequeueRecordImpl(queueId, input.readLong());
}
return records;
}
-
- @Override
- public void objectToEntry(PreparedTransaction preparedTransaction, TupleOutput output)
+ public static void objectToEntry(final PreparedTransaction preparedTransaction, final DatabaseEntry value)
{
- writeRecords(preparedTransaction.getEnqueues(), output);
- writeRecords(preparedTransaction.getDequeues(), output);
-
+ TupleOutput tupleOutput = new TupleOutput();
+ writeRecords(preparedTransaction.getEnqueues(), tupleOutput);
+ writeRecords(preparedTransaction.getDequeues(), tupleOutput);
+ TupleBinding.outputToEntry(tupleOutput, value);
}
- private void writeRecords(Transaction.EnqueueRecord[] records, TupleOutput output)
+ private static void writeRecords(Transaction.EnqueueRecord[] records, TupleOutput output)
{
if(records == null)
{
@@ -95,7 +101,7 @@
}
}
- private void writeRecords(Transaction.DequeueRecord[] records, TupleOutput output)
+ private static void writeRecords(Transaction.DequeueRecord[] records, TupleOutput output)
{
if(records == null)
{
@@ -120,7 +126,7 @@
private long _messageNumber;
private UUID _queueId;
- public EnqueueRecordImpl(UUID queueId, long messageNumber)
+ EnqueueRecordImpl(UUID queueId, long messageNumber)
{
_messageNumber = messageNumber;
_queueId = queueId;
@@ -175,7 +181,7 @@
private final AbstractBDBMessageStore.BDBEnqueueRecord _record;
- public DequeueRecordImpl(final UUID queueId, final long messageNumber)
+ DequeueRecordImpl(final UUID queueId, final long messageNumber)
{
_record = new AbstractBDBMessageStore.BDBEnqueueRecord(queueId, messageNumber);
}
diff --git a/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/QueueEntryBinding.java b/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/QueueEntryBinding.java
index 1154ff2..32f1bb9 100644
--- a/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/QueueEntryBinding.java
+++ b/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/QueueEntryBinding.java
@@ -22,49 +22,39 @@
import java.util.UUID;
-import com.sleepycat.bind.EntryBinding;
-import com.sleepycat.bind.tuple.TupleBinding;
-import com.sleepycat.bind.tuple.TupleInput;
-import com.sleepycat.bind.tuple.TupleOutput;
import com.sleepycat.je.DatabaseEntry;
import org.apache.qpid.server.store.berkeleydb.entry.QueueEntryKey;
+import org.apache.qpid.server.util.CachingUUIDFactory;
-public class QueueEntryBinding implements EntryBinding<QueueEntryKey>
+public class QueueEntryBinding
{
-
- private static final QueueEntryBinding INSTANCE = new QueueEntryBinding();
-
- public static QueueEntryBinding getInstance()
+ private QueueEntryBinding()
{
- return INSTANCE;
}
- /** private constructor forces getInstance instead */
- private QueueEntryBinding() { }
-
- public QueueEntryKey entryToObject(DatabaseEntry entry)
+ public static QueueEntryKey entryToObject(final CachingUUIDFactory uuidFactory, DatabaseEntry entry)
{
byte[] data = entry.getData();
int offset = entry.getOffset();
- UUID queueId = new UUID(readUnsignedLong(data,offset)^ 0x8000000000000000L, readUnsignedLong(data,offset+8)^ 0x8000000000000000L);
+ UUID queueId = uuidFactory.createUuidFromBits(readUnsignedLong(data, offset) ^ 0x8000000000000000L, readUnsignedLong(data, offset + 8) ^ 0x8000000000000000L);
long messageId = readUnsignedLong(data,offset+16)^ 0x8000000000000000L;
return new QueueEntryKey(queueId, messageId);
}
- public void objectToEntry(QueueEntryKey mk, DatabaseEntry entry)
+ public static void objectToEntry(QueueEntryKey entryKey, DatabaseEntry entry)
{
byte[] output = new byte[24];
- UUID uuid = mk.getQueueId();
+ UUID uuid = entryKey.getQueueId();
writeUnsignedLong(uuid.getMostSignificantBits() ^ 0x8000000000000000L, output, 0);
writeUnsignedLong(uuid.getLeastSignificantBits() ^ 0x8000000000000000L, output, 8);
- writeUnsignedLong(mk.getMessageId() ^ 0x8000000000000000L, output, 16);
+ writeUnsignedLong(entryKey.getMessageId() ^ 0x8000000000000000L, output, 16);
entry.setData(output);
}
- private void writeUnsignedLong(long val, byte[] data, int offset)
+ private static void writeUnsignedLong(long val, byte[] data, int offset)
{
data[offset++] = (byte) (val >>> 56);
data[offset++] = (byte) (val >>> 48);
@@ -76,19 +66,15 @@
data[offset] = (byte) val;
}
- private long readUnsignedLong(final byte[] data, int offset)
+ private static long readUnsignedLong(final byte[] data, int offset)
{
- return (((long)data[offset++] & 0xffl) << 56)
- | (((long)data[offset++] & 0xffl) << 48)
- | (((long)data[offset++] & 0xffl) << 40)
- | (((long)data[offset++] & 0xffl) << 32)
- | (((long)data[offset++] & 0xffl) << 24)
- | (((long)data[offset++] & 0xffl) << 16)
- | (((long)data[offset++] & 0xffl) << 8)
- | ((long)data[offset] & 0xffl) ;
+ return (((long)data[offset++] & 0xffL) << 56)
+ | (((long)data[offset++] & 0xffL) << 48)
+ | (((long)data[offset++] & 0xffL) << 40)
+ | (((long)data[offset++] & 0xffL) << 32)
+ | (((long)data[offset++] & 0xffL) << 24)
+ | (((long)data[offset++] & 0xffL) << 16)
+ | (((long)data[offset++] & 0xffL) << 8)
+ | ((long)data[offset] & 0xffL) ;
}
-
-
-
-
}
diff --git a/broker-core/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java b/broker-core/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java
index e7e1b0f..e38be2b 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java
@@ -55,6 +55,7 @@
import org.apache.qpid.server.store.handler.DistributedTransactionHandler;
import org.apache.qpid.server.store.handler.MessageHandler;
import org.apache.qpid.server.store.handler.MessageInstanceHandler;
+import org.apache.qpid.server.util.CachingUUIDFactory;
public abstract class AbstractJDBCMessageStore implements MessageStore
{
@@ -1820,6 +1821,7 @@
Connection conn = null;
try
{
+ CachingUUIDFactory uuidFactory = new CachingUUIDFactory();
conn = newAutoCommitConnection();
PreparedStatement stmt = conn.prepareStatement(SELECT_FROM_QUEUE_ENTRY_FOR_QUEUE);
try
@@ -1832,7 +1834,8 @@
{
String id = rs.getString(1);
long messageId = rs.getLong(2);
- if (!handler.handle(new JDBCEnqueueRecord(UUID.fromString(id), messageId)))
+ UUID uuid = uuidFactory.createUuidFromString(id);
+ if (!handler.handle(new JDBCEnqueueRecord(uuid, messageId)))
{
break;
}
@@ -1867,6 +1870,7 @@
Connection conn = null;
try
{
+ CachingUUIDFactory uuidFactory = new CachingUUIDFactory();
conn = newAutoCommitConnection();
Statement stmt = conn.createStatement();
try
@@ -1878,7 +1882,8 @@
{
String id = rs.getString(1);
long messageId = rs.getLong(2);
- if (!handler.handle(new JDBCEnqueueRecord(UUID.fromString(id), messageId)))
+ UUID queueId = uuidFactory.createUuidFromString(id);
+ if (!handler.handle(new JDBCEnqueueRecord(queueId, messageId)))
{
break;
}
@@ -1943,6 +1948,7 @@
for (Xid xid : xids)
{
+ CachingUUIDFactory uuidFactory = new CachingUUIDFactory();
List<RecordImpl> enqueues = new ArrayList<>();
List<RecordImpl> dequeues = new ArrayList<>();
@@ -1961,7 +1967,7 @@
{
String actionType = rs.getString(1);
- UUID queueId = UUID.fromString(rs.getString(2));
+ UUID queueId = uuidFactory.createUuidFromString(rs.getString(2));
long messageId = rs.getLong(3);
RecordImpl record = new RecordImpl(queueId, messageId);
diff --git a/broker-core/src/main/java/org/apache/qpid/server/util/CachingUUIDFactory.java b/broker-core/src/main/java/org/apache/qpid/server/util/CachingUUIDFactory.java
new file mode 100644
index 0000000..c854ae9
--- /dev/null
+++ b/broker-core/src/main/java/org/apache/qpid/server/util/CachingUUIDFactory.java
@@ -0,0 +1,56 @@
+/*
+ * 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.qpid.server.util;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+public class CachingUUIDFactory
+{
+ private final Map<UUID, UUID> _uuids = new HashMap<>();
+
+ public UUID createUuidFromString(final String name)
+ {
+ UUID candidate = UUID.fromString(name);
+ return cacheIfNecessary(candidate);
+ }
+
+ public UUID createUuidFromBits(final long mostSigBits, final long leastSigBits)
+ {
+ UUID candidate = new UUID(mostSigBits, leastSigBits);
+ return cacheIfNecessary(candidate);
+ }
+
+ private UUID cacheIfNecessary(final UUID candidate)
+ {
+ UUID existing = _uuids.get(candidate);
+ if (existing != null)
+ {
+ return existing;
+ }
+ else
+ {
+ _uuids.put(candidate, candidate);
+ return candidate;
+ }
+ }
+}
diff --git a/broker-core/src/test/java/org/apache/qpid/server/util/CachingUUIDFactoryTest.java b/broker-core/src/test/java/org/apache/qpid/server/util/CachingUUIDFactoryTest.java
new file mode 100644
index 0000000..f27df9a
--- /dev/null
+++ b/broker-core/src/test/java/org/apache/qpid/server/util/CachingUUIDFactoryTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.qpid.server.util;
+
+import java.util.UUID;
+
+import org.apache.qpid.test.utils.QpidTestCase;
+
+public class CachingUUIDFactoryTest extends QpidTestCase
+{
+ private final CachingUUIDFactory _factory = new CachingUUIDFactory();
+
+ public void testUuidFromBits()
+ {
+ UUID first = _factory.createUuidFromBits(0L,0L);
+ UUID second = _factory.createUuidFromBits(0L,0L);
+ assertSame("UUIDFactory should return the same object", first, second);
+ }
+
+ public void testUuidFromString()
+ {
+ String uuidStr = UUID.randomUUID().toString();
+ UUID first = _factory.createUuidFromString(new String(uuidStr));
+ UUID second = _factory.createUuidFromString(new String(uuidStr));
+ UUID third = _factory.createUuidFromBits(second.getMostSignificantBits(), second.getLeastSignificantBits());
+ assertSame("UUIDFactory should return the same object", first, second);
+ assertSame("UUIDFactory should return the same object", first, third);
+ }
+}
\ No newline at end of file