New test.
[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 using System.Threading;
8
9 namespace System.ServiceModel.Dispatcher
10 {
11         internal class OperationInvokerHandler : BaseRequestProcessorHandler
12         {
13                 IDuplexChannel duplex;
14
15                 public OperationInvokerHandler (IChannel channel)
16                 {
17                         duplex = channel as IDuplexChannel;
18                 }
19
20                 protected override bool ProcessRequest (MessageProcessingContext mrc)
21                 {                       
22                         RequestContext rc = mrc.RequestContext;
23                         DispatchRuntime dispatchRuntime = mrc.OperationContext.EndpointDispatcher.DispatchRuntime;
24                         DispatchOperation operation = GetOperation (mrc.IncomingMessage, dispatchRuntime);
25                         mrc.Operation = operation;
26                         try {                           
27                                 DoProcessRequest (mrc);
28                                 if (!mrc.Operation.IsOneWay)
29                                         Reply (mrc, true);
30                         } catch (TargetInvocationException ex) {
31                                 mrc.ReplyMessage = BuildExceptionMessage (mrc, ex.InnerException, 
32                                         dispatchRuntime.ChannelDispatcher.IncludeExceptionDetailInFaults);
33                                 if (!mrc.Operation.IsOneWay)
34                                         Reply (mrc, true);
35                                 ProcessCustomErrorHandlers (mrc, ex);
36                         }
37                         return false;
38                 }
39
40                 void DoProcessRequest (MessageProcessingContext mrc)
41                 {
42                         DispatchOperation operation = mrc.Operation;
43                         Message req = mrc.IncomingMessage;
44                         object instance = mrc.InstanceContext.GetServiceInstance(req);
45                         object [] parameters, outParams;
46                         BuildInvokeParams (mrc, out parameters);
47
48                         if (operation.Invoker.IsSynchronous) {
49                                 object result = operation.Invoker.Invoke (instance, parameters, out outParams);
50                                 HandleInvokeResult (mrc, outParams, result);
51                         } else {
52                                 var ar = operation.Invoker.InvokeBegin (instance, parameters, null, null);
53                                 object result = operation.Invoker.InvokeEnd (instance, out outParams, ar);
54                                 HandleInvokeResult (mrc, outParams, result);
55                         }
56                 }
57
58                 void Reply (MessageProcessingContext mrc, bool useTimeout)
59                 {
60                         if (duplex != null)
61                                 mrc.Reply (duplex, useTimeout);
62                         else
63                                 mrc.Reply (useTimeout);
64                 }
65
66                 DispatchOperation GetOperation (Message input, DispatchRuntime dispatchRuntime)
67                 {
68                         if (dispatchRuntime.OperationSelector != null) {
69                                 string name = dispatchRuntime.OperationSelector.SelectOperation (ref input);
70                                 foreach (DispatchOperation d in dispatchRuntime.Operations)
71                                         if (d.Name == name)
72                                                 return d;
73                         } else {
74                                 string action = input.Headers.Action;
75                                 foreach (DispatchOperation d in dispatchRuntime.Operations)
76                                         if (d.Action == action)
77                                                 return d;
78                         }
79                         return dispatchRuntime.UnhandledDispatchOperation;
80                 }
81
82                 void HandleInvokeResult (MessageProcessingContext mrc, object [] outputs, object result)
83                 {
84                         DispatchOperation operation = mrc.Operation;
85                         mrc.EventsHandler.AfterInvoke (operation);
86
87                         if (operation.IsOneWay)
88                                 return;
89
90                         Message res = null;
91                         if (operation.SerializeReply)
92                                 res = operation.Formatter.SerializeReply (
93                                         mrc.OperationContext.EndpointDispatcher.ChannelDispatcher.MessageVersion, outputs, result);
94                         else
95                                 res = (Message) result;
96                         res.Headers.CopyHeadersFrom (mrc.OperationContext.OutgoingMessageHeaders);
97                         res.Properties.CopyProperties (mrc.OperationContext.OutgoingMessageProperties);
98                         mrc.ReplyMessage = res;
99                 }
100
101                 void BuildInvokeParams (MessageProcessingContext mrc, out object [] parameters)
102                 {
103                         DispatchOperation operation = mrc.Operation;
104                         EnsureValid (operation);
105
106                         if (operation.DeserializeRequest) {
107                                 parameters = operation.Invoker.AllocateInputs ();
108                                 operation.Formatter.DeserializeRequest (mrc.IncomingMessage, parameters);
109                         } else
110                                 parameters = new object [] { mrc.IncomingMessage };
111
112                         mrc.EventsHandler.BeforeInvoke (operation);
113                 }
114
115                 void ProcessCustomErrorHandlers (MessageProcessingContext mrc, Exception ex)
116                 {
117                         var dr = mrc.OperationContext.EndpointDispatcher.DispatchRuntime;
118                         bool shutdown = false;
119                         foreach (var eh in dr.ChannelDispatcher.ErrorHandlers)
120                                 shutdown |= eh.HandleError (ex);
121                         if (shutdown)
122                                 ProcessSessionErrorShutdown (mrc);
123                 }
124
125                 void ProcessSessionErrorShutdown (MessageProcessingContext mrc)
126                 {
127                         var dr = mrc.OperationContext.EndpointDispatcher.DispatchRuntime;
128                         var session = mrc.OperationContext.Channel.InputSession;
129                         var dcc = mrc.OperationContext.Channel as IDuplexContextChannel;
130                         if (session == null || dcc == null)
131                                 return;
132                         foreach (var h in dr.InputSessionShutdownHandlers)
133                                 h.ChannelFaulted (dcc);
134                 }
135
136                 Message BuildExceptionMessage (MessageProcessingContext mrc, Exception ex, bool includeDetailsInFault)
137                 {
138                         var dr = mrc.OperationContext.EndpointDispatcher.DispatchRuntime;
139                         var cd = dr.ChannelDispatcher;
140                         Message msg = null;
141                         foreach (var eh in cd.ErrorHandlers)
142                                 eh.ProvideFault (ex, cd.MessageVersion, ref msg);
143                         if (msg != null)
144                                 return msg;
145
146                         var req = mrc.IncomingMessage;
147
148                         // FIXME: set correct name
149                         FaultCode fc = new FaultCode (
150                                 "InternalServiceFault",
151                                 req.Version.Addressing.Namespace);
152
153
154                         if (includeDetailsInFault) {
155                                 return Message.CreateMessage (req.Version, fc, ex.Message, new ExceptionDetail (ex), req.Headers.Action);
156                         }
157
158                         string faultString =
159                                 @"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).";
160                         return Message.CreateMessage (req.Version, fc, faultString, req.Headers.Action);
161                 }
162
163                 void EnsureValid (DispatchOperation operation)
164                 {
165                         if (operation.Invoker == null)
166                                 throw new InvalidOperationException (String.Format ("DispatchOperation '{0}' for contract '{1}' requires Invoker.", operation.Name, operation.Parent.EndpointDispatcher.ContractName));
167                         if ((operation.DeserializeRequest || operation.SerializeReply) && operation.Formatter == null)
168                                 throw new InvalidOperationException ("The DispatchOperation '" + operation.Name + "' requires Formatter, since DeserializeRequest and SerializeReply are not both false.");
169                 }               
170         }
171 }