2 // System.Runtime.Remoting.RemotingConfiguration.cs
4 // Author: Jaime Anguiano Olarra (jaime@gnome.org)
5 // Lluis Sanchez Gual (lluis@ideary.com)
7 // (C) 2002, Jaime Anguiano Olarra
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
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.
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;
42 namespace System.Runtime.Remoting
45 [System.Runtime.InteropServices.ComVisible (true)]
46 public static class RemotingConfiguration
48 public class RemotingConfiguration
53 // Private constructor: nobody instantiates this.
55 private RemotingConfiguration ()
60 static string applicationID = null;
61 static string applicationName = null;
62 static string configFile = "";
63 static SmallXmlParser parser = null;
64 static string processGuid = null;
65 static bool defaultConfigRead = false;
66 static bool defaultDelayedConfigRead = false;
67 static string _errorMode;
69 static Hashtable wellKnownClientEntries = new Hashtable();
70 static Hashtable activatedClientEntries = new Hashtable();
71 static Hashtable wellKnownServiceEntries = new Hashtable();
72 static Hashtable activatedServiceEntries = new Hashtable();
74 static Hashtable channelTemplates = new Hashtable ();
75 static Hashtable clientProviderTemplates = new Hashtable ();
76 static Hashtable serverProviderTemplates = new Hashtable ();
79 // At this time the ID will be the application name
80 public static string ApplicationId
84 applicationID = ApplicationName;
89 public static string ApplicationName
91 get { return applicationName; }
92 set { applicationName = value; }
97 public static CustomErrorsModes CustomErrorsMode
99 get { throw new NotImplementedException (); }
100 set { throw new NotImplementedException (); }
104 public static string ProcessId
107 if (processGuid == null)
108 processGuid = AppDomain.GetProcessGuid ();
118 [MonoTODO ("Implement ensureSecurity")]
123 static void Configure (string filename, bool ensureSecurity)
125 lock (channelTemplates) {
126 if (!defaultConfigRead) {
127 ReadConfigFile (Environment.GetMachineConfigPath ());
128 defaultConfigRead = true;
131 if (filename != null)
132 ReadConfigFile (filename);
136 [Obsolete ("Use Configure(String,Boolean)")]
138 public static void Configure (string filename)
140 Configure (filename, false);
143 private static void ReadConfigFile (string filename)
147 SmallXmlParser parser = new SmallXmlParser ();
148 using (TextReader rreader = new StreamReader (filename)) {
149 ConfigHandler handler = new ConfigHandler (false);
150 parser.Parse (rreader, handler);
155 throw new RemotingException ("Configuration file '" + filename + "' could not be loaded: " + ex.Message, ex);
159 internal static void LoadDefaultDelayedChannels ()
161 lock (channelTemplates)
163 if (defaultDelayedConfigRead || defaultConfigRead) return;
165 SmallXmlParser parser = new SmallXmlParser ();
166 using (TextReader rreader = new StreamReader (Environment.GetMachineConfigPath ())) {
167 ConfigHandler handler = new ConfigHandler (true);
168 parser.Parse (rreader, handler);
170 defaultDelayedConfigRead = true;
174 public static ActivatedClientTypeEntry[] GetRegisteredActivatedClientTypes ()
176 lock (channelTemplates)
178 ActivatedClientTypeEntry[] entries = new ActivatedClientTypeEntry[activatedClientEntries.Count];
179 activatedClientEntries.Values.CopyTo (entries,0);
184 public static ActivatedServiceTypeEntry[] GetRegisteredActivatedServiceTypes ()
186 lock (channelTemplates)
188 ActivatedServiceTypeEntry[] entries = new ActivatedServiceTypeEntry[activatedServiceEntries.Count];
189 activatedServiceEntries.Values.CopyTo (entries,0);
194 public static WellKnownClientTypeEntry[] GetRegisteredWellKnownClientTypes ()
196 lock (channelTemplates)
198 WellKnownClientTypeEntry[] entries = new WellKnownClientTypeEntry[wellKnownClientEntries.Count];
199 wellKnownClientEntries.Values.CopyTo (entries,0);
204 public static WellKnownServiceTypeEntry[] GetRegisteredWellKnownServiceTypes ()
206 lock (channelTemplates)
208 WellKnownServiceTypeEntry[] entries = new WellKnownServiceTypeEntry[wellKnownServiceEntries.Count];
209 wellKnownServiceEntries.Values.CopyTo (entries,0);
214 public static bool IsActivationAllowed (Type serverType)
216 lock (channelTemplates)
218 return activatedServiceEntries.ContainsKey (serverType);
222 public static ActivatedClientTypeEntry IsRemotelyActivatedClientType (Type serviceType)
224 lock (channelTemplates)
226 return activatedClientEntries [serviceType] as ActivatedClientTypeEntry;
230 public static ActivatedClientTypeEntry IsRemotelyActivatedClientType (string typeName, string assemblyName)
232 return IsRemotelyActivatedClientType (Assembly.Load(assemblyName).GetType (typeName));
235 public static WellKnownClientTypeEntry IsWellKnownClientType (Type serviceType)
237 lock (channelTemplates)
239 return wellKnownClientEntries [serviceType] as WellKnownClientTypeEntry;
243 public static WellKnownClientTypeEntry IsWellKnownClientType (string typeName, string assemblyName)
245 return IsWellKnownClientType (Assembly.Load(assemblyName).GetType (typeName));
248 public static void RegisterActivatedClientType (ActivatedClientTypeEntry entry)
250 lock (channelTemplates)
252 if (wellKnownClientEntries.ContainsKey (entry.ObjectType) || activatedClientEntries.ContainsKey (entry.ObjectType))
253 throw new RemotingException ("Attempt to redirect activation of type '" + entry.ObjectType.FullName + "' which is already redirected.");
255 activatedClientEntries[entry.ObjectType] = entry;
256 ActivationServices.EnableProxyActivation (entry.ObjectType, true);
260 public static void RegisterActivatedClientType (Type type, string appUrl)
262 if (type == null) throw new ArgumentNullException ("type");
263 if (appUrl == null) throw new ArgumentNullException ("appUrl");
265 RegisterActivatedClientType (new ActivatedClientTypeEntry (type, appUrl));
268 public static void RegisterActivatedServiceType (ActivatedServiceTypeEntry entry)
270 lock (channelTemplates)
272 activatedServiceEntries.Add (entry.ObjectType, entry);
276 public static void RegisterActivatedServiceType (Type type)
278 RegisterActivatedServiceType (new ActivatedServiceTypeEntry (type));
281 public static void RegisterWellKnownClientType (Type type, string objectUrl)
283 if (type == null) throw new ArgumentNullException ("type");
284 if (objectUrl == null) throw new ArgumentNullException ("objectUrl");
286 RegisterWellKnownClientType (new WellKnownClientTypeEntry (type, objectUrl));
289 public static void RegisterWellKnownClientType (WellKnownClientTypeEntry entry)
291 lock (channelTemplates)
293 if (wellKnownClientEntries.ContainsKey (entry.ObjectType) || activatedClientEntries.ContainsKey (entry.ObjectType))
294 throw new RemotingException ("Attempt to redirect activation of type '" + entry.ObjectType.FullName + "' which is already redirected.");
296 wellKnownClientEntries[entry.ObjectType] = entry;
297 ActivationServices.EnableProxyActivation (entry.ObjectType, true);
301 public static void RegisterWellKnownServiceType (Type type, string objectUrl, WellKnownObjectMode mode)
303 RegisterWellKnownServiceType (new WellKnownServiceTypeEntry (type, objectUrl, mode));
306 public static void RegisterWellKnownServiceType (WellKnownServiceTypeEntry entry)
308 lock (channelTemplates)
310 wellKnownServiceEntries [entry.ObjectUri] = entry;
311 RemotingServices.CreateWellKnownServerIdentity (entry.ObjectType, entry.ObjectUri, entry.Mode);
315 internal static void RegisterChannelTemplate (ChannelData channel)
317 channelTemplates [channel.Id] = channel;
320 internal static void RegisterClientProviderTemplate (ProviderData prov)
322 clientProviderTemplates [prov.Id] = prov;
325 internal static void RegisterServerProviderTemplate (ProviderData prov)
327 serverProviderTemplates [prov.Id] = prov;
330 internal static void RegisterChannels (ArrayList channels, bool onlyDelayed)
332 foreach (ChannelData channel in channels)
334 if (onlyDelayed && channel.DelayLoadAsClientChannel != "true")
337 if (defaultDelayedConfigRead && channel.DelayLoadAsClientChannel == "true")
340 if (channel.Ref != null)
342 ChannelData template = (ChannelData) channelTemplates [channel.Ref];
343 if (template == null) throw new RemotingException ("Channel template '" + channel.Ref + "' not found");
344 channel.CopyFrom (template);
347 foreach (ProviderData prov in channel.ServerProviders)
349 if (prov.Ref != null)
351 ProviderData template = (ProviderData) serverProviderTemplates [prov.Ref];
352 if (template == null) throw new RemotingException ("Provider template '" + prov.Ref + "' not found");
353 prov.CopyFrom (template);
357 foreach (ProviderData prov in channel.ClientProviders)
359 if (prov.Ref != null)
361 ProviderData template = (ProviderData) clientProviderTemplates [prov.Ref];
362 if (template == null) throw new RemotingException ("Provider template '" + prov.Ref + "' not found");
363 prov.CopyFrom (template);
367 ChannelServices.RegisterChannelConfig (channel);
371 internal static void RegisterTypes (ArrayList types)
373 foreach (TypeEntry type in types)
375 if (type is ActivatedClientTypeEntry)
376 RegisterActivatedClientType ((ActivatedClientTypeEntry)type);
377 else if (type is ActivatedServiceTypeEntry)
378 RegisterActivatedServiceType ((ActivatedServiceTypeEntry)type);
379 else if (type is WellKnownClientTypeEntry)
380 RegisterWellKnownClientType ((WellKnownClientTypeEntry)type);
381 else if (type is WellKnownServiceTypeEntry)
382 RegisterWellKnownServiceType ((WellKnownServiceTypeEntry)type);
387 public static bool CustomErrorsEnabled (bool isLocalRequest)
389 if (_errorMode == "off") return false;
390 if (_errorMode == "on") return true;
391 return !isLocalRequest;
395 internal static void SetCustomErrorsMode (string mode)
398 throw new RemotingException ("mode attribute is required");
400 // the mode is case insensitive
401 string m = mode.ToLower ();
403 if (m != "on" && m != "off" && m != "remoteonly")
404 throw new RemotingException ("Invalid custom error mode: " + mode);
410 /***************************************************************
411 * Internal classes used by RemotingConfiguration.Configure () *
412 ***************************************************************/
414 internal class ConfigHandler : SmallXmlParser.IContentHandler
416 ArrayList typeEntries = new ArrayList ();
417 ArrayList channelInstances = new ArrayList ();
419 ChannelData currentChannel = null;
420 Stack currentProviderData = null;
422 string currentClientUrl = null;
425 string currentXmlPath = "";
426 bool onlyDelayedChannels;
428 public ConfigHandler (bool onlyDelayedChannels)
430 this.onlyDelayedChannels = onlyDelayedChannels;
433 void ValidatePath (string element, params string[] paths)
435 foreach (string path in paths)
436 if (CheckPath (path)) return;
438 throw new RemotingException ("Element " + element + " not allowed in this context");
441 bool CheckPath (string path)
443 if (path.StartsWith ("/"))
444 return path == currentXmlPath;
446 return currentXmlPath.EndsWith (path);
449 public void OnStartParsing (SmallXmlParser parser) {}
451 public void OnProcessingInstruction (string name, string text) {}
453 public void OnIgnorableWhitespace (string s) {}
455 public void OnStartElement (string name, SmallXmlParser.IAttrList attrs)
459 if (currentXmlPath.StartsWith ("/configuration/system.runtime.remoting"))
460 ParseElement (name, attrs);
462 currentXmlPath += "/" + name;
466 throw new RemotingException ("Error in element " + name + ": " + ex.Message, ex);
470 public void ParseElement (string name, SmallXmlParser.IAttrList attrs)
472 if (currentProviderData != null)
474 ReadCustomProviderData (name, attrs);
481 ValidatePath (name, "system.runtime.remoting");
482 if (attrs.Names.Length > 0)
483 appName = attrs.Values[0];
487 ValidatePath (name, "application");
488 ReadLifetine (attrs);
492 ValidatePath (name, "system.runtime.remoting", "application");
496 ValidatePath (name, "channels");
497 if (currentXmlPath.IndexOf ("application") != -1)
498 ReadChannel (attrs, false);
500 ReadChannel (attrs, true);
503 case "serverProviders":
504 ValidatePath (name, "channelSinkProviders", "channel");
507 case "clientProviders":
508 ValidatePath (name, "channelSinkProviders", "channel");
515 if (CheckPath ("application/channels/channel/serverProviders") ||
516 CheckPath ("channels/channel/serverProviders"))
518 prov = ReadProvider (name, attrs, false);
519 currentChannel.ServerProviders.Add (prov);
521 else if (CheckPath ("application/channels/channel/clientProviders") ||
522 CheckPath ("channels/channel/clientProviders"))
524 prov = ReadProvider (name, attrs, false);
525 currentChannel.ClientProviders.Add (prov);
527 else if (CheckPath ("channelSinkProviders/serverProviders"))
529 prov = ReadProvider (name, attrs, true);
530 RemotingConfiguration.RegisterServerProviderTemplate (prov);
532 else if (CheckPath ("channelSinkProviders/clientProviders"))
534 prov = ReadProvider (name, attrs, true);
535 RemotingConfiguration.RegisterClientProviderTemplate (prov);
542 ValidatePath (name, "application");
543 currentClientUrl = attrs.GetValue ("url");
547 ValidatePath (name, "application");
551 ValidatePath (name, "client", "service");
552 if (CheckPath ("client"))
553 ReadClientWellKnown (attrs);
555 ReadServiceWellKnown (attrs);
559 ValidatePath (name, "client", "service");
560 if (CheckPath ("client"))
561 ReadClientActivated (attrs);
563 ReadServiceActivated (attrs);
567 ValidatePath (name, "application");
570 case "interopXmlType":
571 ValidatePath (name, "soapInterop");
572 ReadInteropXml (attrs, false);
575 case "interopXmlElement":
576 ValidatePath (name, "soapInterop");
577 ReadInteropXml (attrs, false);
581 ValidatePath (name, "soapInterop");
586 ValidatePath (name, "system.runtime.remoting");
589 case "channelSinkProviders":
590 ValidatePath (name, "system.runtime.remoting");
594 ValidatePath (name, "system.runtime.remoting");
595 RemotingConfiguration.SetCustomErrorsMode (attrs.GetValue ("mode"));
599 throw new RemotingException ("Element '" + name + "' is not valid in system.remoting.configuration section");
603 public void OnEndElement (string name)
605 if (currentProviderData != null)
607 currentProviderData.Pop ();
608 if (currentProviderData.Count == 0)
609 currentProviderData = null;
612 currentXmlPath = currentXmlPath.Substring (0, currentXmlPath.Length - name.Length - 1);
615 void ReadCustomProviderData (string name, SmallXmlParser.IAttrList attrs)
617 SinkProviderData parent = (SinkProviderData) currentProviderData.Peek ();
619 SinkProviderData data = new SinkProviderData (name);
620 for (int i=0; i < attrs.Names.Length; ++i)
621 data.Properties [attrs.Names[i]] = attrs.GetValue (i);
623 parent.Children.Add (data);
624 currentProviderData.Push (data);
627 void ReadLifetine (SmallXmlParser.IAttrList attrs)
629 for (int i=0; i < attrs.Names.Length; ++i) {
630 switch (attrs.Names[i]) {
632 LifetimeServices.LeaseTime = ParseTime (attrs.GetValue(i));
634 case "sponsorshipTimeout":
635 LifetimeServices.SponsorshipTimeout = ParseTime (attrs.GetValue(i));
637 case "renewOnCallTime":
638 LifetimeServices.RenewOnCallTime = ParseTime (attrs.GetValue(i));
640 case "leaseManagerPollTime":
641 LifetimeServices.LeaseManagerPollTime = ParseTime (attrs.GetValue(i));
644 throw new RemotingException ("Invalid attribute: " + attrs.Names[i]);
649 TimeSpan ParseTime (string s)
651 if (s == "" || s == null) throw new RemotingException ("Invalid time value");
653 int i = s.IndexOfAny (new char[] { 'D','H','M','S' });
659 unit = s.Substring (i);
660 s = s.Substring (0,i);
665 val = double.Parse (s);
668 throw new RemotingException ("Invalid time value: " + s);
671 if (unit == "D") return TimeSpan.FromDays (val);
672 if (unit == "H") return TimeSpan.FromHours (val);
673 if (unit == "M") return TimeSpan.FromMinutes (val);
674 if (unit == "S") return TimeSpan.FromSeconds (val);
675 if (unit == "MS") return TimeSpan.FromMilliseconds (val);
676 throw new RemotingException ("Invalid time unit: " + unit);
679 void ReadChannel (SmallXmlParser.IAttrList attrs, bool isTemplate)
681 ChannelData channel = new ChannelData ();
683 for (int i=0; i < attrs.Names.Length; ++i)
685 string at = attrs.Names[i];
686 string val = attrs.Values[i];
688 if (at == "ref" && !isTemplate)
690 else if (at == "delayLoadAsClientChannel")
691 channel.DelayLoadAsClientChannel = val;
692 else if (at == "id" && isTemplate)
694 else if (at == "type")
697 channel.CustomProperties.Add (at, val);
702 if (channel.Id == null) throw new RemotingException ("id attribute is required");
703 if (channel.Type == null) throw new RemotingException ("id attribute is required");
704 RemotingConfiguration.RegisterChannelTemplate (channel);
707 channelInstances.Add (channel);
709 currentChannel = channel;
712 ProviderData ReadProvider (string name, SmallXmlParser.IAttrList attrs, bool isTemplate)
714 ProviderData prov = (name == "provider") ? new ProviderData () : new FormatterData ();
715 SinkProviderData data = new SinkProviderData ("root");
716 prov.CustomData = data.Children;
718 currentProviderData = new Stack ();
719 currentProviderData.Push (data);
721 for (int i=0; i < attrs.Names.Length; ++i)
723 string at = attrs.Names[i];
724 string val = attrs.Values[i];
726 if (at == "id" && isTemplate)
728 else if (at == "type")
730 else if (at == "ref" && !isTemplate)
733 prov.CustomProperties.Add (at, val);
736 if (prov.Id == null && isTemplate) throw new RemotingException ("id attribute is required");
740 void ReadClientActivated (SmallXmlParser.IAttrList attrs)
742 string type = GetNotNull (attrs, "type");
743 string assm = ExtractAssembly (ref type);
745 if (currentClientUrl == null || currentClientUrl == "")
746 throw new RemotingException ("url attribute is required in client element when it contains activated entries");
748 typeEntries.Add (new ActivatedClientTypeEntry (type, assm, currentClientUrl));
751 void ReadServiceActivated (SmallXmlParser.IAttrList attrs)
753 string type = GetNotNull (attrs, "type");
754 string assm = ExtractAssembly (ref type);
756 typeEntries.Add (new ActivatedServiceTypeEntry (type, assm));
759 void ReadClientWellKnown (SmallXmlParser.IAttrList attrs)
761 string url = GetNotNull (attrs, "url");
762 string type = GetNotNull (attrs, "type");
763 string assm = ExtractAssembly (ref type);
765 typeEntries.Add (new WellKnownClientTypeEntry (type, assm, url));
768 void ReadServiceWellKnown (SmallXmlParser.IAttrList attrs)
770 string objectUri = GetNotNull (attrs, "objectUri");
771 string smode = GetNotNull (attrs, "mode");
772 string type = GetNotNull (attrs, "type");
773 string assm = ExtractAssembly (ref type);
775 WellKnownObjectMode mode;
776 if (smode == "SingleCall") mode = WellKnownObjectMode.SingleCall;
777 else if (smode == "Singleton") mode = WellKnownObjectMode.Singleton;
778 else throw new RemotingException ("wellknown object mode '" + smode + "' is invalid");
780 typeEntries.Add (new WellKnownServiceTypeEntry (type, assm, objectUri, mode));
783 void ReadInteropXml (SmallXmlParser.IAttrList attrs, bool isElement)
785 Type t = Type.GetType (GetNotNull (attrs, "clr"));
786 string[] xmlName = GetNotNull (attrs, "xml").Split (',');
787 string localName = xmlName [0].Trim ();
788 string ns = xmlName.Length > 0 ? xmlName[1].Trim() : null;
790 if (isElement) SoapServices.RegisterInteropXmlElement (localName, ns, t);
791 else SoapServices.RegisterInteropXmlType (localName, ns, t);
794 void ReadPreload (SmallXmlParser.IAttrList attrs)
796 string type = attrs.GetValue ("type");
797 string assm = attrs.GetValue ("assembly");
799 if (type != null && assm != null)
800 throw new RemotingException ("Type and assembly attributes cannot be specified together");
803 SoapServices.PreLoad (Type.GetType (type));
804 else if (assm != null)
805 SoapServices.PreLoad (Assembly.Load (assm));
807 throw new RemotingException ("Either type or assembly attributes must be specified");
810 string GetNotNull (SmallXmlParser.IAttrList attrs, string name)
812 string value = attrs.GetValue (name);
813 if (value == null || value == "")
814 throw new RemotingException (name + " attribute is required");
818 string ExtractAssembly (ref string type)
820 int i = type.IndexOf (',');
821 if (i == -1) return "";
823 string asm = type.Substring (i+1).Trim();
824 type = type.Substring (0, i).Trim();
828 public void OnChars (string ch) {}
830 public void OnEndParsing (SmallXmlParser parser)
832 RemotingConfiguration.RegisterChannels (channelInstances, onlyDelayedChannels);
833 if (appName != null) RemotingConfiguration.ApplicationName = appName;
835 if (!onlyDelayedChannels)
836 RemotingConfiguration.RegisterTypes (typeEntries);
841 /*******************************************************************
842 * Internal data structures used by ConfigHandler, to store *
843 * machine.config's remoting related data. *
844 * If having them implemented this way, makes configuration too *
845 * slow, we can use string arrays. *
846 *******************************************************************/
848 internal class ChannelData {
850 internal string Type;
852 internal string DelayLoadAsClientChannel;
854 ArrayList _serverProviders = new ArrayList ();
855 ArrayList _clientProviders = new ArrayList ();
856 Hashtable _customProperties = new Hashtable ();
858 internal ArrayList ServerProviders {
860 if (_serverProviders == null) _serverProviders = new ArrayList ();
861 return _serverProviders;
865 public ArrayList ClientProviders {
867 if (_clientProviders == null) _clientProviders = new ArrayList ();
868 return _clientProviders;
872 public Hashtable CustomProperties {
874 if (_customProperties == null) _customProperties = new Hashtable ();
875 return _customProperties;
879 public void CopyFrom (ChannelData other)
881 if (Ref == null) Ref = other.Ref;
882 if (Id == null) Id = other.Id;
883 if (Type == null) Type = other.Type;
884 if (DelayLoadAsClientChannel == null) DelayLoadAsClientChannel = other.DelayLoadAsClientChannel;
886 if (other._customProperties != null)
888 foreach (DictionaryEntry entry in other._customProperties)
889 if (!CustomProperties.ContainsKey (entry.Key))
890 CustomProperties [entry.Key] = entry.Value;
893 if (_serverProviders == null && other._serverProviders != null)
895 foreach (ProviderData prov in other._serverProviders)
897 ProviderData np = new ProviderData();
899 ServerProviders.Add (np);
903 if (_clientProviders == null && other._clientProviders != null)
905 foreach (ProviderData prov in other._clientProviders)
907 ProviderData np = new ProviderData();
909 ClientProviders.Add (np);
915 internal class ProviderData {
917 internal string Type;
920 internal Hashtable CustomProperties = new Hashtable ();
921 internal IList CustomData;
923 public void CopyFrom (ProviderData other)
925 if (Ref == null) Ref = other.Ref;
926 if (Id == null) Id = other.Id;
927 if (Type == null) Type = other.Type;
929 foreach (DictionaryEntry entry in other.CustomProperties)
930 if (!CustomProperties.ContainsKey (entry.Key))
931 CustomProperties [entry.Key] = entry.Value;
933 if (other.CustomData != null)
935 if (CustomData == null) CustomData = new ArrayList ();
936 foreach (SinkProviderData data in other.CustomData)
937 CustomData.Add (data);
942 internal class FormatterData: ProviderData {