svn commit: r575074 - in /ofbiz/trunk/framework/service: dtd/ entitydef/ src/org/ofbiz/service/ src/org/ofbiz/service/semaphore/

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

svn commit: r575074 - in /ofbiz/trunk/framework/service: dtd/ entitydef/ src/org/ofbiz/service/ src/org/ofbiz/service/semaphore/

jaz-3
Author: jaz
Date: Wed Sep 12 13:58:44 2007
New Revision: 575074

URL: http://svn.apache.org/viewvc?rev=575074&view=rev
Log:
implemented ServiceSemaphore; built in locking to prevent services (when defined) from running at the same time; uses database record to work with a cluser of application servers. Three modes are available:

None - Does no locking
Wait - Waits for the previous service to complete (currently up to 5 minutes)
Fail - Fails when the same service is already running

use the semaphore="none|wait|fail" in the service definition (none is the default when the attribute is not defined)

Added:
    ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/
    ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/SemaphoreFailException.java   (with props)
    ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/SemaphoreWaitException.java   (with props)
    ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/ServiceSemaphore.java   (with props)
Modified:
    ofbiz/trunk/framework/service/dtd/services.xsd
    ofbiz/trunk/framework/service/entitydef/entitygroup.xml
    ofbiz/trunk/framework/service/entitydef/entitymodel.xml
    ofbiz/trunk/framework/service/src/org/ofbiz/service/ModelService.java
    ofbiz/trunk/framework/service/src/org/ofbiz/service/ModelServiceReader.java
    ofbiz/trunk/framework/service/src/org/ofbiz/service/ServiceDispatcher.java

Modified: ofbiz/trunk/framework/service/dtd/services.xsd
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/dtd/services.xsd?rev=575074&r1=575073&r2=575074&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/dtd/services.xsd (original)
+++ ofbiz/trunk/framework/service/dtd/services.xsd Wed Sep 12 13:58:44 2007
@@ -111,6 +111,15 @@
                 </xs:restriction>
             </xs:simpleType>
         </xs:attribute>
+        <xs:attribute name="semaphore" default="none">
+            <xs:simpleType>
+                <xs:restriction base="xs:token">
+                    <xs:enumeration value="none"/>
+                    <xs:enumeration value="fail"/>
+                    <xs:enumeration value="wait"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
     </xs:attributeGroup>
     <xs:element name="notification">
         <xs:complexType>

Modified: ofbiz/trunk/framework/service/entitydef/entitygroup.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/entitydef/entitygroup.xml?rev=575074&r1=575073&r2=575074&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/entitydef/entitygroup.xml (original)
+++ ofbiz/trunk/framework/service/entitydef/entitygroup.xml Wed Sep 12 13:58:44 2007
@@ -29,5 +29,10 @@
     <entity-group group="org.ofbiz" entity="RecurrenceInfo" />
     <entity-group group="org.ofbiz" entity="RecurrenceRule" />
     <entity-group group="org.ofbiz" entity="RuntimeData" />
+    
+  <!-- ========================================================= -->
+  <!-- org.ofbiz.service.semaphore -->
+  <!-- ========================================================= -->
+    <entity-group group="org.ofbiz" entity="ServiceSemaphore" />
 </entitygroup>
 

Modified: ofbiz/trunk/framework/service/entitydef/entitymodel.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/entitydef/entitymodel.xml?rev=575074&r1=575073&r2=575074&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/entitydef/entitymodel.xml (original)
+++ ofbiz/trunk/framework/service/entitydef/entitymodel.xml Wed Sep 12 13:58:44 2007
@@ -33,6 +33,7 @@
   <!-- ======================== Data Model ===================== -->
   <!-- The modules in this file are as follows:                  -->
   <!--  - org.ofbiz.service.schedule -->
+  <!--  - org.ofbiz.service.semaphore -->
   <!-- ========================================================= -->
 
 
@@ -41,7 +42,7 @@
   <!-- ========================================================= -->
 
     <entity entity-name="JobSandbox" package-name="org.ofbiz.service.schedule" title="Job Scheduler Sandbox Entity"
-        sequence-bank-size="100">
+            sequence-bank-size="100">
         <field name="jobId" type="id-ne"></field>
         <field name="jobName" type="name"></field>
         <field name="runTime" type="date-time"></field>
@@ -121,9 +122,21 @@
       <prim-key field="recurrenceRuleId"/>
     </entity>
     <entity entity-name="RuntimeData" package-name="org.ofbiz.service.schedule" title="Runtime Data Entity"
-        sequence-bank-size="100">
+            sequence-bank-size="100">
         <field name="runtimeDataId" type="id-ne"></field>
         <field name="runtimeInfo" type="very-long"></field>
         <prim-key field="runtimeDataId"/>
     </entity>
+
+  <!-- ========================================================= -->
+  <!-- org.ofbiz.service.semaphore -->
+  <!-- ========================================================= -->
+    <entity entity-name="ServiceSemaphore" package-name="org.ofbiz.service.semaphore" title="Semaphore Lock Entity"
+            sequence-bank-size="100">
+        <field name="serviceName" type="name"></field>
+        <field name="lockThread" type="name"></field>
+        <field name="lockTime" type="date-time"></field>        
+        <prim-key field="serviceName"/>
+    </entity>
+
 </entitymodel>

Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/ModelService.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/ModelService.java?rev=575074&r1=575073&r2=575074&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/src/org/ofbiz/service/ModelService.java (original)
+++ ofbiz/trunk/framework/service/src/org/ofbiz/service/ModelService.java Wed Sep 12 13:58:44 2007
@@ -133,6 +133,9 @@
     
     /** Permission service resource-description */
     public String permissionResourceDesc;
+
+    /** Semaphore setting (wait, fail, none) */
+    public String semaphore;
     
     /** Set of services this service implements */
     public Set implServices = new ListOrderedSet();

Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/ModelServiceReader.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/ModelServiceReader.java?rev=575074&r1=575073&r2=575074&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/src/org/ofbiz/service/ModelServiceReader.java (original)
+++ ofbiz/trunk/framework/service/src/org/ofbiz/service/ModelServiceReader.java Wed Sep 12 13:58:44 2007
@@ -258,7 +258,8 @@
         service.name = UtilXml.checkEmpty(serviceElement.getAttribute("name"));
         service.engineName = UtilXml.checkEmpty(serviceElement.getAttribute("engine"));
         service.location = UtilXml.checkEmpty(serviceElement.getAttribute("location"));
-        service.invoke = UtilXml.checkEmpty(serviceElement.getAttribute("invoke"));  
+        service.invoke = UtilXml.checkEmpty(serviceElement.getAttribute("invoke"));
+        service.semaphore = UtilXml.checkEmpty(serviceElement.getAttribute("semaphore"));
         service.defaultEntityName = UtilXml.checkEmpty(serviceElement.getAttribute("default-entity-name"));
         service.fromLoader = isFromURL ? readerURL.toExternalForm() : handler.getLoaderName();        
 

Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/ServiceDispatcher.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/ServiceDispatcher.java?rev=575074&r1=575073&r2=575074&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/src/org/ofbiz/service/ServiceDispatcher.java (original)
+++ ofbiz/trunk/framework/service/src/org/ofbiz/service/ServiceDispatcher.java Wed Sep 12 13:58:44 2007
@@ -18,24 +18,11 @@
  *******************************************************************************/
 package org.ofbiz.service;
 
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-
-import javax.transaction.Transaction;
-
 import javolution.util.FastList;
 import javolution.util.FastMap;
-
 import org.apache.commons.collections.map.LRUMap;
 import org.ofbiz.base.config.GenericConfigException;
-import org.ofbiz.base.util.Debug;
-import org.ofbiz.base.util.GeneralRuntimeException;
-import org.ofbiz.base.util.UtilMisc;
-import org.ofbiz.base.util.UtilTimer;
-import org.ofbiz.base.util.UtilValidate;
-import org.ofbiz.base.util.UtilXml;
+import org.ofbiz.base.util.*;
 import org.ofbiz.entity.GenericDelegator;
 import org.ofbiz.entity.GenericEntityException;
 import org.ofbiz.entity.GenericValue;
@@ -53,8 +40,15 @@
 import org.ofbiz.service.jms.JmsListenerFactory;
 import org.ofbiz.service.job.JobManager;
 import org.ofbiz.service.job.JobManagerException;
+import org.ofbiz.service.semaphore.ServiceSemaphore;
 import org.w3c.dom.Element;
 
+import javax.transaction.Transaction;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
 /**
  * Global Service Dispatcher
  */
@@ -237,6 +231,13 @@
      * @throws GenericServiceException
      */
     public Map runSync(String localName, ModelService modelService, Map context, boolean validateOut) throws ServiceAuthException, ServiceValidationException, GenericServiceException {
+        // check for semaphore and aquire a lock
+        ServiceSemaphore lock = null;
+        if ("wait".equals(modelService.semaphore) || "fail".equals(modelService.semaphore)) {
+            lock = new ServiceSemaphore(delegator, modelService);
+            lock.acquire();
+        }
+
         long serviceStartTime = System.currentTimeMillis();
         boolean debugging = checkDebug(modelService, 1, true);
         if (Debug.verboseOn()) {
@@ -540,6 +541,11 @@
             Debug.logTiming("Sync service [" + localName + "/" + modelService.name + "] finished in [" + timeToRun + "] milliseconds", module);
         } else if (timeToRun > 200) {
             Debug.logInfo("Sync service [" + localName + "/" + modelService.name + "] finished in [" + timeToRun + "] milliseconds", module);
+        }
+
+        // release the semaphore lock
+        if (lock != null) {
+            lock.release();
         }
         
         return result;

Added: ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/SemaphoreFailException.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/SemaphoreFailException.java?rev=575074&view=auto
==============================================================================
--- ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/SemaphoreFailException.java (added)
+++ ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/SemaphoreFailException.java Wed Sep 12 13:58:44 2007
@@ -0,0 +1,25 @@
+package org.ofbiz.service.semaphore;
+
+import org.ofbiz.service.GenericServiceException;
+
+/**
+ * SemaphoreFailException
+ */
+public class SemaphoreFailException extends GenericServiceException {
+
+    public SemaphoreFailException() {
+        super();
+    }
+
+    public SemaphoreFailException(String str) {
+        super(str);
+    }
+
+    public SemaphoreFailException(String str, Throwable nested) {
+        super(str, nested);
+    }
+
+    public SemaphoreFailException(Throwable nested) {
+        super(nested);
+    }
+}

Propchange: ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/SemaphoreFailException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/SemaphoreFailException.java
------------------------------------------------------------------------------
    svn:keywords = "Date Rev Author URL Id"

Propchange: ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/SemaphoreFailException.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/SemaphoreWaitException.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/SemaphoreWaitException.java?rev=575074&view=auto
==============================================================================
--- ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/SemaphoreWaitException.java (added)
+++ ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/SemaphoreWaitException.java Wed Sep 12 13:58:44 2007
@@ -0,0 +1,25 @@
+package org.ofbiz.service.semaphore;
+
+import org.ofbiz.service.GenericServiceException;
+
+/**
+ * SemaphoreWaitException
+ */
+public class SemaphoreWaitException extends GenericServiceException {
+
+    public SemaphoreWaitException() {
+        super();
+    }
+
+    public SemaphoreWaitException(String str) {
+        super(str);
+    }
+
+    public SemaphoreWaitException(String str, Throwable nested) {
+        super(str, nested);
+    }
+
+    public SemaphoreWaitException(Throwable nested) {
+        super(nested);
+    }
+}

Propchange: ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/SemaphoreWaitException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/SemaphoreWaitException.java
------------------------------------------------------------------------------
    svn:keywords = "Date Rev Author URL Id"

Propchange: ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/SemaphoreWaitException.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/ServiceSemaphore.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/ServiceSemaphore.java?rev=575074&view=auto
==============================================================================
--- ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/ServiceSemaphore.java (added)
+++ ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/ServiceSemaphore.java Wed Sep 12 13:58:44 2007
@@ -0,0 +1,155 @@
+package org.ofbiz.service.semaphore;
+
+import org.ofbiz.base.util.Debug;
+import org.ofbiz.base.util.UtilDateTime;
+import org.ofbiz.base.util.UtilMisc;
+import org.ofbiz.entity.GenericDelegator;
+import org.ofbiz.entity.GenericEntityException;
+import org.ofbiz.entity.GenericValue;
+import org.ofbiz.entity.transaction.GenericTransactionException;
+import org.ofbiz.entity.transaction.TransactionUtil;
+import org.ofbiz.service.ModelService;
+
+import javax.transaction.Transaction;
+import java.sql.Timestamp;
+import java.util.Map;
+
+/**
+ * ServiceSemaphore
+ */
+public class ServiceSemaphore {
+
+    public static final String module = ServiceSemaphore.class.getName();
+    public static final int SEMAPHORE_MODE_FAIL = 0;
+    public static final int SEMAPHORE_MODE_WAIT = 1;
+    public static final int SEMAPHORE_MODE_NONE = 2;
+    public static final long MAX_WAIT = 600;
+    public static final long SLEEP = 500;
+
+    protected GenericDelegator delegator;
+    protected GenericValue lock;
+    protected ModelService model;
+
+    protected int wait = 0;
+    protected int mode = 2;
+
+    public ServiceSemaphore(GenericDelegator delegator, ModelService model) {
+        this.mode = "wait".equals(model.semaphore) ? 1 : ("fail".equals(model.semaphore) ? 0 : 2);
+        this.delegator = delegator;
+        this.model = model;
+        this.lock = null;
+    }
+
+    public void acquire() throws SemaphoreWaitException, SemaphoreFailException {
+        if (mode == SEMAPHORE_MODE_NONE) return;
+
+        String threadName = Thread.currentThread().getName();
+        Timestamp lockTime = UtilDateTime.nowTimestamp();
+        GenericValue semaphore;
+
+        try {
+            semaphore = delegator.findByPrimaryKey("ServiceSemaphore", UtilMisc.toMap("serviceName", model.name));
+        } catch (GenericEntityException e) {
+            throw new SemaphoreFailException(e);
+        }
+
+        if (semaphore == null) {
+            Map fields = UtilMisc.toMap("serviceName", model.name, "lockThread", threadName, "lockTime", lockTime);
+            semaphore = delegator.makeValue("ServiceSemaphore", fields);
+
+            // use the special method below so we can reuse the unqiue tx functions
+            dbWrite(semaphore, false);
+        } else {            
+            waitOrFail(mode);
+        }
+    }
+
+    public void release() throws SemaphoreFailException {
+        if (mode == SEMAPHORE_MODE_NONE) return;
+
+        // remove the lock file
+        dbWrite(lock, true);
+    }
+
+    private void waitOrFail(int mode) throws SemaphoreWaitException, SemaphoreFailException {
+        switch (mode) {
+            case SEMAPHORE_MODE_FAIL:
+                // fail
+                throw new SemaphoreFailException("Service [" + model.name + "] is locked");
+            case SEMAPHORE_MODE_WAIT:
+                if (wait < MAX_WAIT) {
+                    ++wait;
+                    try {
+                        Thread.sleep(SLEEP);              
+                    } catch (InterruptedException e) {
+                        Debug.logInfo(e, "Sleep interrupted: ServiceSemaphone.waitOrFail()", module);
+                    }
+
+                    // try again
+                    acquire();
+                    break;
+                } else {
+                    throw new SemaphoreWaitException("Service [" + model.name + "] wait timeout exceeded");
+                }
+            case SEMAPHORE_MODE_NONE:
+                Debug.logWarning("Semaphore mode [none] attempted to aquire a lock; but should not have!", module);
+                break;
+            default:
+                throw new SemaphoreFailException();
+        }
+    }
+
+    private synchronized void dbWrite(GenericValue value, boolean delete) throws SemaphoreFailException {
+        Transaction parent = null;
+        boolean beganTx = false;
+        boolean isError = false;
+
+        try {
+            // prepare the suspended transaction
+            parent = TransactionUtil.suspend();            
+            beganTx = TransactionUtil.begin();
+            if (!beganTx) {
+                throw new SemaphoreFailException("Cannot obtain unique transaction for semaphore logging");
+            }
+
+            // store the value
+            try {
+                if (delete) {
+                    value.refresh();
+                    value.remove();
+                    lock = null;
+                } else {
+                    lock = value.create();
+                }
+            } catch (GenericEntityException e) {
+                Debug.logError(e, module);
+                isError = true;
+                throw new SemaphoreFailException("Cannot obtain unique transaction for semaphore logging");
+            }
+        } catch (GenericTransactionException e) {
+            Debug.logError(e, module);
+        } finally {
+            if (isError) {
+                try {
+                    TransactionUtil.rollback(beganTx, "ServiceSemaphore: dbWrite()", new Exception());
+                } catch (GenericTransactionException e) {
+                    Debug.logError(e, module);
+                }
+            }
+            if (!isError && beganTx) {
+                try {
+                    TransactionUtil.commit(beganTx);
+                } catch (GenericTransactionException e) {
+                    Debug.logError(e, module);
+                }
+            }
+            if (parent != null) {
+                try {
+                    TransactionUtil.resume(parent);
+                } catch (GenericTransactionException e) {
+                    Debug.logError(e, module);
+                }
+            }
+        }
+    }
+}

Propchange: ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/ServiceSemaphore.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/ServiceSemaphore.java
------------------------------------------------------------------------------
    svn:keywords = "Date Rev Author URL Id"

Propchange: ofbiz/trunk/framework/service/src/org/ofbiz/service/semaphore/ServiceSemaphore.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain