fixed issue 20515: OBCriteria creates contention at JVM
authorAsier Lostalé <asier.lostale@openbravo.com>
Wed, 15 Feb 2017 14:13:23 +0100
changeset 31437 f11b270a42af
parent 31436 8359543723db
child 31438 9f835f99b47d
fixed issue 20515: OBCriteria creates contention at JVM

When using OBCriteria for same entity by several concurrent threads, there was
contention at JVM due to locks in Class.forName(entityName).

This Class.forName(entityName) always results in ClassNotFoundException because
entityName is not an actual class name. Hibernate criteria can accept either an
entity name or a class name. It has been changed to set class name instead of
entity so that it finds the class in the classloader cache earlier removing contention.

Note when criteria is used for scrolling, Hibernate's flow is different and it
expects it to be an entity name, though in this case there's no contention because
class is resolved by internal caches. This is also covered in the fix as exceptional
case.
src/org/openbravo/dal/service/OBCriteria.java
src/org/openbravo/dal/service/OBDal.java
--- a/src/org/openbravo/dal/service/OBCriteria.java	Tue Feb 21 13:48:41 2017 +0100
+++ b/src/org/openbravo/dal/service/OBCriteria.java	Wed Feb 15 14:13:23 2017 +0100
@@ -11,7 +11,7 @@
  * under the License. 
  * The Original Code is Openbravo ERP. 
  * The Initial Developer of the Original Code is Openbravo SLU 
- * All portions are Copyright (C) 2008-2016 Openbravo SLU 
+ * All portions are Copyright (C) 2008-2017 Openbravo SLU 
  * All Rights Reserved. 
  * Contributor(s):  ______________________________________.
  ************************************************************************
@@ -73,6 +73,8 @@
 
   // package visible
 
+  private boolean scrolling = false;
+
   public OBCriteria(String entityOrClassName) {
     super(entityOrClassName, (SessionImplementor) SessionHandler.getInstance().getSession());
   }
@@ -117,16 +119,37 @@
    * See the scroll method on the Hibernate Criteria class.
    */
   public ScrollableResults scroll() throws HibernateException {
-    initialize();
-    return super.scroll();
+    scrolling = true;
+    try {
+      initialize();
+      return super.scroll();
+    } finally {
+      scrolling = false;
+    }
   }
 
   /**
    * See the scroll method on the Hibernate Criteria class.
    */
   public ScrollableResults scroll(ScrollMode scrollMode) throws HibernateException {
-    initialize();
-    return super.scroll(scrollMode);
+    scrolling = true;
+    try {
+      initialize();
+      return super.scroll(scrollMode);
+    } finally {
+      scrolling = false;
+    }
+  }
+
+  @Override
+  public String getEntityOrClassName() {
+    if (scrolling && entity != null) {
+      // When criteria is used for scrolling, Hibernate expects this method to return the entity
+      // name. For listing instead it can accept either entity or implementing class name, if entity
+      // name is returned, it performs worse. So return always class name but when scrolling.
+      return entity.getName();
+    }
+    return super.getEntityOrClassName();
   }
 
   /**
--- a/src/org/openbravo/dal/service/OBDal.java	Tue Feb 21 13:48:41 2017 +0100
+++ b/src/org/openbravo/dal/service/OBDal.java	Wed Feb 15 14:13:23 2017 +0100
@@ -442,7 +442,7 @@
   public <T extends BaseOBObject> OBCriteria<T> createCriteria(Class<T> clz) {
     checkReadAccess(clz);
     final Entity entity = ModelProvider.getInstance().getEntity(clz);
-    final OBCriteria<T> obCriteria = new OBCriteria<T>(entity.getName());
+    final OBCriteria<T> obCriteria = new OBCriteria<T>(clz.getName());
     obCriteria.setEntity(entity);
     return obCriteria;
   }
@@ -459,7 +459,7 @@
   public <T extends BaseOBObject> OBCriteria<T> createCriteria(Class<T> clz, String alias) {
     checkReadAccess(clz);
     final Entity entity = ModelProvider.getInstance().getEntity(clz);
-    final OBCriteria<T> obCriteria = new OBCriteria<T>(entity.getName(), alias);
+    final OBCriteria<T> obCriteria = new OBCriteria<T>(clz.getName(), alias);
     obCriteria.setEntity(entity);
     return obCriteria;
   }
@@ -473,8 +473,9 @@
    */
   public <T extends BaseOBObject> OBCriteria<T> createCriteria(String entityName) {
     checkReadAccess(entityName);
-    final OBCriteria<T> obCriteria = new OBCriteria<T>(entityName);
-    obCriteria.setEntity(ModelProvider.getInstance().getEntity(entityName));
+    Entity entity = ModelProvider.getInstance().getEntity(entityName);
+    final OBCriteria<T> obCriteria = new OBCriteria<T>(entity.getMappingClass().getName());
+    obCriteria.setEntity(entity);
     return obCriteria;
   }