The dark side of the Cloud; network connectivity issues and service unavailability - AKA Transient Faults
In these days where everything is moving to "the cloud", new challenges arises that most of us where spared from before; temporary condition such as network connectivity issues or simply a service unavailability. The technical term for this is called Transient Fault Handling and one you must be conversant with.
On MSDN there is a very informative and good background information for Transient Fault Handling which i strongly encourage you to read. While you are there, you could consider if the comprehensive work of the Enterprise Library from version 5.0 suits your needs in what has been named the Transient Fault Handling Application Block.
If it seems a big over the top for your solution, you could consider the static TransientFaultUtility class found in the Cuemon namespace; it is fully compatible with cloud provides such as Windows Azure. It has several overloads for invoking a fault sensitive method, and will continue until the operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to a transient fault condition.
The minimum required parameters for invoking a transient fault protected method is an integer specifying retryAttempts, a function delegate that will determine if isTransientFault and last but not least; a function delegate/action delegate pointing to the faultSensitiveMethod.
To see this in action, have a look at Figure 1. What we do here is simply throwing an HttpException should we encounter a HTTP 502. This can easily be extended to the ones listed in the IsTransientFault callback method. Otherwise we just write some debug information. Figure 2 shows how to consume the TransientFaultExample class and is intentionally set to fail in the first run. Figure 3 shows the Debug Trace.
Figure 1: A simple TransientFaultExample class that can easily be rewritten to more real-life scenarios
Figure 2: Consumes the class defined in Figure 1
If you don't like the default recovery wait time this can easily be added using one of the overloads on the TransientFaultUtility class. For your convenience, I have included the default implementation in Figure 4.
Figure 4: The default implementation of the function delegate recoveryWaitTime
I hope this little introduction have inspired you to master Transient Fault Handling one way or another. Happy coding.
On MSDN there is a very informative and good background information for Transient Fault Handling which i strongly encourage you to read. While you are there, you could consider if the comprehensive work of the Enterprise Library from version 5.0 suits your needs in what has been named the Transient Fault Handling Application Block.
If it seems a big over the top for your solution, you could consider the static TransientFaultUtility class found in the Cuemon namespace; it is fully compatible with cloud provides such as Windows Azure. It has several overloads for invoking a fault sensitive method, and will continue until the operation is successful, the amount of retry attempts has been reached, or a failed operation is not considered related to a transient fault condition.
The minimum required parameters for invoking a transient fault protected method is an integer specifying retryAttempts, a function delegate that will determine if isTransientFault and last but not least; a function delegate/action delegate pointing to the faultSensitiveMethod.
To see this in action, have a look at Figure 1. What we do here is simply throwing an HttpException should we encounter a HTTP 502. This can easily be extended to the ones listed in the IsTransientFault callback method. Otherwise we just write some debug information. Figure 2 shows how to consume the TransientFaultExample class and is intentionally set to fail in the first run. Figure 3 shows the Debug Trace.
public class TransientFaultExample
{
public TransientFaultExample()
{
NetHttpUtility.DefaultHttpTimeout = TimeSpan.FromSeconds(15);
}
public void OpenWebsite(Uri location)
{
using (HttpWebResponse response = NetHttpUtility.HttpGet(location))
{
if (response.StatusCode == HttpStatusCode.BadGateway) { throw new HttpException(502, response.StatusDescription); }
Debug.WriteLine("Status code in response was {0} - {1}.", (int)response.StatusCode, response.StatusDescription);
Debug.WriteLine("The headers of the response was {0}.", ConvertUtility.ToDelimitedString(response.Headers.AllKeys, ", ", HeaderConverter, response.Headers) as object);
}
}
private string HeaderConverter(string header, WebHeaderCollection headers)
{
return string.Format("{0}: {1}", header, headers[header] ?? "null");
}
public bool IsTransientFault(Exception exception)
{
HttpException httpException = exception as HttpException;
if (httpException != null)
{
switch (httpException.GetHttpCode())
{
case 404:
case 408:
case 410:
case 500:
case 502:
case 503:
case 504:
return true;
default:
return false;
}
}
return (exception.Message.IndexOf("timed out", StringComparison.OrdinalIgnoreCase) >= 0);
}
}
[TestClass]
public class TransientFaultExampleTest
{
[TestMethod]
public void TransientFault()
{
TransientFaultExample transient = new TransientFaultExample();
try
{
TransientFaultUtility.ExecuteAction(5, transient.IsTransientFault, transient.OpenWebsite, new Uri("http://www.google.com:88/"));
}
catch (TransientFaultException ex)
{
Debug.WriteLine("TransientFaultException was thrown (which is good): {0}", ConvertUtility.ToString(ex, Encoding.Default, true) as object);
}
try
{
TransientFaultUtility.ExecuteAction(5, transient.IsTransientFault, transient.OpenWebsite, new Uri("http://www.google.com/"));
}
catch (TransientFaultException ex)
{
Debug.WriteLine("TransientFaultException was thrown (which is not so good - for Google at least): {0}", ConvertUtility.ToString(ex, Encoding.Default, true) as object);
Assert.Fail();
}
}
}
Debug Trace:
TransientFaultException was thrown (which is good): TransientFaultException (Cuemon)
Source:
Cuemon
Message:
The amount of retry attempts has been reached.
Data:
Key: Attempts
Value: 5
Key: RecoveryWaitTimeInSeconds
Value: 21
Key: TotalRecoveryWaitTimeInSeconds
Value: 56
InnerException [of TransientFaultException]:
TimeoutException (System)
Source:
Cuemon
Message:
The operation has timed out.
Status code in response was 200 - OK.
The headers of the response was Cache-Control: private, max-age=0, Content-Type: text/html; charset=ISO-8859-1, Date: Thu, 25 Apr 2013 01:09:52 GMT, Expires: -1, Set-Cookie: PREF=ID=fd0ca0865d752f5b:FF=0:TM=1366852192:LM=1366852192:S=KNM_xhwUAUmLa1-f; expires=Sat, 25-Apr-2015 01:09:52 GMT; path=/; domain=.google.dk,NID=67=vYXlkUfWQ_paZ7fdrkXaq2gmgUati-Y3FfiPzpRLQTTWn7lQgWowKZgE53z4_D1G04SmEk0N_4YdaUKC2RZkajhrCZ69QrCRHKBumdejJg4Z2MKak7fUF0QUbL7nKf3F; expires=Fri, 25-Oct-2013 01:09:52 GMT; path=/; domain=.google.dk; HttpOnly, P3P: CP="This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info.", Server: gws, X-XSS-Protection: 1; mode=block, X-Frame-Options: SAMEORIGIN, Transfer-Encoding: chunked.
Figure 3: The output of the test in Figure 2TransientFaultException was thrown (which is good): TransientFaultException (Cuemon)
Source:
Cuemon
Message:
The amount of retry attempts has been reached.
Data:
Key: Attempts
Value: 5
Key: RecoveryWaitTimeInSeconds
Value: 21
Key: TotalRecoveryWaitTimeInSeconds
Value: 56
InnerException [of TransientFaultException]:
TimeoutException (System)
Source:
Cuemon
Message:
The operation has timed out.
Status code in response was 200 - OK.
The headers of the response was Cache-Control: private, max-age=0, Content-Type: text/html; charset=ISO-8859-1, Date: Thu, 25 Apr 2013 01:09:52 GMT, Expires: -1, Set-Cookie: PREF=ID=fd0ca0865d752f5b:FF=0:TM=1366852192:LM=1366852192:S=KNM_xhwUAUmLa1-f; expires=Sat, 25-Apr-2015 01:09:52 GMT; path=/; domain=.google.dk,NID=67=vYXlkUfWQ_paZ7fdrkXaq2gmgUati-Y3FfiPzpRLQTTWn7lQgWowKZgE53z4_D1G04SmEk0N_4YdaUKC2RZkajhrCZ69QrCRHKBumdejJg4Z2MKak7fUF0QUbL7nKf3F; expires=Fri, 25-Oct-2013 01:09:52 GMT; path=/; domain=.google.dk; HttpOnly, P3P: CP="This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info.", Server: gws, X-XSS-Protection: 1; mode=block, X-Frame-Options: SAMEORIGIN, Transfer-Encoding: chunked.
If you don't like the default recovery wait time this can easily be added using one of the overloads on the TransientFaultUtility class. For your convenience, I have included the default implementation in Figure 4.
/// <summary>
/// Specifies the amount of time to wait for a transient fault to recover gracefully before trying a new attempt.
/// </summary>
/// <param name="currentAttempt">The current attempt.</param>
/// <returns>A <see cref="TimeSpan"/> that defines the amount of time to wait for a transient fault to recover gracefully.</returns>
/// <remarks>Default implementation is <see cref="RecoveryWaitTime"/> + 2^ to a maximum of 5; a total of 5 (default) + 32 = 37 seconds.</remarks>
public static TimeSpan RecoveryWaitTime(int currentAttempt)
{
TimeSpan sleep = DefaultRecoveryWaitTime;
sleep = sleep.Add(TimeSpan.FromSeconds(Math.Pow(2, currentAttempt > 5 ? 5 : currentAttempt)));
return sleep;
}
I hope this little introduction have inspired you to master Transient Fault Handling one way or another. Happy coding.
Thursday, April 25, 2013
Posted by Michael Mortensen
Tag :
.net,
action,
azure,
cloud,
connection,
connectivity,
delegate,
enterprise,
func,
issues,
library,
network,
robust,
temporary,
timeout,
transient fault handling,
unavailability
AES - An Easy way of applying Security?
So you are running .NET 2.0 and want to implement the Advanced Encryption Standard (hereafter referred to as AES), eh? Well, no worries. Although this was first "officially" supported with .NET 3.5 in the System.Security.Cryptography namespace, the standard itself is based on the Rijndael cipher why it is rather easy to take advantage of this widely accepted encryption standard.
For more information about the theory behind Rijndael and AES take a look at this Wikipedia article.
As you may have discovered from the reading above and the example provided by MSDN, it can be rather cumbersome and time consuming to implement the AES, why the Cuemon.Security.Cryptography namespace provide you with a static AdvancedEncryptionStandardUtility class that significantly will ease the implementation of an AES secured application accepting key sizes of 128-, 192- and 256-bit.
To give an example on how you could use the AES, have a look at Figure 1. This very basic AesExample class will pre-initialize some required properties that we can use in Figure 2 with an equally basic test method. Figure 3 shows the output of the test.
Figure 1: A basic class to show a potential usage of the AdvancedEncryptionStandardUtility class
Figure 2: A simple test method that utilizes the class in Figure 1
When using the AES always provide a new, random and unique IV on each usage. With this in mind, it is perfectly safe to send the IV together with the encrypted value as there is no way to guess the secret key since the output is unpredictable. For more information do take a look at this Wikipedia article.
I hope this little introduction to the AdvancedEncryptionStandardUtility class has proven usefull to you. Happy coding.
For more information about the theory behind Rijndael and AES take a look at this Wikipedia article.
As you may have discovered from the reading above and the example provided by MSDN, it can be rather cumbersome and time consuming to implement the AES, why the Cuemon.Security.Cryptography namespace provide you with a static AdvancedEncryptionStandardUtility class that significantly will ease the implementation of an AES secured application accepting key sizes of 128-, 192- and 256-bit.
To give an example on how you could use the AES, have a look at Figure 1. This very basic AesExample class will pre-initialize some required properties that we can use in Figure 2 with an equally basic test method. Figure 3 shows the output of the test.
public class AesExample
{
public AesExample()
{
this.Encoding = Encoding.UTF8;
this.InitializationVector = AdvancedEncryptionStandardUtility.GenerateInitializationVector();
}
public virtual byte[] SecretKey
{
get { return Convert.FromBase64String("bzl6ZmxoU3Z3a2x4YkVLTmdZYkRGTEI5TEVUMTAya0s="); }
}
public Encoding Encoding { get; private set; }
public byte[] InitializationVector { get; set; }
public byte[] Lock(string secret)
{
return AdvancedEncryptionStandardUtility.Encrypt(ConvertUtility.ToByteArray(secret, PreambleSequence.Remove, this.Encoding),
this.SecretKey,
this.InitializationVector);
}
public string Unlock(byte[] secret)
{
return ConvertUtility.ToString(AdvancedEncryptionStandardUtility.Decrypt(secret, this.SecretKey, this.InitializationVector),
PreambleSequence.Remove,
this.Encoding);
}
}
[TestClass]
public class AesExampleTest
{
[TestMethod]
public void TestAes()
{
AesExample aes = new AesExample();
string sensitiveValue = "This is my top secret value that needs protection by AES 256.";
byte[] lockedValue = aes.Lock(sensitiveValue);
string unlockedValue = aes.Unlock(lockedValue);
Assert.IsTrue(sensitiveValue.Equals(unlockedValue));
Debug.WriteLine("Sensitive value is: {0}", sensitiveValue as object);
Debug.WriteLine("Secret key is: {0}", Convert.ToBase64String(aes.SecretKey) as object);
Debug.WriteLine("Initialization Vector is: {0}", Convert.ToBase64String(aes.InitializationVector) as object);
Debug.WriteLine("Locked value is: {0}", Convert.ToBase64String(lockedValue) as object);
Debug.WriteLine("Unlocked value is: {0}", unlockedValue as object);
}
}
Debug Trace:
Senstive value is: This is my top secret value that needs protection by AES 256.
Secret key is: bzl6ZmxoU3Z3a2x4YkVLTmdZYkRGTEI5TEVUMTAya0s=
Initialization Vector is: RkJnTlRlYXlxNjBya1R2cQ==
Locked value is: J+wLayRmJIglaHUKSMfFblL75Cz1g5qU2oWrXUU9wVhR8aMbhw0YQPZQ3fTsRfxFYZfHlm1Xp31MD7CdQwIvDw==
Unlocked value is: This is my top secret value that needs protection by AES 256.
Figure 3: The output of the test in Figure 2Senstive value is: This is my top secret value that needs protection by AES 256.
Secret key is: bzl6ZmxoU3Z3a2x4YkVLTmdZYkRGTEI5TEVUMTAya0s=
Initialization Vector is: RkJnTlRlYXlxNjBya1R2cQ==
Locked value is: J+wLayRmJIglaHUKSMfFblL75Cz1g5qU2oWrXUU9wVhR8aMbhw0YQPZQ3fTsRfxFYZfHlm1Xp31MD7CdQwIvDw==
Unlocked value is: This is my top secret value that needs protection by AES 256.
When using the AES always provide a new, random and unique IV on each usage. With this in mind, it is perfectly safe to send the IV together with the encrypted value as there is no way to guess the secret key since the output is unpredictable. For more information do take a look at this Wikipedia article.
I hope this little introduction to the AdvancedEncryptionStandardUtility class has proven usefull to you. Happy coding.
Wednesday, April 24, 2013
Posted by Michael Mortensen
Tag :
.net,
aes,
aes128,
aes192,
aes256,
decryption,
encryption,
initialization vector,
iv,
password,
rijndael,
security
Say what? Hash in my code?!
At some point you will eventually find the need to implement a way of either verifying or securing part of your data with the use of cryptographic hash functions. Luckily .NET offers a comprehensive set of classes for this task found in the System.Security.Cryptography namespace.
What is a cryptographic hash function? Well, for a meaningful explanation do refer to this Wikipedia article and afterwards think of it as a digital fingerprint (or checksum if you prefer).
Like i said, .NET offers a comprehensive set of classes to accommodate the need for hashing, and although simple to work with out-of-the-box, it can be rather cumbersome in the long run as you have to remember which algorithm type to use, create a byte array and last but not least; remember to dispose your object afterwards. This is one of the reasons why I introduced the static HashUtility class; to ease the usage while offering a flexible way of hashing your objects.
The overloads taking either a string or a string array in the above mentioned class, all uses UTF-16 as the backing encoding by design, where as most online hashing generators uses UTF-8. However, this can easily be overridden by using of the many overloads of the ComputeHash method.
Supported hash algorithm types per April 24th 2013 is:
Let's get on with the show; in Figure 1 you can see an example of a simple PasswordHashExample class that will use a SHA256 hash algorithm using UTF-8 for backing encoding, where Figure 2 show an equally simple test method that uses this class. Figure 3 shows the output of the test.
Figure 1: A simple class to show a potential usage of the HashUtility class
Figure 2: An equally simple test method that utilizes the class in Figure 1
As you can see, the usage is very simple and the only thing you need to consider is the encoding should you choose to use one of the overloads where a string or an array of strings needs hashing. Since all strings in .NET "per se" is UTF-16 encoded, this is also the default here; but only for the string overloads.
I hope you liked this little sneak preview into the HashUtility class. Happy coding.
What is a cryptographic hash function? Well, for a meaningful explanation do refer to this Wikipedia article and afterwards think of it as a digital fingerprint (or checksum if you prefer).
Like i said, .NET offers a comprehensive set of classes to accommodate the need for hashing, and although simple to work with out-of-the-box, it can be rather cumbersome in the long run as you have to remember which algorithm type to use, create a byte array and last but not least; remember to dispose your object afterwards. This is one of the reasons why I introduced the static HashUtility class; to ease the usage while offering a flexible way of hashing your objects.
The overloads taking either a string or a string array in the above mentioned class, all uses UTF-16 as the backing encoding by design, where as most online hashing generators uses UTF-8. However, this can easily be overridden by using of the many overloads of the ComputeHash method.
Supported hash algorithm types per April 24th 2013 is:
- MD5 (default)
- SHA-1
- SHA-256
- SHA-384
- SHA-512
- RIPEMD-160
- CRC-32
Let's get on with the show; in Figure 1 you can see an example of a simple PasswordHashExample class that will use a SHA256 hash algorithm using UTF-8 for backing encoding, where Figure 2 show an equally simple test method that uses this class. Figure 3 shows the output of the test.
public class PasswordHashExample
{
public PasswordHashExample()
{
this.PasswordSalt = StringUtility.CreateRandomString(32);
}
public string PasswordHash { get; private set; }
public string PasswordSalt { get; set; }
public void SetPassword(string password)
{
this.PasswordHash = this.ComputePassword(password, this.PasswordSalt);
}
public bool ValidatePassword(string password)
{
return this.PasswordHash.Equals(this.ComputePassword(password, this.PasswordSalt));
}
private string ComputePassword(string password, string salt)
{
return HashUtility.ComputeHash(new string[] { password, salt }, HashAlgorithmType.SHA256, Encoding.UTF8);
}
}
[TestClass]
public class PasswordHashExampleTest
{
[TestMethod]
public void TestPassword()
{
string password = "mySuperStr0ngP@ssword!";
PasswordHashExample example = new PasswordHashExample();
example.SetPassword(password);
Assert.IsTrue(example.ValidatePassword(password));
Assert.IsFalse(example.ValidatePassword("mySuperStrongPassword!"));
Debug.WriteLine("Password is: {0}", password as object);
Debug.WriteLine("Salt is: {0}", example.PasswordSalt as object);
Debug.WriteLine("Hashed password is: {0}", example.PasswordHash as object);
}
}
Debug Trace:
Password is: mySuperStr0ngP@ssword!
Salt is: FWoyWJxrK6cqS9lL5EFzPp5EMKoGFteP
Hashed password is: 95dae249a69c67654868a59077fae296a532aef39332c919975de5b1e69a60a6
Figure 3: The output of the test in Figure 2Password is: mySuperStr0ngP@ssword!
Salt is: FWoyWJxrK6cqS9lL5EFzPp5EMKoGFteP
Hashed password is: 95dae249a69c67654868a59077fae296a532aef39332c919975de5b1e69a60a6
As you can see, the usage is very simple and the only thing you need to consider is the encoding should you choose to use one of the overloads where a string or an array of strings needs hashing. Since all strings in .NET "per se" is UTF-16 encoded, this is also the default here; but only for the string overloads.
I hope you liked this little sneak preview into the HashUtility class. Happy coding.
Posted by Michael Mortensen
WebConfigurationUtility - one of the newborns
There are still areas in the .NET Framework where I ask myself why? Why was this obvious method not part of the otherwise comprehensive collection of methods?
What I am talking about here is of course a way to get custom HTTP handlers. For reasons unknown to me, you cannot get a collection of HTTP handlers as you can with HTTP modules on the HttpApplication Class. What you can try instead is to open a web.config file using the WebConfigurationManager Class, but surprisingly this does not provide access to custom HTTP handlers from IIS7+, as this StackOverflow article also confirms.
So what can you do?
Well (for me at least) the most obvious solution was to bypass the ConfigurationManager while supporting elements from it, and simply provide ways to open a Web-application configuration file and afterwards provide ways to get specific areas of this file. I know - one could just parse the XML and find the area of interest them self, but I want to lend a helping hand in these matters so the code is kept as clean without the clutter of XPath expressions.
So, with the above in mind, I decided to create a static class named WebConfigurationUtility shown in Figure 1, where I added the methods i needed up front for my latest development additions while looking for ways to re-factor some of my older code to use these new methods; all while keeping the compatibility to IIS6 and newer.
To provide an example, you can see in Figure 2 that we are opening a Web-application configuration file from the root of an ASP.NET application. From here we call the GetHandlers method which in return gives us an IEnumerable of the HttpHandlerAction Class which we can then iterate and work with as we are used to.
Figure 2: Code snippet from a future Endpoint addition of the Cuemon.ServiceModel assembly
Well, that is all I have for now in regards to the WebConfigurationUtility. I hope this gave a little insight in finding ways to support and - to some extend - improve existing functionality.
What I am talking about here is of course a way to get custom HTTP handlers. For reasons unknown to me, you cannot get a collection of HTTP handlers as you can with HTTP modules on the HttpApplication Class. What you can try instead is to open a web.config file using the WebConfigurationManager Class, but surprisingly this does not provide access to custom HTTP handlers from IIS7+, as this StackOverflow article also confirms.
So what can you do?
Well (for me at least) the most obvious solution was to bypass the ConfigurationManager while supporting elements from it, and simply provide ways to open a Web-application configuration file and afterwards provide ways to get specific areas of this file. I know - one could just parse the XML and find the area of interest them self, but I want to lend a helping hand in these matters so the code is kept as clean without the clutter of XPath expressions.
So, with the above in mind, I decided to create a static class named WebConfigurationUtility shown in Figure 1, where I added the methods i needed up front for my latest development additions while looking for ways to re-factor some of my older code to use these new methods; all while keeping the compatibility to IIS6 and newer.
![]() |
| Figure 1: WebConfigurationUtility members |
To provide an example, you can see in Figure 2 that we are opening a Web-application configuration file from the root of an ASP.NET application. From here we call the GetHandlers method which in return gives us an IEnumerable of the HttpHandlerAction Class which we can then iterate and work with as we are used to.
IXPathNavigable webConfig = WebConfigurationUtility.OpenWebConfiguration("/");
IEnumerable<HttpHandlerAction> handlers = WebConfigurationUtility.GetHandlers(webConfig);
foreach (Type endpointType in EndpointRoutes.Keys)
{
foreach (HttpHandlerAction handler in handlers)
{
if (endpointType == CallingAssembly.GetType(handler.Type))
{
EndpointHandlers.Add(endpointType, handler);
}
}
}
Well, that is all I have for now in regards to the WebConfigurationUtility. I hope this gave a little insight in finding ways to support and - to some extend - improve existing functionality.
Tuesday, April 23, 2013
Posted by Michael Mortensen
A warm welcome to my first blog
Let me start by introducing myself. My name is Michael, I am 35 years of age and currently sole contributor to the Cuemon .NET Framework Additions project, which was first released in 2008.
The main reason for the project back in the days was to:
The main reason for the project back in the days was to:
- Have a simple, logical and every-day usage additions to the Microsoft .NET Framework that help solves trivial matters
- Support XML and XSLT in traditional web pages
- Having a way of defining business entities
- Having an abstract repository for storage of data for business entities
- Ease the usage of XML related operations
- Follow the highly acclaimed book "Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries" by Krzysztof Cwalina and Brad Abrams
Today this assembly library has grown every so large and continuous to add new ways to extend the .NET Framework from version 2.0 with SP1 and forward; and I will strive to keep it targeted this platform as many projects still rely on this framework version - with a good reason; the CLR is robust, flexible and versatile - and up to this date - even with .NET Framework 4.5, the CLR in 2.0 and 4.0 is very similar to one another.
The Microsoft architects and engineers really did an excellent job with the .NET Framework and i sincerely hope it will inspire others as it did me.

That being said, let me go to the purpose of this blog; it is created to support the Cuemon project with motives and background for implementing an idea, the route to an idea, explanation of how and why i choose to do things the way i do, walk through of the assembly family members and hopefully input from visitors to this blog with feedback as well as new ideas.




