2009-05-13 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.Dispatcher / OperationInvokerHandler.cs
1 using System;\r
2 using System.Collections.Generic;\r
3 using System.Text;\r
4 using System.ServiceModel.Channels;\r
5 using System.ServiceModel;\r
6 using System.Reflection;\r
7 \r
8 namespace System.ServiceModel.Dispatcher\r
9 {\r
10         internal class OperationInvokerHandler : BaseRequestProcessorHandler\r
11         {\r
12                 protected override bool ProcessRequest (MessageProcessingContext mrc)\r
13                 {                       \r
14                         RequestContext rc = mrc.RequestContext;\r
15                         DispatchRuntime dispatchRuntime = mrc.OperationContext.EndpointDispatcher.DispatchRuntime;\r
16                         DispatchOperation operation = GetOperation (mrc.IncomingMessage, dispatchRuntime);\r
17                         mrc.Operation = operation;\r
18                         try {                           \r
19                                 DoProcessRequest (mrc);\r
20                                 if (!operation.Invoker.IsSynchronous)\r
21                                         return true;\r
22                         } catch (TargetInvocationException ex) {\r
23                                 mrc.ReplyMessage = BuildExceptionMessage (mrc.IncomingMessage, ex.InnerException, \r
24                                         dispatchRuntime.ChannelDispatcher.IncludeExceptionDetailInFaults);\r
25                         }\r
26                         return false;\r
27                 }\r
28 \r
29                 void DoProcessRequest (MessageProcessingContext mrc)\r
30                 {\r
31                         DispatchOperation operation = mrc.Operation;\r
32                         Message req = mrc.IncomingMessage;\r
33                         object instance = mrc.InstanceContext.GetServiceInstance(req);\r
34                         object [] parameters;                   \r
35                         BuildInvokeParams (mrc, out parameters);\r
36 \r
37                         if (operation.Invoker.IsSynchronous) {\r
38                                 object result = operation.Invoker.Invoke (instance, parameters);\r
39                                 HandleInvokeResult (mrc, parameters, result);\r
40                         } else {// asynchronous\r
41                                 InvokeAsynchronous (mrc, instance, parameters);\r
42                         }                       \r
43                 }\r
44 \r
45                 void InvokeAsynchronous (MessageProcessingContext mrc, object instance, object [] parameters)\r
46                 {\r
47                         DispatchOperation operation = mrc.Operation;\r
48                         DispatchRuntime dispatchRuntime = mrc.OperationContext.EndpointDispatcher.DispatchRuntime;\r
49                         operation.Invoker.InvokeBegin (instance, parameters,\r
50                                         delegate (IAsyncResult res) {                                           \r
51                                                 try {\r
52                                                         object result;\r
53                                                         result = operation.Invoker.InvokeEnd (instance, out parameters, res);\r
54                                                         HandleInvokeResult (mrc, parameters, result);\r
55                                                         mrc.Reply (true);\r
56                                                 } catch (Exception ex) {\r
57                                                         mrc.ReplyMessage = BuildExceptionMessage (mrc.IncomingMessage, ex, dispatchRuntime.ChannelDispatcher.IncludeExceptionDetailInFaults);\r
58                                                         mrc.Reply (false);\r
59                                                 }                               \r
60                                         },\r
61                                         null);                  \r
62                 }\r
63 \r
64                 DispatchOperation GetOperation (Message input, DispatchRuntime dispatchRuntime)\r
65                 {\r
66                         if (dispatchRuntime.OperationSelector != null) {\r
67                                 string name = dispatchRuntime.OperationSelector.SelectOperation (ref input);\r
68                                 foreach (DispatchOperation d in dispatchRuntime.Operations)\r
69                                         if (d.Name == name)\r
70                                                 return d;\r
71                         } else {\r
72                                 string action = input.Headers.Action;\r
73                                 foreach (DispatchOperation d in dispatchRuntime.Operations)\r
74                                         if (d.Action == action)\r
75                                                 return d;\r
76                         }\r
77                         return dispatchRuntime.UnhandledDispatchOperation;\r
78                 }\r
79 \r
80                 void HandleInvokeResult (MessageProcessingContext mrc, object [] outputs, object result)\r
81                 {\r
82                         DispatchOperation operation = mrc.Operation;\r
83                         mrc.EventsHandler.AfterInvoke (operation);\r
84 \r
85                         Message res = null;\r
86                         if (operation.SerializeReply)\r
87                                 res = operation.Formatter.SerializeReply (\r
88                                         mrc.OperationContext.EndpointDispatcher.ChannelDispatcher.MessageVersion, outputs, result);\r
89                         else\r
90                                 res = (Message) result;\r
91                         res.Headers.CopyHeadersFrom (mrc.OperationContext.OutgoingMessageHeaders);\r
92                         res.Properties.CopyProperties (mrc.OperationContext.OutgoingMessageProperties);\r
93                         mrc.ReplyMessage = res;\r
94                 }\r
95 \r
96                 Message CreateActionNotSupported (Message req)\r
97                 {\r
98                         FaultCode fc = new FaultCode (\r
99                                 req.Version.Addressing.ActionNotSupported,\r
100                                 req.Version.Addressing.Namespace);\r
101                         // FIXME: set correct namespace URI\r
102                         return Message.CreateMessage (req.Version, fc,\r
103                                 String.Format ("action '{0}' is not supported in this service contract.", req.Headers.Action), String.Empty);\r
104                 }\r
105 \r
106                 void BuildInvokeParams (MessageProcessingContext mrc, out object [] parameters)\r
107                 {\r
108                         DispatchOperation operation = mrc.Operation;\r
109                         EnsureValid (operation);\r
110 \r
111                         if (operation.DeserializeRequest) {\r
112                                 parameters = operation.Invoker.AllocateParameters ();\r
113                                 operation.Formatter.DeserializeRequest (mrc.IncomingMessage, parameters);\r
114                         } else\r
115                                 parameters = new object [] { mrc.IncomingMessage };\r
116 \r
117                         mrc.EventsHandler.BeforeInvoke (operation);\r
118                 }\r
119 \r
120                 Message BuildExceptionMessage (Message req, Exception ex, bool includeDetailsInFault)\r
121                 {                       \r
122                         // FIXME: set correct name\r
123                         FaultCode fc = new FaultCode (\r
124                                 "InternalServiceFault",\r
125                                 req.Version.Addressing.Namespace);\r
126 \r
127 \r
128                         if (includeDetailsInFault) {\r
129                                 return Message.CreateMessage (req.Version, fc, ex.Message, new ExceptionDetail (ex), req.Headers.Action);\r
130                         }\r
131                         // MS returns: The server was unable to process the request due to an internal error.  For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the &lt;serviceDebug&gt; configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs.";\r
132                         //\r
133                         string faultString =\r
134                                 @"The server was unable to process the request due to an internal error.  The server may be able to return exception details (it depends on the server settings).";\r
135                         return Message.CreateMessage (req.Version, fc, faultString, req.Headers.Action);\r
136                 }\r
137 \r
138                 void EnsureValid (DispatchOperation operation)\r
139                 {\r
140                         if (operation.Invoker == null)\r
141                                 throw new InvalidOperationException ("DispatchOperation requires Invoker.");\r
142                         if ((operation.DeserializeRequest || operation.SerializeReply) && operation.Formatter == null)\r
143                                 throw new InvalidOperationException ("The DispatchOperation '" + operation.Name + "' requires Formatter, since DeserializeRequest and SerializeReply are not both false.");\r
144                 }               \r
145         }\r
146 }\r