2005-02-10 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / class / corlib / System.Runtime.Remoting / RemotingConfiguration.cs
1 //
2 // System.Runtime.Remoting.RemotingConfiguration.cs
3 //
4 // Author: Jaime Anguiano Olarra (jaime@gnome.org)
5 //         Lluis Sanchez Gual (lluis@ideary.com)
6 //
7 // (C) 2002, Jaime Anguiano Olarra
8 //
9
10 //
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.IO;
35 using System.Reflection;
36 using System.Collections;
37 using System.Runtime.Remoting.Activation;
38 using System.Runtime.Remoting.Channels;
39 using System.Runtime.Remoting.Lifetime;
40 using Mono.Xml;
41
42 namespace System.Runtime.Remoting
43 {       
44         public class RemotingConfiguration
45         {
46                 //
47                 // Private constructor: nobody instantiates this.
48                 //
49                 private RemotingConfiguration ()
50                 {
51                 }
52                 
53                 static string applicationID = null;
54                 static string applicationName = null;
55                 static string configFile = "";
56                 static MiniParser parser = null; 
57                 static string processGuid = null;
58                 static bool defaultConfigRead = false;
59                 static bool defaultDelayedConfigRead = false;
60                 static string _errorMode;
61
62                 static Hashtable wellKnownClientEntries = new Hashtable();
63                 static Hashtable activatedClientEntries = new Hashtable();
64                 static Hashtable wellKnownServiceEntries = new Hashtable();
65                 static Hashtable activatedServiceEntries = new Hashtable();
66                 
67                 static Hashtable channelTemplates = new Hashtable ();
68                 static Hashtable clientProviderTemplates = new Hashtable ();
69                 static Hashtable serverProviderTemplates = new Hashtable ();
70                 
71                 // public properties
72                 // At this time the ID will be the application name 
73                 public static string ApplicationId 
74                 {
75                         get 
76                         { 
77                                 applicationID = AppDomain.CurrentDomain.SetupInformation.ApplicationName; 
78                                 return applicationID;
79                         }
80                 }
81                 
82                 public static string ApplicationName 
83                 {
84                         get { 
85                                 try {
86                                         applicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName; 
87                                 }
88                                 catch (Exception e) {
89                                         throw e;
90                                 }
91                                 // We return null if the application name has not been set.
92                                 return null;
93                         }
94                         set { applicationName = value; }
95                 }
96                 
97                 public static string ProcessId 
98                 {
99                         get {
100                                 if (processGuid == null)
101                                         processGuid = AppDomain.GetProcessGuid ();
102
103                                 return processGuid;
104                         }
105                 }
106
107
108                 // public methods
109                 
110                 public static void Configure (string filename) 
111                 {
112                         lock (channelTemplates)
113                         {
114                                 if (!defaultConfigRead)
115                                 {
116                                         ReadConfigFile (Environment.GetMachineConfigPath ());
117                                         defaultConfigRead = true;
118                                 }
119                                 
120                                 if (filename != null)
121                                         ReadConfigFile (filename);
122                         }
123                 }
124
125                 private static void ReadConfigFile (string filename)
126                 {
127                         try
128                         {
129                                 MiniParser parser = new MiniParser ();
130                                 RReader rreader = new RReader (filename);
131                                 ConfigHandler handler = new ConfigHandler (false);
132                                 parser.Parse (rreader, handler);
133                         }
134                         catch (Exception ex)
135                         {
136                                 throw new RemotingException ("Configuration file '" + filename + "' could not be loaded: " + ex.Message);
137                         }
138                 }
139                 
140                 internal static void LoadDefaultDelayedChannels ()
141                 {
142                         lock (channelTemplates)
143                         {
144                                 if (defaultDelayedConfigRead || defaultConfigRead) return;
145                                 
146                                 MiniParser parser = new MiniParser ();
147                                 RReader rreader = new RReader (Environment.GetMachineConfigPath ());
148                                 ConfigHandler handler = new ConfigHandler (true);
149                                 parser.Parse (rreader, handler);
150                                 defaultDelayedConfigRead = true;
151                         }
152                 }
153         
154                 public static ActivatedClientTypeEntry[] GetRegisteredActivatedClientTypes () 
155                 {
156                         lock (channelTemplates)
157                         {
158                                 ActivatedClientTypeEntry[] entries = new ActivatedClientTypeEntry[activatedClientEntries.Count];
159                                 activatedClientEntries.Values.CopyTo (entries,0);
160                                 return entries;
161                         }
162                 }
163
164                 public static ActivatedServiceTypeEntry[] GetRegisteredActivatedServiceTypes () 
165                 {
166                         lock (channelTemplates)
167                         {
168                                 ActivatedServiceTypeEntry[] entries = new ActivatedServiceTypeEntry[activatedServiceEntries.Count];
169                                 activatedServiceEntries.Values.CopyTo (entries,0);
170                                 return entries;
171                         }
172                 }
173
174                 public static WellKnownClientTypeEntry[] GetRegisteredWellKnownClientTypes () 
175                 {
176                         lock (channelTemplates)
177                         {
178                                 WellKnownClientTypeEntry[] entries = new WellKnownClientTypeEntry[wellKnownClientEntries.Count];
179                                 wellKnownClientEntries.Values.CopyTo (entries,0);
180                                 return entries;
181                         }
182                 }
183
184                 public static WellKnownServiceTypeEntry[] GetRegisteredWellKnownServiceTypes () 
185                 {
186                         lock (channelTemplates)
187                         {
188                                 WellKnownServiceTypeEntry[] entries = new WellKnownServiceTypeEntry[wellKnownServiceEntries.Count];
189                                 wellKnownServiceEntries.Values.CopyTo (entries,0);
190                                 return entries;
191                         }
192                 }
193
194                 public static bool IsActivationAllowed (Type serverType) 
195                 {
196                         lock (channelTemplates)
197                         {
198                                 return activatedServiceEntries.ContainsKey (serverType);
199                         }
200                 }
201
202                 public static ActivatedClientTypeEntry IsRemotelyActivatedClientType (Type serviceType) 
203                 {
204                         lock (channelTemplates)
205                         {
206                                 return activatedClientEntries [serviceType] as ActivatedClientTypeEntry;
207                         }
208                 }
209
210                 public static ActivatedClientTypeEntry IsRemotelyActivatedClientType (string typeName, string assemblyName) 
211                 {
212                         return IsRemotelyActivatedClientType (Assembly.Load(assemblyName).GetType (typeName));
213                 }
214
215                 public static WellKnownClientTypeEntry IsWellKnownClientType (Type serviceType) 
216                 {
217                         lock (channelTemplates)
218                         {
219                                 return wellKnownClientEntries [serviceType] as WellKnownClientTypeEntry;
220                         }
221                 }
222
223                 public static WellKnownClientTypeEntry IsWellKnownClientType (string typeName, string assemblyName) 
224                 {
225                         return IsWellKnownClientType (Assembly.Load(assemblyName).GetType (typeName));
226                 }
227
228                 public static void RegisterActivatedClientType (ActivatedClientTypeEntry entry) 
229                 {
230                         lock (channelTemplates)
231                         {
232                                 if (wellKnownClientEntries.ContainsKey (entry.ObjectType) || activatedClientEntries.ContainsKey (entry.ObjectType))
233                                         throw new RemotingException ("Attempt to redirect activation of type '" + entry.ObjectType.FullName + "' which is already redirected.");
234         
235                                 activatedClientEntries[entry.ObjectType] = entry;
236                                 ActivationServices.EnableProxyActivation (entry.ObjectType, true);
237                         }
238                 }
239
240                 public static void RegisterActivatedClientType (Type type, string appUrl) 
241                 {
242                         if (type == null) throw new ArgumentNullException ("type");
243                         if (appUrl == null) throw new ArgumentNullException ("appUrl");
244
245                         RegisterActivatedClientType (new ActivatedClientTypeEntry (type, appUrl));
246                 }
247
248                 public static void RegisterActivatedServiceType (ActivatedServiceTypeEntry entry) 
249                 {
250                         lock (channelTemplates)
251                         {
252                                 activatedServiceEntries.Add (entry.ObjectType, entry);
253                         }
254                 }
255
256                 public static void RegisterActivatedServiceType (Type type) 
257                 {
258                         RegisterActivatedServiceType (new ActivatedServiceTypeEntry (type));
259                 }
260
261                 public static void RegisterWellKnownClientType (Type type, string objectUrl) 
262                 {
263                         if (type == null) throw new ArgumentNullException ("type");
264                         if (objectUrl == null) throw new ArgumentNullException ("objectUrl");
265
266                         RegisterWellKnownClientType (new WellKnownClientTypeEntry (type, objectUrl));
267                 }
268
269                 public static void RegisterWellKnownClientType (WellKnownClientTypeEntry entry) 
270                 {
271                         lock (channelTemplates)
272                         {
273                                 if (wellKnownClientEntries.ContainsKey (entry.ObjectType) || activatedClientEntries.ContainsKey (entry.ObjectType))
274                                         throw new RemotingException ("Attempt to redirect activation of type '" + entry.ObjectType.FullName + "' which is already redirected.");
275         
276                                 wellKnownClientEntries[entry.ObjectType] = entry;
277                                 ActivationServices.EnableProxyActivation (entry.ObjectType, true);
278                         }
279                 }
280
281                 public static void RegisterWellKnownServiceType (Type type, string objectUrl, WellKnownObjectMode mode) 
282                 {
283                         RegisterWellKnownServiceType (new WellKnownServiceTypeEntry (type, objectUrl, mode));
284                 }
285
286                 public static void RegisterWellKnownServiceType (WellKnownServiceTypeEntry entry) 
287                 {
288                         lock (channelTemplates)
289                         {
290                                 wellKnownServiceEntries [entry.ObjectUri] = entry;
291                                 RemotingServices.CreateWellKnownServerIdentity (entry.ObjectType, entry.ObjectUri, entry.Mode);
292                         }
293                 }
294
295                 internal static void RegisterChannelTemplate (ChannelData channel)
296                 {
297                         channelTemplates [channel.Id] = channel;
298                 }
299                 
300                 internal static void RegisterClientProviderTemplate (ProviderData prov)
301                 {
302                         clientProviderTemplates [prov.Id] = prov;
303                 }
304                 
305                 internal static void RegisterServerProviderTemplate (ProviderData prov)
306                 {
307                         serverProviderTemplates [prov.Id] = prov;
308                 }
309                 
310                 internal static void RegisterChannels (ArrayList channels, bool onlyDelayed)
311                 {
312                         foreach (ChannelData channel in channels)
313                         {
314                                 if (onlyDelayed && channel.DelayLoadAsClientChannel != "true")
315                                         continue;
316                                         
317                                 if (defaultDelayedConfigRead && channel.DelayLoadAsClientChannel == "true")
318                                         continue;
319                                         
320                                 if (channel.Ref != null)
321                                 {
322                                         ChannelData template = (ChannelData) channelTemplates [channel.Ref];
323                                         if (template == null) throw new RemotingException ("Channel template '" + channel.Ref + "' not found");
324                                         channel.CopyFrom (template);
325                                 }
326                                 
327                                 foreach (ProviderData prov in channel.ServerProviders)
328                                 {
329                                         if (prov.Ref != null)
330                                         {
331                                                 ProviderData template = (ProviderData) serverProviderTemplates [prov.Ref];
332                                                 if (template == null) throw new RemotingException ("Provider template '" + prov.Ref + "' not found");
333                                                 prov.CopyFrom (template);
334                                         }
335                                 }
336                                 
337                                 foreach (ProviderData prov in channel.ClientProviders)
338                                 {
339                                         if (prov.Ref != null)
340                                         {
341                                                 ProviderData template = (ProviderData) clientProviderTemplates [prov.Ref];
342                                                 if (template == null) throw new RemotingException ("Provider template '" + prov.Ref + "' not found");
343                                                 prov.CopyFrom (template);
344                                         }
345                                 }
346                                 
347                                 ChannelServices.RegisterChannelConfig (channel);
348                         }
349                 }
350                 
351                 internal static void RegisterTypes (ArrayList types)
352                 {
353                         foreach (TypeEntry type in types)
354                         {
355                                 if (type is ActivatedClientTypeEntry)
356                                         RegisterActivatedClientType ((ActivatedClientTypeEntry)type);
357                                 else if (type is ActivatedServiceTypeEntry)
358                                         RegisterActivatedServiceType ((ActivatedServiceTypeEntry)type);
359                                 else if (type is WellKnownClientTypeEntry)
360                                         RegisterWellKnownClientType ((WellKnownClientTypeEntry)type);
361                                 else if (type is WellKnownServiceTypeEntry)
362                                         RegisterWellKnownServiceType ((WellKnownServiceTypeEntry)type);
363                         }
364                 }
365                 
366 #if NET_1_1
367                 public static bool CustomErrorsEnabled (bool isLocalRequest)
368                 {
369                         if (_errorMode == "off") return false;
370                         if (_errorMode == "on") return true;
371                         return !isLocalRequest;
372                 }
373 #endif
374
375                 internal static void SetCustomErrorsMode (string mode)
376                 {
377                         if (mode != "on" && mode != "off" && mode != "remoteOnly")
378                                 throw new RemotingException ("Invalid custom error mode: " + mode);
379                                 
380                         _errorMode = mode;
381                 }
382         }
383
384         /***************************************************************
385          * Internal classes used by RemotingConfiguration.Configure () *
386          ***************************************************************/
387          
388         internal class RReader : MiniParser.IReader {
389                 private string xml; // custom remoting config file
390                 private int pos;
391
392                 public RReader (string filename)
393                 {
394                         try {
395                                 StreamReader sr = new StreamReader (filename);
396                                 xml = sr.ReadToEnd ();
397                                 sr.Close ();
398                         }
399                         catch {
400                                 xml = null;
401                         }
402                 }
403
404                 public int Read () {
405                         try {
406                                 return (int) xml[pos++];
407                         }
408                         catch {
409                                 return -1;
410                         }
411                 }
412         }
413         
414         internal class ConfigHandler : MiniParser.IHandler 
415         {
416                 ArrayList typeEntries = new ArrayList ();
417                 ArrayList channelInstances = new ArrayList ();
418                 
419                 ChannelData currentChannel = null;
420                 Stack currentProviderData = null;
421                 
422                 string currentClientUrl = null;
423                 string appName;
424                 
425                 string currentXmlPath = "";
426                 bool onlyDelayedChannels;
427                 
428                 public ConfigHandler (bool onlyDelayedChannels)
429                 {
430                         this.onlyDelayedChannels = onlyDelayedChannels;
431                 }
432                 
433                 void ValidatePath (string element, params string[] paths)
434                 {
435                         foreach (string path in paths)
436                                 if (CheckPath (path)) return;
437                                 
438                         throw new RemotingException ("Element " + element + " not allowed in this context");
439                 }
440                 
441                 bool CheckPath (string path)
442                 {
443                         if (path.StartsWith ("/"))
444                                 return path == currentXmlPath;
445                         else
446                                 return currentXmlPath.EndsWith (path);
447                 }
448                 
449                 public void OnStartParsing (MiniParser parser) {}
450                 
451                 public void OnStartElement (string name, MiniParser.IAttrList attrs)
452                 {
453                         try
454                         {
455                                 if (currentXmlPath.StartsWith ("/configuration/system.runtime.remoting"))
456                                         ParseElement (name, attrs);
457                                         
458                                 currentXmlPath += "/" + name;
459                         }
460                         catch (Exception ex)
461                         {
462                                 throw new RemotingException ("Error in element " + name + ": " + ex.Message);
463                         }
464                 }
465                 
466                 public void ParseElement (string name, MiniParser.IAttrList attrs)
467                 {
468                         if (currentProviderData != null)
469                         {
470                                 ReadCustomProviderData (name, attrs);
471                                 return;
472                         }
473                         
474                         switch (name) 
475                         {
476                                 case "application":
477                                         ValidatePath (name, "system.runtime.remoting");
478                                         if (attrs.Names.Length > 0)
479                                                 appName = attrs.Values[0];
480                                         break;
481                                         
482                                 case "lifetime":
483                                         ValidatePath (name, "application");
484                                         ReadLifetine (attrs);
485                                         break;
486                                         
487                                 case "channels":
488                                         ValidatePath (name, "system.runtime.remoting", "application");
489                                         break;
490                                         
491                                 case "channel":
492                                         ValidatePath (name, "channels");
493                                         if (currentXmlPath.IndexOf ("application") != -1)
494                                                 ReadChannel (attrs, false);
495                                         else
496                                                 ReadChannel (attrs, true);
497                                         break;
498                                         
499                                 case "serverProviders":
500                                         ValidatePath (name, "channelSinkProviders", "channel");
501                                         break;
502                                         
503                                 case "clientProviders":
504                                         ValidatePath (name, "channelSinkProviders", "channel");
505                                         break;
506                                         
507                                 case "provider":
508                                 case "formatter":
509                                         ProviderData prov;
510                                         
511                                         if (CheckPath ("application/channels/channel/serverProviders") ||
512                                                 CheckPath ("channels/channel/serverProviders"))
513                                         {
514                                                 prov = ReadProvider (name, attrs, false);
515                                                 currentChannel.ServerProviders.Add (prov);
516                                         }
517                                         else if (CheckPath ("application/channels/channel/clientProviders") ||
518                                                 CheckPath ("channels/channel/clientProviders"))
519                                         {
520                                                 prov = ReadProvider (name, attrs, false);
521                                                 currentChannel.ClientProviders.Add (prov);
522                                         }
523                                         else if (CheckPath ("channelSinkProviders/serverProviders"))
524                                         {
525                                                 prov = ReadProvider (name, attrs, true);
526                                                 RemotingConfiguration.RegisterServerProviderTemplate (prov);
527                                         }
528                                         else if (CheckPath ("channelSinkProviders/clientProviders"))
529                                         {
530                                                 prov = ReadProvider (name, attrs, true);
531                                                 RemotingConfiguration.RegisterClientProviderTemplate (prov);
532                                         }
533                                         else 
534                                                 ValidatePath (name);
535                                         break;
536                                         
537                                 case "client":
538                                         ValidatePath (name, "application");
539                                         currentClientUrl = attrs.GetValue ("url");
540                                         break;
541                                         
542                                 case "service":
543                                         ValidatePath (name, "application");
544                                         break;
545                                         
546                                 case "wellknown":
547                                         ValidatePath (name, "client", "service");
548                                         if (CheckPath ("client"))
549                                                 ReadClientWellKnown (attrs);
550                                         else
551                                                 ReadServiceWellKnown (attrs);
552                                         break;
553                                         
554                                 case "activated":
555                                         ValidatePath (name, "client", "service");
556                                         if (CheckPath ("client"))
557                                                 ReadClientActivated (attrs);
558                                         else
559                                                 ReadServiceActivated (attrs);
560                                         break;
561                                         
562                                 case "soapInterop":
563                                         ValidatePath (name, "application");
564                                         break;
565                                         
566                                 case "interopXmlType":
567                                         ValidatePath (name, "soapInterop");
568                                         ReadInteropXml (attrs, false);
569                                         break;
570                                         
571                                 case "interopXmlElement":
572                                         ValidatePath (name, "soapInterop");
573                                         ReadInteropXml (attrs, false);
574                                         break;
575                                         
576                                 case "preLoad":
577                                         ValidatePath (name, "soapInterop");
578                                         ReadPreload (attrs);
579                                         break;
580                                         
581                                 case "debug":
582                                         ValidatePath (name, "system.runtime.remoting");
583                                         break;
584                                         
585                                 case "channelSinkProviders":
586                                         ValidatePath (name, "system.runtime.remoting");
587                                         break;
588                                         
589                                 case "customErrors":
590                                         ValidatePath (name, "system.runtime.remoting");
591                                         RemotingConfiguration.SetCustomErrorsMode (attrs.GetValue ("mode"));
592                                         break;
593                                         
594                                 default:
595                                         throw new RemotingException ("Element '" + name + "' is not valid in system.remoting.configuration section");
596                         }
597                 }
598                 
599                 public void OnEndElement (string name)
600                 {
601                         if (currentProviderData != null)
602                         {
603                                 currentProviderData.Pop ();
604                                 if (currentProviderData.Count > 0) return;
605                                 currentProviderData = null;
606                         }
607                         
608                         currentXmlPath = currentXmlPath.Substring (0, currentXmlPath.Length - name.Length - 1);
609                 }
610                 
611                 void ReadCustomProviderData (string name, MiniParser.IAttrList attrs)
612                 {
613                         SinkProviderData parent = (SinkProviderData) currentProviderData.Peek ();
614                         
615                         SinkProviderData data = new SinkProviderData (name);
616                         for (int i=0; i < attrs.Names.Length; ++i) 
617                                 data.Properties [attrs.Names[i]] = attrs.GetValue (i);
618                                 
619                         parent.Children.Add (data);
620                         currentProviderData.Push (data);
621                 }
622
623                 void ReadLifetine (MiniParser.IAttrList attrs)
624                 {
625                         for (int i=0; i < attrs.Names.Length; ++i) {
626                                 switch (attrs.Names[i]) {
627                                 case "leaseTime":
628                                         LifetimeServices.LeaseTime = ParseTime (attrs.GetValue(i));
629                                         break;
630                                 case "sponsorshipTimeout":
631                                         LifetimeServices.SponsorshipTimeout = ParseTime (attrs.GetValue(i));
632                                         break;
633                                 case "renewOnCallTime":
634                                         LifetimeServices.RenewOnCallTime = ParseTime (attrs.GetValue(i));
635                                         break;
636                                 case "leaseManagerPollTime":
637                                         LifetimeServices.LeaseManagerPollTime = ParseTime (attrs.GetValue(i));
638                                         break;
639                                 default:
640                                         throw new RemotingException ("Invalid attribute: " + attrs.Names[i]);
641                                 }
642                         }
643                 }
644                 
645                 TimeSpan ParseTime (string s)
646                 {
647                         if (s == "" || s == null) throw new RemotingException ("Invalid time value");
648                         
649                         int i = s.IndexOfAny (new char[] { 'D','H','M','S' });
650                         
651                         string unit;
652                         if (i == -1) 
653                                 unit = "S";
654                         else { 
655                                 unit = s.Substring (i);
656                                 s = s.Substring (0,i);
657                         }
658                         double val;
659                         
660                         try {
661                                 val = double.Parse (s);
662                         }
663                         catch {
664                                 throw new RemotingException ("Invalid time value: " + s);
665                         }
666                         
667                         if (unit == "D") return TimeSpan.FromDays (val);
668                         if (unit == "H") return TimeSpan.FromHours (val);
669                         if (unit == "M") return TimeSpan.FromMinutes (val);
670                         if (unit == "S") return TimeSpan.FromSeconds (val);
671                         if (unit == "MS") return TimeSpan.FromMilliseconds (val);
672                         throw new RemotingException ("Invalid time unit: " + unit);
673                 }
674                 
675                 void ReadChannel (MiniParser.IAttrList attrs, bool isTemplate)
676                 {
677                         ChannelData channel = new ChannelData ();
678                         
679                         for (int i=0; i < attrs.Names.Length; ++i) 
680                         {
681                                 string at = attrs.Names[i];
682                                 string val = attrs.Values[i];
683                                 
684                                 if (at == "ref" && !isTemplate)
685                                         channel.Ref = val;
686                                 else if (at == "delayLoadAsClientChannel")
687                                         channel.DelayLoadAsClientChannel = val;
688                                 else if (at == "id" && isTemplate)
689                                         channel.Id = val;
690                                 else if (at == "type")
691                                         channel.Type = val;
692                                 else
693                                         channel.CustomProperties.Add (at, val);
694                         }
695                         
696                         if (isTemplate)
697                         {
698                                 if (channel.Id == null) throw new RemotingException ("id attribute is required");
699                                 if (channel.Type == null) throw new RemotingException ("id attribute is required");
700                                 RemotingConfiguration.RegisterChannelTemplate (channel);
701                         }
702                         else
703                                 channelInstances.Add (channel);
704                                 
705                         currentChannel = channel;
706                 }
707                 
708                 ProviderData ReadProvider (string name, MiniParser.IAttrList attrs, bool isTemplate)
709                 {
710                         ProviderData prov = (name == "provider") ? new ProviderData () : new FormatterData ();
711                         SinkProviderData data = new SinkProviderData ("root");
712                         prov.CustomData = data.Children;
713                         
714                         currentProviderData = new Stack ();
715                         currentProviderData.Push (data);
716                         
717                         for (int i=0; i < attrs.Names.Length; ++i) 
718                         {
719                                 string at = attrs.Names[i];
720                                 string val = attrs.Values[i];
721                                 
722                                 if (at == "id" && isTemplate)
723                                         prov.Id = val;
724                                 else if (at == "type")
725                                         prov.Type = val;
726                                 else if (at == "ref" && !isTemplate)
727                                         prov.Ref = val;
728                                 else
729                                         prov.CustomProperties.Add (at, val);
730                         }
731                         
732                         if (prov.Id == null && isTemplate) throw new RemotingException ("id attribute is required");
733                         return prov;
734                 }
735                 
736                 void ReadClientActivated (MiniParser.IAttrList attrs)
737                 {
738                         string type = GetNotNull (attrs, "type");
739                         string assm = ExtractAssembly (ref type);
740                         
741                         if (currentClientUrl == null || currentClientUrl == "") 
742                                 throw new RemotingException ("url attribute is required in client element when it contains activated entries");
743
744                         typeEntries.Add (new ActivatedClientTypeEntry (type, assm, currentClientUrl));
745                 }
746                 
747                 void ReadServiceActivated (MiniParser.IAttrList attrs)
748                 {
749                         string type = GetNotNull (attrs, "type");
750                         string assm = ExtractAssembly (ref type);
751                         
752                         typeEntries.Add (new ActivatedServiceTypeEntry (type, assm));
753                 }
754                 
755                 void ReadClientWellKnown (MiniParser.IAttrList attrs)
756                 {
757                         string url = GetNotNull (attrs, "url");
758                         string type = GetNotNull (attrs, "type");
759                         string assm = ExtractAssembly (ref type);
760                         
761                         typeEntries.Add (new WellKnownClientTypeEntry (type, assm, url));
762                 }
763                 
764                 void ReadServiceWellKnown (MiniParser.IAttrList attrs)
765                 {
766                         string objectUri = GetNotNull (attrs, "objectUri");
767                         string smode = GetNotNull (attrs, "mode");
768                         string type = GetNotNull (attrs, "type");
769                         string assm = ExtractAssembly (ref type);
770                         
771                         WellKnownObjectMode mode;
772                         if (smode == "SingleCall") mode = WellKnownObjectMode.SingleCall;
773                         else if (smode == "Singleton") mode = WellKnownObjectMode.Singleton;
774                         else throw new RemotingException ("wellknown object mode '" + smode + "' is invalid");
775                         
776                         typeEntries.Add (new WellKnownServiceTypeEntry (type, assm, objectUri, mode));
777                 }
778                 
779                 void ReadInteropXml (MiniParser.IAttrList attrs, bool isElement)
780                 {
781                         Type t = Type.GetType (GetNotNull (attrs, "clr"));
782                         string[] xmlName = GetNotNull (attrs, "xml").Split (',');
783                         string localName = xmlName [0].Trim ();
784                         string ns = xmlName.Length > 0 ? xmlName[1].Trim() : null;
785                         
786                         if (isElement) SoapServices.RegisterInteropXmlElement (localName, ns, t);
787                         else SoapServices.RegisterInteropXmlType (localName, ns, t);
788                 }
789                 
790                 void ReadPreload (MiniParser.IAttrList attrs)
791                 {
792                         string type = attrs.GetValue ("type");
793                         string assm = attrs.GetValue ("assembly");
794                         
795                         if (type != null && assm != null)
796                                 throw new RemotingException ("Type and assembly attributes cannot be specified together");
797                                 
798                         if (type != null)
799                                 SoapServices.PreLoad (Type.GetType (type));
800                         else if (assm != null)
801                                 SoapServices.PreLoad (Assembly.Load (assm));
802                         else
803                                 throw new RemotingException ("Either type or assembly attributes must be specified");
804                 }
805                                         
806                 string GetNotNull (MiniParser.IAttrList attrs, string name)
807                 {
808                         string value = attrs.GetValue (name);
809                         if (value == null || value == "") 
810                                 throw new RemotingException (name + " attribute is required");
811                         return value;
812                 }
813                 
814                 string ExtractAssembly (ref string type)
815                 {
816                         int i = type.IndexOf (',');
817                         if (i == -1) return "";
818                         
819                         string asm = type.Substring (i+1).Trim();
820                         type = type.Substring (0, i).Trim();
821                         return asm;
822                 }
823                 
824                 public void OnChars (string ch) {}
825                 
826                 public void OnEndParsing (MiniParser parser)
827                 {
828                         RemotingConfiguration.RegisterChannels (channelInstances, onlyDelayedChannels);
829                         if (appName != null) RemotingConfiguration.ApplicationName = appName;
830                         
831                         if (!onlyDelayedChannels)
832                                 RemotingConfiguration.RegisterTypes (typeEntries);
833                 }
834         }
835
836
837                 /*******************************************************************
838          * Internal data structures used by ConfigHandler, to store             *
839          * machine.config's remoting related data.                         *
840          * If having them implemented this way, makes configuration too    *
841          * slow, we can use string arrays.                                 *
842          *******************************************************************/
843                  
844         internal class ChannelData {
845                 internal string Ref;
846                 internal string Type;
847                 internal string Id;
848                 internal string DelayLoadAsClientChannel;
849                 
850                 ArrayList _serverProviders = new ArrayList ();
851                 ArrayList _clientProviders = new ArrayList ();
852                 Hashtable _customProperties = new Hashtable ();
853                 
854                 internal ArrayList ServerProviders {
855                         get {
856                                 if (_serverProviders == null) _serverProviders = new ArrayList ();
857                                 return _serverProviders;
858                         }
859                 }
860                 
861                 public ArrayList ClientProviders {
862                         get {
863                                 if (_clientProviders == null) _clientProviders = new ArrayList ();
864                                 return _clientProviders;
865                         }
866                 }
867                 
868                 public Hashtable CustomProperties {
869                         get {
870                                 if (_customProperties == null) _customProperties = new Hashtable ();
871                                 return _customProperties;
872                         }
873                 }
874                 
875                 public void CopyFrom (ChannelData other)
876                 {
877                         if (Ref == null) Ref = other.Ref;
878                         if (Id == null) Id = other.Id;
879                         if (Type == null) Type = other.Type;
880                         if (DelayLoadAsClientChannel == null) DelayLoadAsClientChannel = other.DelayLoadAsClientChannel;
881
882                         if (other._customProperties != null)
883                         {
884                                 foreach (DictionaryEntry entry in other._customProperties)
885                                         if (!CustomProperties.ContainsKey (entry.Key))
886                                                 CustomProperties [entry.Key] = entry.Value;
887                         }
888                         
889                         if (_serverProviders == null && other._serverProviders != null)
890                         {
891                                 foreach (ProviderData prov in other._serverProviders)
892                                 {
893                                         ProviderData np = new ProviderData();
894                                         np.CopyFrom (prov);
895                                         ServerProviders.Add (np);
896                                 }
897                         }
898                         
899                         if (_clientProviders == null && other._clientProviders != null)
900                         {
901                                 foreach (ProviderData prov in other._clientProviders)
902                                 {
903                                         ProviderData np = new ProviderData();
904                                         np.CopyFrom (prov);
905                                         ClientProviders.Add (np);
906                                 }
907                         }
908                 }
909         }
910         
911         internal class ProviderData {
912                 internal string Ref;
913                 internal string Type;
914                 internal string Id;
915                 
916                 internal Hashtable CustomProperties = new Hashtable ();
917                 internal IList CustomData;
918                 
919                 public void CopyFrom (ProviderData other)
920                 {
921                         if (Ref == null) Ref = other.Ref;
922                         if (Id == null) Id = other.Id;
923                         if (Type == null) Type = other.Type;
924                         
925                         foreach (DictionaryEntry entry in other.CustomProperties)
926                                 if (!CustomProperties.ContainsKey (entry.Key))
927                                         CustomProperties [entry.Key] = entry.Value;
928                                         
929                         if (other.CustomData != null)
930                         {
931                                 if (CustomData == null) CustomData = new ArrayList ();
932                                 foreach (SinkProviderData data in other.CustomData)
933                                         CustomData.Add (data);
934                         }
935                 }
936         }
937         
938         internal class FormatterData: ProviderData {
939         }       
940 }