2010-04-06 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System.ServiceModel / System.ServiceModel.Dispatcher / OperationInvokerHandler.cs
blob3793bb2cba67b4ada5f2ddf70c4ff499bd37b325
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;
9 namespace System.ServiceModel.Dispatcher
11 internal class OperationInvokerHandler : BaseRequestProcessorHandler
13 IDuplexChannel duplex;
15 public OperationInvokerHandler (IChannel channel)
17 duplex = channel as IDuplexChannel;
20 protected override bool ProcessRequest (MessageProcessingContext mrc)
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);
37 return false;
40 void DoProcessRequest (MessageProcessingContext mrc)
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);
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);
58 void Reply (MessageProcessingContext mrc, bool useTimeout)
60 if (duplex != null)
61 mrc.Reply (duplex, useTimeout);
62 else
63 mrc.Reply (useTimeout);
66 DispatchOperation GetOperation (Message input, DispatchRuntime dispatchRuntime)
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;
79 return dispatchRuntime.UnhandledDispatchOperation;
82 void HandleInvokeResult (MessageProcessingContext mrc, object [] outputs, object result)
84 DispatchOperation operation = mrc.Operation;
85 mrc.EventsHandler.AfterInvoke (operation);
87 if (operation.IsOneWay)
88 return;
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;
101 void BuildInvokeParams (MessageProcessingContext mrc, out object [] parameters)
103 DispatchOperation operation = mrc.Operation;
104 EnsureValid (operation);
106 if (operation.DeserializeRequest) {
107 parameters = operation.Invoker.AllocateInputs ();
108 operation.Formatter.DeserializeRequest (mrc.IncomingMessage, parameters);
109 } else
110 parameters = new object [] { mrc.IncomingMessage };
112 mrc.EventsHandler.BeforeInvoke (operation);
115 void ProcessCustomErrorHandlers (MessageProcessingContext mrc, Exception ex)
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);
125 void ProcessSessionErrorShutdown (MessageProcessingContext mrc)
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);
136 Message BuildExceptionMessage (MessageProcessingContext mrc, Exception ex, bool includeDetailsInFault)
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;
146 var req = mrc.IncomingMessage;
148 var fe = ex as FaultException;
149 if (fe != null && fe.GetType ().IsGenericType) {
150 var t = fe.GetType ().GetGenericArguments () [0];
151 foreach (var fci in mrc.Operation.FaultContractInfos)
152 if (fci.Detail == t)
153 return Message.CreateMessage (req.Version, fe.CreateMessageFault (), fci.Action);
156 // FIXME: set correct name
157 FaultCode fc = new FaultCode (
158 "InternalServiceFault",
159 req.Version.Addressing.Namespace);
162 if (includeDetailsInFault) {
163 return Message.CreateMessage (req.Version, fc, ex.Message, new ExceptionDetail (ex), req.Headers.Action);
166 string faultString =
167 @"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).";
168 return Message.CreateMessage (req.Version, fc, faultString, req.Headers.Action);
171 void EnsureValid (DispatchOperation operation)
173 if (operation.Invoker == null)
174 throw new InvalidOperationException (String.Format ("DispatchOperation '{0}' for contract '{1}' requires Invoker.", operation.Name, operation.Parent.EndpointDispatcher.ContractName));
175 if ((operation.DeserializeRequest || operation.SerializeReply) && operation.Formatter == null)
176 throw new InvalidOperationException ("The DispatchOperation '" + operation.Name + "' requires Formatter, since DeserializeRequest and SerializeReply are not both false.");