Browse Source

[core] Update exception handling in general

-Removed odd typing using 'response_data' and 'response' members as typifiers
-Standardized on PopupException and StructuredException
abec 13 years ago
parent
commit
027aaf2db9

+ 0 - 1
apps/beeswax/src/beeswax/tests.py

@@ -1059,7 +1059,6 @@ for x in sys.stdin:
 
   def test_select_query_server(self):
     c = make_logged_in_client()
-
     _make_query(c, 'SELECT bogus FROM test', server='default') # Improvement: mock another server
 
     history = beeswax.models.QueryHistory.objects.latest('id')

+ 3 - 1
apps/jobsub/src/jobsub/middleware.py

@@ -15,6 +15,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from desktop.lib.django_util import StructuredException
+
 from jobsubd.ttypes import SubmissionError
 
 class SubmissionErrorRecastMiddleware(object):
@@ -27,4 +29,4 @@ class SubmissionErrorRecastMiddleware(object):
   """
   def process_exception(self, request, exception):
     if isinstance(exception, SubmissionError) and not hasattr(SubmissionError, "response_data"):
-      exception.response_data = dict(code="SUBMISSION_ERROR", message=exception.message)
+    	raise StructuredException(code="SUBMISSION_ERROR", message=exception.message)

+ 11 - 6
desktop/core/src/desktop/lib/django_util.py

@@ -309,15 +309,16 @@ def get_app_nice_name(app_name):
 
 class StructuredException(Exception):
   """
-  Many exceptions in this application are a string and a filename
+  Many exceptions in this application are a string and some data
   that applies to.  The middleware will take these exceptions
   and render them.
   """
-  def __init__(self, code, message, data=None):
+  def __init__(self, code, message, data=None, error_code=500):
     Exception.__init__(self, message)
     self.code = code
     self.message = message
     self.data = data
+    self.error_code = error_code
 
   def __str__(self):
     return "%s (code %s): %s" % (self.message, self.code, repr(self.data))
@@ -334,28 +335,32 @@ class MessageException(StructuredException):
 
   This has been superceded by PopupException.
   """
-  def __init__(self, msg, filename=None):
+  def __init__(self, msg, filename=None, error_code=500):
     StructuredException.__init__(self,
       code="GENERIC_MESSAGE",
       message=msg,
-      data=dict(filename=filename))
+      data=dict(filename=filename),
+      error_code=error_code)
 
 class PopupException(Exception):
   """
   Middleware will render this exception; and the template
   renders it as a pop-up.
   """
-  def __init__(self, message, title="Error", detail=None):
+  def __init__(self, message, title="Error", detail=None, error_code=500):
     Exception.__init__(self, message)
     self.message = message
     self.title = title
     self.detail = detail
+    self.error_code = error_code
 
   def response(self, request):
     data = dict(title=self.title, message=self.message, detail=self.detail)
     if not request.ajax:
       data['request'] = request
-    return render("popup_error.mako", request, data)
+    response = render("popup_error.mako", request, data)
+    response.status_code = self.error_code
+    return response
 
 class TruncatingModel(models.Model):
   """

+ 12 - 4
desktop/core/src/desktop/lib/thrift_util.py

@@ -17,6 +17,7 @@
 #
 # Utilities for Thrift
 import desktop.lib.eventlet_util
+from desktop.lib.django_util import StructuredException
 
 import Queue
 import logging
@@ -27,7 +28,7 @@ import time
 import sasl
 import sys
 
-from thrift.Thrift import TType
+from thrift.Thrift import TType, TApplicationException
 from thrift.transport.TSocket import TSocket
 from thrift.transport.TTransport import TBufferedTransport, TMemoryBuffer,\
                                         TTransportException
@@ -284,12 +285,19 @@ class PooledClient(object):
             superclient.set_timeout(self.conf.timeout_seconds)
             ret = res(*args, **kwargs)
             return ret
+          except TApplicationException, e:
+            # Unknown thrift exception... typically IO errors
+            logging.info("Thrift saw an application exception: " + str(e), exc_info=False)
+            raise StructuredException('THRIFTAPPLICATION', str(e), data=None, error_code=502)
+          except socket.error, e:
+            logging.info("Thrift saw a socket error: " + str(e), exc_info=False)
+            raise StructuredException('THRIFTSOCKET', str(e), data=None, error_code=502)
+          except TTransportException, e:
+            logging.info("Thrift saw a transport exception: " + str(e), exc_info=False)
+            raise StructuredException('THRIFTTRANSPORT', str(e), data=None, error_code=502)
           except Exception, e:
             # Stack tends to be only noisy here.
             logging.info("Thrift saw exception: " + str(e), exc_info=False)
-            msg = "Exception communicating with %s at %s:%s: %s" % (
-              self.conf.service_name, self.conf.host, self.conf.port, str(e))
-            e.response_data = dict(code="THRIFT_EXCEPTION", message=msg, data="")
             raise
         finally:
           _connection_pool.return_client(self.conf, superclient)

+ 7 - 12
desktop/core/src/desktop/middleware.py

@@ -33,7 +33,7 @@ import django.views.generic.simple
 
 import desktop.conf
 from desktop.lib import apputil, i18n
-from desktop.lib.django_util import render, render_json, is_jframe_request, PopupException
+from desktop.lib.django_util import render, render_json, is_jframe_request, PopupException, StructuredException
 from desktop.log.access import access_log, log_page_hit
 from desktop import appmanager
 from hadoop import cluster
@@ -70,25 +70,20 @@ class ExceptionMiddleware(object):
     logging.info("Processing exception: %s: %s" % (i18n.smart_unicode(exception),
                                                    i18n.smart_unicode(tb)))
 
-    if hasattr(exception, "response"):
+    if isinstance(exception, PopupException):
       return exception.response(request)
 
-    if hasattr(exception, "response_data"):
+    if isinstance(exception, StructuredException):
       if request.ajax:
         response = render_json(exception.response_data)
         response[MIDDLEWARE_HEADER] = 'EXCEPTION'
+        response.status_code = getattr(exception, 'error_code', 500)
         return response
       else:
-        return render("error.mako", request,
+        response = render("error.mako", request,
                       dict(error=exception.response_data.get("message")))
-
-    # We didn't handle it as a special exception, but if we're ajax we still
-    # need to do some kind of nicer handling than the built-in page
-    # Note that exception may actually be an Http404 or similar.
-    if request.ajax:
-      err = _("An error occurred: %(error)s") % {'error': exception}
-      logging.exception("Middleware caught an exception")
-      return PopupException(err, detail=None).response(request)
+        response.status_code = getattr(exception, 'error_code', 500)
+        return response
 
     return None
 

+ 5 - 12
desktop/libs/hadoop/src/hadoop/job_tracker.py

@@ -19,6 +19,7 @@
 
 from desktop.lib import thrift_util
 from desktop.lib.conf import validate_port
+from desktop.lib.django_util import StructuredException
 from desktop.lib.thrift_util import fixup_enums
 
 from hadoop.api.jobtracker import Jobtracker
@@ -251,8 +252,7 @@ class LiveJobTracker(object):
     try:
       job = self.client.getJob(self.thread_local.request_context, jobid)
     except JobNotFoundException, e:
-      e.response_data = dict(code="JT_JOB_NOT_FOUND", message="Could not find job %s on JobTracker." % jobid.asString, data=jobid)
-      raise
+      raise StructuredException(code="JT_JOB_NOT_FOUND", message="Could not find job %s on JobTracker." % jobid.asString, data=jobid)
     self._fixup_job(job)
     return job
 
@@ -281,8 +281,7 @@ class LiveJobTracker(object):
     try:
       job = self.client.getRetiredJob(self.thread_local.request_context, jobid)
     except JobNotFoundException, e:
-        e.response_data = dict(code="JT_JOB_NOT_FOUND", message="Could not find job %s on JobTracker." % jobid.asString, data=jobid)
-        raise
+        raise StructuredException(code="JT_JOB_NOT_FOUND", message="Could not find job %s on JobTracker." % jobid.asString, data=jobid)
     self._fixup_job(job)
     self._fixup_retired_job(job)
     return job
@@ -362,15 +361,9 @@ class LiveJobTracker(object):
     try:
       tip = self.client.getTask(self.thread_local.request_context, taskid)
     except JobNotFoundException, e:
-      e.response_data = dict(code="JT_JOB_NOT_FOUND",
-                             message="Could not find job %s on JobTracker." % (jobid.asString,),
-                             data=jobid)
-      raise e
+      raise StructuredException(code="JT_JOB_NOT_FOUND", message="Could not find job %s on JobTracker." % jobid.asString, data=jobid)
     except TaskNotFoundException, e:
-      e.response_data = dict(code="JT_TASK_NOT_FOUND",
-                             message="Could not find task %s on JobTracker." % (taskid.asString,),
-                             data=taskid)
-      raise e
+      raise StructuredException(code="JT_TASK_NOT_FOUND", message="Could not find task %s on JobTracker." % taskid.asString, data=taskid)
     self._fixup_task_in_progress(tip)
     return tip