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