Author: curtisr7
Date: Thu Aug 12 20:15:41 2010
New Revision: 984943
URL: http://svn.apache.org/viewvc?rev=984943&view=rev
Log:
OPENJPA-1765: Add locking to TableJDBCSeq to ensure thread safety.
Added:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/generationtype/TestTableGeneratorMultithreaded.java
(with props)
Modified:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/TableJDBCSeq.java
Modified:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/TableJDBCSeq.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/TableJDBCSeq.java?rev=984943&r1=984942&r2=984943&view=diff
==============================================================================
---
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/TableJDBCSeq.java
(original)
+++
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/TableJDBCSeq.java
Thu Aug 12 20:15:41 2010
@@ -24,15 +24,15 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
-import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
import javax.transaction.NotSupportedException;
import org.apache.commons.lang.StringUtils;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.jdbc.conf.JDBCConfigurationImpl;
-import org.apache.openjpa.jdbc.identifier.Normalizer;
import org.apache.openjpa.jdbc.identifier.DBIdentifier;
+import org.apache.openjpa.jdbc.identifier.Normalizer;
import org.apache.openjpa.jdbc.identifier.QualifiedDBIdentifier;
import org.apache.openjpa.jdbc.identifier.DBIdentifier.DBIdentifierType;
import org.apache.openjpa.jdbc.meta.ClassMapping;
@@ -71,9 +71,7 @@ import org.apache.openjpa.util.UserExcep
*
* @author Abe White
*/
-public class TableJDBCSeq
- extends AbstractJDBCSeq
- implements Configurable {
+public class TableJDBCSeq extends AbstractJDBCSeq implements Configurable {
public static final String ACTION_DROP = "drop";
public static final String ACTION_ADD = "add";
@@ -88,8 +86,7 @@ public class TableJDBCSeq
private transient Log _log = null;
private int _alloc = 50;
private int _intValue = 1;
- private final HashMap<ClassMapping, Status> _stat =
- new HashMap<ClassMapping, Status>();
+ private final ConcurrentHashMap<ClassMapping, Status> _stat = new
ConcurrentHashMap<ClassMapping, Status>();
private DBIdentifier _table = DBIdentifier.newTable(DEFAULT_TABLE);
private DBIdentifier _seqColumnName =
DBIdentifier.newColumn("SEQUENCE_VALUE");
@@ -286,8 +283,7 @@ public class TableJDBCSeq
}
}
- protected Object nextInternal(JDBCStore store, ClassMapping mapping)
- throws Exception {
+ protected Object nextInternal(JDBCStore store, ClassMapping mapping)
throws Exception {
// if needed, grab the next handful of ids
Status stat = getStatus(mapping);
if (stat == null)
@@ -301,8 +297,8 @@ public class TableJDBCSeq
stat.seq = Math.max(stat.seq, 1);
if (stat.seq < stat.max)
return stat.seq++;
+ allocateSequence(store, mapping, stat, _alloc, true);
}
- allocateSequence(store, mapping, stat, _alloc, true);
}
}
@@ -350,10 +346,15 @@ public class TableJDBCSeq
* if cannot handle the given class. The mapping may be null.
*/
protected Status getStatus(ClassMapping mapping) {
- Status status = (Status)_stat.get(mapping);
+ Status status = (Status) _stat.get(mapping);
if (status == null){
status = new Status();
- _stat.put(mapping, status);
+ Status tStatus = _stat.putIfAbsent(mapping, status);
+ // This can happen if another thread calls .put(..) sometime after
our call to get. Return
+ // the value from the putIfAbsent call as that is truly in the map.
+ if (tStatus != null) {
+ return tStatus;
+ }
}
return status;
}
Added:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/generationtype/TestTableGeneratorMultithreaded.java
URL:
http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/generationtype/TestTableGeneratorMultithreaded.java?rev=984943&view=auto
==============================================================================
---
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/generationtype/TestTableGeneratorMultithreaded.java
(added)
+++
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/generationtype/TestTableGeneratorMultithreaded.java
Thu Aug 12 20:15:41 2010
@@ -0,0 +1,120 @@
+/*
+ * 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.persistence.generationtype;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.openjpa.jdbc.kernel.TableJDBCSeq;
+import org.apache.openjpa.kernel.StoreContext;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.MetaDataRepository;
+import org.apache.openjpa.meta.SequenceMetaData;
+import org.apache.openjpa.persistence.EntityManagerImpl;
+import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
+import org.apache.openjpa.persistence.test.SingleEMFTestCase;
+
+public class TestTableGeneratorMultithreaded extends SingleEMFTestCase {
+ public final int ALLOC_SIZE = 100000;
+ public final int INITIAL = 1;
+ public final int LOOPS = 100000;
+ public final int THREADS = 5;
+
+ public void setUp() {
+ setUp(Dog.class, CLEAR_TABLES
+ // , "openjpa.Log", "SQL=trace",
"openjpa.ConnectionFactoryProperties","PrintParameters=true"
+ );
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ MetaDataRepository repo =
emf.getConfiguration().getMetaDataRepositoryInstance();
+ // Initialize MetaData
+ repo.getMetaData(Dog.class, loader, true);
+ repo.getSequenceMetaData("Dog_Gen", loader, true);
+
+ }
+
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testAllIdsAreUsed() throws Exception {
+ List<WorkerThread> threads = new ArrayList<WorkerThread>();
+ for (int i = 0; i < THREADS; i++) {
+ threads.add(new WorkerThread(emf));
+ }
+ for (Thread t : threads) {
+ t.start();
+ }
+ long max = 0;
+ for (WorkerThread w : threads) {
+ w.join();
+ max = Math.max(max, (Long) w.getLast());
+ }
+ assertEquals((LOOPS * (THREADS)), max);
+ }
+
+ class WorkerThread extends Thread {
+ Object _first = null, _last = null;
+ int _count = 0;
+ StoreContext _ctx;
+ TableJDBCSeq _seq;
+ ClassMetaData _cmd;
+ EntityManagerImpl _em;
+ MetaDataRepository _repo;
+
+ public WorkerThread(OpenJPAEntityManagerFactorySPI emf) {
+ _repo = emf.getConfiguration().getMetaDataRepositoryInstance();
+ }
+
+ public void run() {
+ ClassLoader contextLoader =
Thread.currentThread().getContextClassLoader();
+ SequenceMetaData meta = _repo.getSequenceMetaData("Dog_Gen",
contextLoader, true);
+ meta.setInitialValue(1);
+ meta.setIncrement(10000);
+ _em = (EntityManagerImpl) emf.createEntityManager();
+ _ctx = (StoreContext) _em.getBroker();
+ _cmd = _repo.getMetaData(Dog.class, contextLoader, true);
+
+ _seq = (TableJDBCSeq) meta.getInstance(contextLoader);
+ // Change defaults so this test doesn't take so long to run.
+ _seq.setAllocate(ALLOC_SIZE);
+ _seq.setInitialValue(1);
+
+ Object obj = _seq.next(_ctx, _cmd);
+ _first = obj;
+ // start at 1 because we already got our first result.
+ for (int i = 1; i < LOOPS; i++) {
+ obj = _seq.next(_ctx, _cmd);
+ }
+ _last = obj;
+ _em.close();
+ }
+
+ public Object getLast() {
+ return _last;
+ }
+
+ public Object getFirst() {
+ return _first;
+ }
+
+ public int getCount() {
+ return _count;
+ }
+ }// end WorkerThread
+}
Propchange:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/generationtype/TestTableGeneratorMultithreaded.java
------------------------------------------------------------------------------
svn:eol-style = native