aboutsummaryrefslogtreecommitdiffstats
path: root/actionwebservice/lib/action_web_service/dispatcher/abstract.rb
blob: e03446924a9aaba1a829ec67f48bbc06d729bc1a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
require 'benchmark'

module ActionWebService # :nodoc:
  module Dispatcher # :nodoc:
    class DispatcherError < ActionWebService::ActionWebServiceError # :nodoc:
    end

    def self.append_features(base) # :nodoc:
      super
      base.class_inheritable_option(:web_service_dispatching_mode, :direct)
      base.class_inheritable_option(:web_service_exception_reporting, true)
      base.send(:include, ActionWebService::Dispatcher::InstanceMethods)
    end

    module InstanceMethods # :nodoc:
      private
        def dispatch_web_service_request(action_pack_request)
          protocol_request = protocol_response = nil
          bm = Benchmark.measure do
            protocol_request = probe_request_protocol(action_pack_request)
            protocol_response = dispatch_protocol_request(protocol_request)
          end
          [protocol_request, protocol_response, bm.real, nil]
        rescue Exception => e
          protocol_response = prepare_exception_response(protocol_request, e) 
          [protocol_request, prepare_exception_response(protocol_request, e), nil, e]
        end
      
        def dispatch_protocol_request(protocol_request)
          case web_service_dispatching_mode
          when :direct
            dispatch_direct_request(protocol_request)
          when :delegated
            dispatch_delegated_request(protocol_request)
          else
            raise(ContainerError, "unsupported dispatching mode :#{web_service_dispatching_mode}")
          end
        end

        def dispatch_direct_request(protocol_request)
          request = prepare_dispatch_request(protocol_request)
          return_value = direct_invoke(request)
          protocol_request.marshal(return_value)
        end

        def dispatch_delegated_request(protocol_request)
          request = prepare_dispatch_request(protocol_request)
          return_value = delegated_invoke(request)
          protocol_request.marshal(return_value)
        end

        def direct_invoke(request)
          return nil unless before_direct_invoke(request)
          return_value = send(request.method_name)
          after_direct_invoke(request)
          return_value
        end

        def before_direct_invoke(request)
          @method_params = request.params
        end

        def after_direct_invoke(request)
        end

        def delegated_invoke(request)
          cancellation_reason = nil
          web_service = request.web_service
          return_value = web_service.perform_invocation(request.method_name, request.params) do |x|
            cancellation_reason = x
          end
          if cancellation_reason
            raise(DispatcherError, "request canceled: #{cancellation_reason}")
          end
          return_value
        end

        def fallback_invoke(dispatch_request)
          raise NotImplementedError
        end

        def prepare_dispatch_request(protocol_request)
          api = method_name = web_service_name = web_service = params = nil
          public_method_name = protocol_request.public_method_name
          case web_service_dispatching_mode
          when :direct
            api = self.class.web_service_api
          when :delegated
            web_service_name = protocol_request.web_service_name
            web_service = web_service_object(web_service_name)
            api = web_service.class.web_service_api
          end
          method_name  = api.api_method_name(public_method_name)
          signature = nil
          if method_name
            signature = api.api_methods[method_name]
            protocol_request.type = Protocol::CheckedMessage
            protocol_request.signature = signature[:expects]
            protocol_request.return_signature = signature[:returns]
          else
            method_name = api.default_api_method
            if method_name
              protocol_request.type = Protocol::UncheckedMessage
            else
              raise(DispatcherError, "no such method #{web_service_name}##{public_method_name}")
            end
          end
          params = protocol_request.unmarshal
          DispatchRequest.new(
            :api                => api,
            :public_method_name => public_method_name,
            :method_name        => method_name,
            :signature          => signature,
            :web_service_name   => web_service_name,
            :web_service        => web_service,
            :params             => params)
        end

        def prepare_exception_response(protocol_request, exception)
          if protocol_request && exception
            case web_service_dispatching_mode
            when :direct
              if web_service_exception_reporting
                return protocol_request.protocol.marshal_exception(exception)
              else
                raise exception
              end
            when :delegated
              web_service = web_service_object(protocol_request.web_service_name)
              if web_service && web_service.class.web_service_exception_reporting
                return protocol_request.protocol.marshal_exception(exception)
              else
                raise exception
              end
            end
          else
            protocol_request.protocol.marshal_exception(RuntimeError.new("missing protocol request or exception"))
          end
        rescue Exception
          nil
        end

        class DispatchRequest
          attr :api
          attr :public_method_name
          attr :method_name
          attr :signature
          attr :web_service_name
          attr :web_service
          attr :params

          def initialize(values={})
            values.each{|k,v| instance_variable_set("@#{k.to_s}", v)}
          end
        end
    end
  end
end