From 7f990d008fc6d136ba8646ddf376c87cb4197a92 Mon Sep 17 00:00:00 2001
From: Sinisa Veseli <sveseli@aps.anl.gov>
Date: Wed, 22 Mar 2017 20:20:32 +0000
Subject: [PATCH] fix issue with suds cache

---
 src/python/dm/__init__.py               |  2 +-
 src/python/dm/aps_bss/api/apsBssApi.py  | 14 +++---
 src/python/dm/aps_bss/impl/bssClient.py | 11 +++--
 src/python/dm/common/api/dmApi.py       |  1 -
 src/python/dm/common/api/dmSudsApi.py   | 61 +++++++++++++++++++++++++
 5 files changed, 75 insertions(+), 14 deletions(-)
 create mode 100755 src/python/dm/common/api/dmSudsApi.py

diff --git a/src/python/dm/__init__.py b/src/python/dm/__init__.py
index 3e733647..f4bbd36e 100644
--- a/src/python/dm/__init__.py
+++ b/src/python/dm/__init__.py
@@ -1 +1 @@
-__version__ = "1.1 (2017.03.13)"
+__version__ = "1.1 (2017.03.22)"
diff --git a/src/python/dm/aps_bss/api/apsBssApi.py b/src/python/dm/aps_bss/api/apsBssApi.py
index 11e4f934..42bc938b 100755
--- a/src/python/dm/aps_bss/api/apsBssApi.py
+++ b/src/python/dm/aps_bss/api/apsBssApi.py
@@ -2,10 +2,10 @@
 
 import os
 from dm.common.exceptions.configurationError import ConfigurationError
-from dm.common.api.dmApi import DmApi
+from dm.common.api.dmSudsApi import DmSudsApi
 from dm.aps_bss.impl.bssClient import BssClient
 
-class ApsBssApi(DmApi):
+class ApsBssApi(DmSudsApi):
     ''' Data Management API for APS Beamline Scheduling System. '''
     def __init__(self, beamlineName=None, username=None, password=None, loginFile=None):
         '''
@@ -31,7 +31,7 @@ class ApsBssApi(DmApi):
 
         >>> api = ApsBssApi(beamlineName='1-ID-B,C,E', username='dm', password='XYZ')
         '''
-        DmApi.__init__(self)
+        DmSudsApi.__init__(self)
         if not username or not password:
             if not loginFile:
                 loginFile = os.environ.get('DM_BSS_LOGIN_FILE')
@@ -55,7 +55,7 @@ class ApsBssApi(DmApi):
         self.beamlineName = beamlineName
         self.bssClient = BssClient(username, password)
 
-    @DmApi.execute2
+    @DmSudsApi.executeSudsCall
     def listRuns(self):
         '''
         List all available runs.
@@ -70,7 +70,7 @@ class ApsBssApi(DmApi):
         '''
         return self.bssClient.listRuns()
 
-    @DmApi.execute2
+    @DmSudsApi.executeSudsCall
     def getCurrentRun(self):
         '''
         Find current run.
@@ -83,7 +83,7 @@ class ApsBssApi(DmApi):
         '''
         return self.bssClient.getCurrentRun()
 
-    @DmApi.execute2
+    @DmSudsApi.executeSudsCall
     def listBeamlineProposals(self, runName=None):
         '''
         List beamline proposals for a given run.
@@ -103,7 +103,7 @@ class ApsBssApi(DmApi):
             runName = self.getCurrentRun()['name']
         return self.bssClient.listBeamlineProposals(self.beamlineName, runName)
 
-    @DmApi.execute2
+    @DmSudsApi.executeSudsCall
     def getBeamlineProposal(self, proposalId, runName=None):
         '''
         Get beamline proposal with a given id.
diff --git a/src/python/dm/aps_bss/impl/bssClient.py b/src/python/dm/aps_bss/impl/bssClient.py
index dc81e6f4..df8f129f 100644
--- a/src/python/dm/aps_bss/impl/bssClient.py
+++ b/src/python/dm/aps_bss/impl/bssClient.py
@@ -6,6 +6,7 @@ import string
 from suds.wsse import Security
 from suds.wsse import UsernameToken
 from suds.client import Client
+from suds.cache import NoCache
 
 from dm.common.exceptions.objectNotFound import ObjectNotFound
 from dm.common.objects.runInfo import RunInfo
@@ -18,7 +19,7 @@ from dm.common.utility.loggingManager import LoggingManager
 class BssClient:
 
     WSDL_URL = 'https://schedule.aps.anl.gov/beamschedds/springws'
-    CACHE_DURATION_IN_SECONDS = 10
+    #CACHE_DURATION_IN_SECONDS = 10
 
     def __init__(self, username, password):
         self.logger = LoggingManager.getInstance().getLogger(self.__class__.__name__)
@@ -29,12 +30,12 @@ class BssClient:
         runScheduleServiceUrl = self.WSDL_URL + '/runScheduleService/runScheduleWebService.wsdl'
 
         try:
-            self.runScheduleServiceClient = Client(runScheduleServiceUrl)
-            self.runScheduleServiceClient.options.cache.setduration(seconds=self.CACHE_DURATION_IN_SECONDS)
+            self.runScheduleServiceClient = Client(runScheduleServiceUrl, cache=NoCache())
+            #self.runScheduleServiceClient.options.cache.setduration(seconds=self.CACHE_DURATION_IN_SECONDS)
             self.setSoapHeader(self.runScheduleServiceClient, username, password)
         
-            self.beamlineScheduleServiceClient = Client(beamlineScheduleServiceUrl)
-            self.beamlineScheduleServiceClient.options.cache.setduration(seconds=self.CACHE_DURATION_IN_SECONDS)
+            self.beamlineScheduleServiceClient = Client(beamlineScheduleServiceUrl, cache=NoCache())
+            #self.beamlineScheduleServiceClient.options.cache.setduration(seconds=self.CACHE_DURATION_IN_SECONDS)
             self.setSoapHeader(self.beamlineScheduleServiceClient, username, password)
         except Exception, ex:
             self.logger.error('Cannot open BSS connection: %s' % str(ex))
diff --git a/src/python/dm/common/api/dmApi.py b/src/python/dm/common/api/dmApi.py
index 9de12e9d..d1c0ed1b 100755
--- a/src/python/dm/common/api/dmApi.py
+++ b/src/python/dm/common/api/dmApi.py
@@ -1,6 +1,5 @@
 #!/usr/bin/env python
 
-import json
 from functools import wraps
 from decorator import decorator
 from dm.common.exceptions.dmException import DmException
diff --git a/src/python/dm/common/api/dmSudsApi.py b/src/python/dm/common/api/dmSudsApi.py
new file mode 100755
index 00000000..a9aecb6b
--- /dev/null
+++ b/src/python/dm/common/api/dmSudsApi.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+
+import os
+import shutil
+from functools import wraps
+from decorator import decorator
+
+from dm.common.exceptions.dmException import DmException
+from dm.common.api.dmApi import DmApi
+
+class DmSudsApi(DmApi):
+    """ Base dm api class that utilizes suds calls. """
+
+    SUDS_CACHE_DIR = '/tmp/suds'
+
+    def __init__(self, username=None, password=None):
+        DmApi.__init__(self, username=username, password=password)
+
+    # Remove suds cache directory if it exists.
+    # Workaround for bug in current suds package that leaves 
+    # cache files behind, which may result in permission denied errors
+    # for multiple users
+    @classmethod
+    def removeSudsCache(cls):
+        if os.path.exists(cls.SUDS_CACHE_DIR):
+            try:
+                shutil.rmtree(cls.SUDS_CACHE_DIR)
+            except Exception, ex:
+                cls.getLogger().warn('Cannot remove suds cache %s: %s' % (cls.SUDS_CACHE_DIR, ex))
+    
+    # Exception decorator for api calls
+    # Use two decorators for the sake of documentation
+    @classmethod
+    def executeSudsCall(cls, *dargs, **dkwargs):
+        def internalCall(func):
+            @wraps(func)
+            def wrappedCall(func, *args, **kwargs):
+                try:
+                    response = func(*args, **kwargs)
+                    cls.removeSudsCache()
+                    return response
+                except DmException, ex:
+                    cls.removeSudsCache()
+                    raise ex
+                except Exception, ex:
+                    cls.getLogger().exception('%s' % ex)
+                    cls.removeSudsCache()
+                    raise DmException(exception=ex)
+            return decorator(wrappedCall, func)
+        if len(dargs) == 1 and callable(dargs[0]):
+            return internalCall(dargs[0])
+        else:
+            return internalCall
+
+#######################################################################
+# Testing.
+
+if __name__ == '__main__':
+    pass
+
+
-- 
GitLab