1 //
2 // System.Diagnostics.DiagnosticsConfigurationHandler.cs
3 //
4 // Comments from John R. Hicks <angryjohn69@nc.rr.com> original implementation
5 // can be found at: /mcs/docs/apidocs/xml/en/System.Diagnostics
6 //
7 // Authors:
8 //	John R. Hicks <angryjohn69@nc.rr.com>
9 //	Jonathan Pryor <jonpryor@vt.edu>
10 //
11 // (C) 2002, 2005
12 //
13 
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 //
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 //
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34 using System;
35 using System.Collections;
36 using System.Collections.Specialized;
37 using System.Configuration;
38 using System.Reflection;
39 using System.Threading;
40 #if (XML_DEP)
41 using System.Xml;
42 #endif
43 namespace System.Diagnostics
44 {
45 /*
46 	// It handles following elements in <system.diagnostics> :
47 	//	- <sharedListeners> [2.0]
48 	//	- <sources>
49 	//		- <source>
50 	//			- <listeners> (collection)
51 	//	- <switches>
52 	//		- <add name=string value=string />
53 	//	- <trace autoflush=bool indentsize=int useGlobalLock=bool>
54 	//		- <listeners>
55 	internal sealed class DiagnosticsConfiguration
56 	{
57 #if NO_LOCK_FREE
58 		private static object lock_ = new object();
59 #endif
60 		private static object settings;
61 
62 		public static IDictionary Settings {
63 			get {
64 #if !NO_LOCK_FREE
65 				if (settings == null) {
66 					object s = ConfigurationSettings.GetConfig ("system.diagnostics");
67 					if (s == null)
68 						throw new Exception ("INTERNAL configuration error: failed to get configuration 'system.diagnostics'");
69 					Thread.MemoryBarrier ();
70 					while (Interlocked.CompareExchange (ref settings, s, null) == null) {
71 						// do nothing; we're just setting settings.
72 					}
73 					Thread.MemoryBarrier ();
74 				}
75 #else
76 				lock (lock_) {
77 					if (settings == null)
78 						settings = ConfigurationSettings.GetConfig ("system.diagnostics");
79 				}
80 #endif
81 				return (IDictionary) settings;
82 			}
83 		}
84 	}
85 */
86 
87 #if (XML_DEP)
88 	[Obsolete ("This class is obsoleted")]
89 	public class DiagnosticsConfigurationHandler : IConfigurationSectionHandler
90 	{
91 		TraceImplSettings configValues;
92 
ElementHandler(IDictionary d, XmlNode node)93 		delegate void ElementHandler (IDictionary d, XmlNode node);
94 
95 		IDictionary elementHandlers = new Hashtable ();
96 
DiagnosticsConfigurationHandler()97 		public DiagnosticsConfigurationHandler ()
98 		{
99 			elementHandlers ["assert"] = new ElementHandler (AddAssertNode);
100 			elementHandlers ["performanceCounters"] = new ElementHandler (AddPerformanceCountersNode);
101 			elementHandlers ["switches"] = new ElementHandler (AddSwitchesNode);
102 			elementHandlers ["trace"] = new ElementHandler (AddTraceNode);
103 			elementHandlers ["sources"] = new ElementHandler (AddSourcesNode);
104 		}
105 
Create(object parent, object configContext, XmlNode section)106 		public virtual object Create (object parent, object configContext, XmlNode section)
107 		{
108 			IDictionary d;
109 			if (parent == null)
110 				d = new Hashtable (CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default);
111 			else
112 				d = (IDictionary) ((ICloneable)parent).Clone();
113 
114 			if (d.Contains (TraceImplSettings.Key))
115 				configValues = (TraceImplSettings) d [TraceImplSettings.Key];
116 			else
117 				d.Add (TraceImplSettings.Key, configValues = new TraceImplSettings ());
118 
119 			// process <sharedListeners> first
120 			foreach (XmlNode child in section.ChildNodes) {
121 				switch (child.NodeType) {
122 				case XmlNodeType.Element:
123 					if (child.LocalName != "sharedListeners")
124 						continue;
125 					AddTraceListeners (d, child, GetSharedListeners (d));
126 					break;
127 				}
128 			}
129 
130 			foreach (XmlNode child in section.ChildNodes) {
131 				XmlNodeType type = child.NodeType;
132 
133 				switch (type) {
134 				/* ignore */
135 				case XmlNodeType.Whitespace:
136 				case XmlNodeType.Comment:
137 					continue;
138 				case XmlNodeType.Element:
139 					if (child.LocalName == "sharedListeners")
140 						continue;
141 					ElementHandler eh = (ElementHandler) elementHandlers [child.Name];
142 					if (eh != null)
143 						eh (d, child);
144 					else
145 						ThrowUnrecognizedElement (child);
146 					break;
147 				default:
148 					ThrowUnrecognizedElement (child);
149 					break;
150 				}
151 			}
152 
153 			return d;
154 		}
155 
156 		// Remarks: Both attribute are optional
AddAssertNode(IDictionary d, XmlNode node)157 		private void AddAssertNode (IDictionary d, XmlNode node)
158 		{
159 			XmlAttributeCollection c = node.Attributes;
160 			string assertuienabled = GetAttribute (c, "assertuienabled", false, node);
161 			string logfilename = GetAttribute (c, "logfilename", false, node);
162 			ValidateInvalidAttributes (c, node);
163 			if (assertuienabled != null) {
164 				try {
165 					d ["assertuienabled"] = bool.Parse (assertuienabled);
166 				}
167 				catch (Exception e) {
168 					throw new ConfigurationException ("The `assertuienabled' attribute must be `true' or `false'",
169 							e, node);
170 				}
171 			}
172 
173 			if (logfilename != null)
174 				d ["logfilename"] = logfilename;
175 
176 			DefaultTraceListener dtl = (DefaultTraceListener) configValues.Listeners["Default"];
177 			if (dtl != null) {
178 				if (assertuienabled != null)
179 					dtl.AssertUiEnabled = (bool) d ["assertuienabled"];
180 				if (logfilename != null)
181 					dtl.LogFileName = logfilename;
182 			}
183 
184 			if (node.ChildNodes.Count > 0)
185 				ThrowUnrecognizedElement (node.ChildNodes[0]);
186 		}
187 
AddPerformanceCountersNode(IDictionary d, XmlNode node)188 		private void AddPerformanceCountersNode (IDictionary d, XmlNode node)
189 		{
190 			XmlAttributeCollection c = node.Attributes;
191 			string filemappingsize = GetAttribute (c, "filemappingsize", false, node);
192 			ValidateInvalidAttributes (c, node);
193 			if (filemappingsize != null) {
194 				try {
195 					d ["filemappingsize"] = int.Parse (filemappingsize);
196 				}
197 				catch (Exception e) {
198 					throw new ConfigurationException ("The `filemappingsize' attribute must be an integral value.",
199 							e, node);
200 				}
201 			}
202 
203 			if (node.ChildNodes.Count > 0)
204 				ThrowUnrecognizedElement (node.ChildNodes[0]);
205 		}
206 
207 		// name and value attributes are required
208 		// Docs do not define "remove" or "clear" elements, but .NET recognizes
209 		// them
AddSwitchesNode(IDictionary d, XmlNode node)210 		private void AddSwitchesNode (IDictionary d, XmlNode node)
211 		{
212 			// There are no attributes on <switch/>
213 			ValidateInvalidAttributes (node.Attributes, node);
214 
215 			IDictionary newNodes = new Hashtable ();
216 
217 			foreach (XmlNode child in node.ChildNodes) {
218 				XmlNodeType t = child.NodeType;
219 				if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
220 					continue;
221 				if (t == XmlNodeType.Element) {
222 					XmlAttributeCollection attributes = child.Attributes;
223 					string name = null;
224 					string value = null;
225 					switch (child.Name) {
226 						case "add":
227 							name = GetAttribute (attributes, "name", true, child);
228 							value = GetAttribute (attributes, "value", true, child);
229 							newNodes [name] = GetSwitchValue (name, value);
230 							break;
231 						case "remove":
232 							name = GetAttribute (attributes, "name", true, child);
233 							newNodes.Remove (name);
234 							break;
235 						case "clear":
236 							newNodes.Clear ();
237 							break;
238 						default:
239 							ThrowUnrecognizedElement (child);
240 							break;
241 					}
242 					ValidateInvalidAttributes (attributes, child);
243 				}
244 				else
245 					ThrowUnrecognizedNode (child);
246 			}
247 
248 			d [node.Name] = newNodes;
249 		}
250 
GetSwitchValue(string name, string value)251 		private static object GetSwitchValue (string name, string value)
252 		{
253 			return value;
254 		}
255 
AddTraceNode(IDictionary d, XmlNode node)256 		private void AddTraceNode (IDictionary d, XmlNode node)
257 		{
258 			AddTraceAttributes (d, node);
259 
260 			foreach (XmlNode child in node.ChildNodes) {
261 				XmlNodeType t = child.NodeType;
262 				if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
263 					continue;
264 				if (t == XmlNodeType.Element) {
265 					if (child.Name == "listeners")
266 						AddTraceListeners (d, child, configValues.Listeners);
267 					else
268 						ThrowUnrecognizedElement (child);
269 					ValidateInvalidAttributes (child.Attributes, child);
270 				}
271 				else
272 					ThrowUnrecognizedNode (child);
273 			}
274 		}
275 
276 		// all attributes are optional
AddTraceAttributes(IDictionary d, XmlNode node)277 		private void AddTraceAttributes (IDictionary d, XmlNode node)
278 		{
279 			XmlAttributeCollection c = node.Attributes;
280 			string autoflushConf = GetAttribute (c, "autoflush", false, node);
281 			string indentsizeConf = GetAttribute (c, "indentsize", false, node);
282 			ValidateInvalidAttributes (c, node);
283 			if (autoflushConf != null) {
284 				bool autoflush = false;
285 				try {
286 					autoflush = bool.Parse (autoflushConf);
287 					d ["autoflush"] = autoflush;
288 				} catch (Exception e) {
289 					throw new ConfigurationException ("The `autoflush' attribute must be `true' or `false'",
290 							e, node);
291 				}
292 				configValues.AutoFlush = autoflush;
293 			}
294 			if (indentsizeConf != null) {
295 				int indentsize = 0;
296 				try {
297 					indentsize = int.Parse (indentsizeConf);
298 					d ["indentsize"] = indentsize;
299 				} catch (Exception e) {
300 					throw new ConfigurationException ("The `indentsize' attribute must be an integral value.",
301 							e, node);
302 				}
303 				configValues.IndentSize = indentsize;
304 			}
305 		}
306 
GetSharedListeners(IDictionary d)307 		private TraceListenerCollection GetSharedListeners (IDictionary d)
308 		{
309 			TraceListenerCollection shared_listeners = d ["sharedListeners"] as TraceListenerCollection;
310 			if (shared_listeners == null) {
311 				shared_listeners = new TraceListenerCollection ();
312 				d ["sharedListeners"] = shared_listeners;
313 			}
314 			return shared_listeners;
315 		}
316 
AddSourcesNode(IDictionary d, XmlNode node)317 		private void AddSourcesNode (IDictionary d, XmlNode node)
318 		{
319 			// FIXME: are there valid attributes?
320 			ValidateInvalidAttributes (node.Attributes, node);
321 			Hashtable sources = d ["sources"] as Hashtable;
322 			if (sources == null) {
323 				sources = new Hashtable ();
324 				d ["sources"] = sources;
325 			}
326 
327 			foreach (XmlNode child in node.ChildNodes) {
328 				XmlNodeType t = child.NodeType;
329 				if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
330 					continue;
331 				if (t == XmlNodeType.Element) {
332 					if (child.Name == "source")
333 						AddTraceSource (d, sources, child);
334 					else
335 						ThrowUnrecognizedElement (child);
336 //					ValidateInvalidAttributes (child.Attributes, child);
337 				}
338 				else
339 					ThrowUnrecognizedNode (child);
340 			}
341 		}
342 
AddTraceSource(IDictionary d, Hashtable sources, XmlNode node)343 		private void AddTraceSource (IDictionary d, Hashtable sources, XmlNode node)
344 		{
345 			string name = null;
346 			SourceLevels levels = SourceLevels.Error;
347 			StringDictionary atts = new StringDictionary ();
348 			foreach (XmlAttribute a in node.Attributes) {
349 				switch (a.Name) {
350 				case "name":
351 					name = a.Value;
352 					break;
353 				case "switchValue":
354 					levels = (SourceLevels) Enum.Parse (typeof (SourceLevels), a.Value);
355 					break;
356 				default:
357 					atts [a.Name] = a.Value;
358 					break;
359 				}
360 			}
361 			if (name == null)
362 				throw new ConfigurationException ("Mandatory attribute 'name' is missing in 'source' element.");
363 
364 			// ignore duplicate ones (no error occurs)
365 			if (sources.ContainsKey (name))
366 				return;
367 
368 			TraceSourceInfo sinfo = new TraceSourceInfo (name, levels, configValues);
369 			sources.Add (sinfo.Name, sinfo);
370 
371 			foreach (XmlNode child in node.ChildNodes) {
372 				XmlNodeType t = child.NodeType;
373 				if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
374 					continue;
375 				if (t == XmlNodeType.Element) {
376 					if (child.Name == "listeners")
377 						AddTraceListeners (d, child, sinfo.Listeners);
378 					else
379 						ThrowUnrecognizedElement (child);
380 					ValidateInvalidAttributes (child.Attributes, child);
381 				}
382 				else
383 					ThrowUnrecognizedNode (child);
384 			}
385 		}
386 
387 		// only defines "add" and "remove", but "clear" also works
388 		// for add, "name" is required; initializeData is optional; "type" is required in 1.x, optional in 2.0.
AddTraceListeners(IDictionary d, XmlNode listenersNode, TraceListenerCollection listeners)389 		private void AddTraceListeners (IDictionary d, XmlNode listenersNode, TraceListenerCollection listeners)
390 		{
391 			// There are no attributes on <listeners/>
392 			ValidateInvalidAttributes (listenersNode.Attributes, listenersNode);
393 
394 			foreach (XmlNode child in listenersNode.ChildNodes) {
395 				XmlNodeType t = child.NodeType;
396 				if (t == XmlNodeType.Whitespace || t == XmlNodeType.Comment)
397 					continue;
398 				if (t == XmlNodeType.Element) {
399 					XmlAttributeCollection attributes = child.Attributes;
400 					string name = null;
401 					switch (child.Name) {
402 						case "add":
403 							AddTraceListener (d, child, attributes, listeners);
404 							break;
405 						case "remove":
406 							name = GetAttribute (attributes, "name", true, child);
407 							RemoveTraceListener (name);
408 							break;
409 						case "clear":
410 							configValues.Listeners.Clear ();
411 							break;
412 						default:
413 							ThrowUnrecognizedElement (child);
414 							break;
415 					}
416 					ValidateInvalidAttributes (attributes, child);
417 				}
418 				else
419 					ThrowUnrecognizedNode (child);
420 			}
421 		}
422 
AddTraceListener(IDictionary d, XmlNode child, XmlAttributeCollection attributes, TraceListenerCollection listeners)423 		private void AddTraceListener (IDictionary d, XmlNode child, XmlAttributeCollection attributes, TraceListenerCollection listeners)
424 		{
425 			string name = GetAttribute (attributes, "name", true, child);
426 			string type = null;
427 
428 #if CONFIGURATION_DEP
429 			type = GetAttribute (attributes, "type", false, child);
430 			if (type == null) {
431 				// indicated by name.
432 				TraceListener shared = GetSharedListeners (d) [name];
433 				if (shared == null)
434 					throw new ConfigurationException (String.Format ("Shared trace listener {0} does not exist.", name));
435 				if (attributes.Count != 0)
436 					throw new ConfigurationErrorsException (string.Format (
437 						"Listener '{0}' references a shared " +
438 						"listener and can only have a 'Name' " +
439 						"attribute.", name));
440 				shared.IndentSize = configValues.IndentSize;
441 				listeners.Add (shared);
442 				return;
443 			}
444 #else
445 			type = GetAttribute (attributes, "type", true, child);
446 #endif
447 
448 			Type t = Type.GetType (type);
449 			if (t == null)
450 				throw new ConfigurationException (string.Format ("Invalid Type Specified: {0}", type));
451 
452 			object[] args;
453 			Type[] types;
454 
455 			string initializeData = GetAttribute (attributes, "initializeData", false, child);
456 			if (initializeData != null) {
457 				args = new object[] { initializeData };
458 				types = new Type[] { typeof(string) };
459 			} else {
460 				args = null;
461 				types = Type.EmptyTypes;
462 			}
463 
464 			BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
465 			if (t.Assembly == GetType ().Assembly)
466 				flags |= BindingFlags.NonPublic;
467 
468 			ConstructorInfo ctor = t.GetConstructor (flags, null, types, null);
469 			if (ctor == null)
470 				throw new ConfigurationException ("Couldn't find constructor for class " + type);
471 
472 			TraceListener l = (TraceListener) ctor.Invoke (args);
473 			l.Name = name;
474 
475 #if CONFIGURATION_DEP
476 			string trace = GetAttribute (attributes, "traceOutputOptions", false, child);
477 			if (trace != null) {
478 				if (trace != trace.Trim ())
479 					throw new ConfigurationErrorsException (string.Format (
480 						"Invalid value '{0}' for 'traceOutputOptions'.",
481 						trace), child);
482 
483 				TraceOptions trace_options;
484 
485 				try {
486 					trace_options = (TraceOptions) Enum.Parse (
487 						typeof (TraceOptions), trace);
488 				} catch (ArgumentException) {
489 					throw new ConfigurationErrorsException (string.Format (
490 						"Invalid value '{0}' for 'traceOutputOptions'.",
491 						trace), child);
492 				}
493 
494 				l.TraceOutputOptions = trace_options;
495 			}
496 
497 			string [] supported_attributes = l.GetSupportedAttributes ();
498 			if (supported_attributes != null) {
499 				for (int i = 0; i < supported_attributes.Length; i++) {
500 					string key = supported_attributes [i];
501 					string value = GetAttribute (attributes, key, false, child);
502 					if (value != null)
503 						l.Attributes.Add (key, value);
504 				}
505 			}
506 #endif
507 
508 			l.IndentSize = configValues.IndentSize;
509 			listeners.Add (l);
510 		}
511 
RemoveTraceListener(string name)512 		private void RemoveTraceListener (string name)
513 		{
514 			try {
515 				configValues.Listeners.Remove (name);
516 			}
517 			catch (ArgumentException) {
518 				// The specified listener wasn't in the collection
519 				// Ignore this; .NET does.
520 			}
521 			catch (Exception e) {
522 				throw new ConfigurationException (
523 						string.Format ("Unknown error removing listener: {0}", name),
524 						e);
525 			}
526 		}
527 
GetAttribute(XmlAttributeCollection attrs, string attr, bool required, XmlNode node)528 		private string GetAttribute (XmlAttributeCollection attrs, string attr, bool required, XmlNode node)
529 		{
530 			XmlAttribute a = attrs[attr];
531 
532 			string r = null;
533 
534 			if (a != null) {
535 				r = a.Value;
536 				if (required)
537 					ValidateAttribute (attr, r, node);
538 				attrs.Remove (a);
539 			}
540 			else if (required)
541 				ThrowMissingAttribute (attr, node);
542 
543 			return r;
544 		}
545 
ValidateAttribute(string attribute, string value, XmlNode node)546 		private void ValidateAttribute (string attribute, string value, XmlNode node)
547 		{
548 			if (value == null || value.Length == 0)
549 				throw new ConfigurationException (string.Format ("Required attribute '{0}' cannot be empty.", attribute), node);
550 		}
551 
ValidateInvalidAttributes(XmlAttributeCollection c, XmlNode node)552 		private void ValidateInvalidAttributes (XmlAttributeCollection c, XmlNode node)
553 		{
554 			if (c.Count != 0)
555 				ThrowUnrecognizedAttribute (c[0].Name, node);
556 		}
557 
ThrowMissingAttribute(string attribute, XmlNode node)558 		private void ThrowMissingAttribute (string attribute, XmlNode node)
559 		{
560 			throw new ConfigurationException (string.Format ("Required attribute '{0}' not found.", attribute), node);
561 		}
562 
ThrowUnrecognizedNode(XmlNode node)563 		private void ThrowUnrecognizedNode (XmlNode node)
564 		{
565 			throw new ConfigurationException (
566 					string.Format ("Unrecognized node `{0}'; nodeType={1}", node.Name, node.NodeType),
567 					node);
568 		}
569 
ThrowUnrecognizedElement(XmlNode node)570 		private void ThrowUnrecognizedElement (XmlNode node)
571 		{
572 			throw new ConfigurationException (
573 					string.Format ("Unrecognized element '{0}'.", node.Name),
574 					node);
575 		}
576 
ThrowUnrecognizedAttribute(string attribute, XmlNode node)577 		private void ThrowUnrecognizedAttribute (string attribute, XmlNode node)
578 		{
579 			throw new ConfigurationException (
580 					string.Format ("Unrecognized attribute '{0}' on element <{1}/>.", attribute, node.Name),
581 					node);
582 		}
583 	}
584 #endif
585 }
586 
587