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