Saturday, November 22, 2014

New Blogs!

Check out my new blogs for Isos Technology. Here's the latest.

Saturday, June 18, 2011

Curiously Recurring Template Pattern

I was inspired to write this after seeing this question & answer on stackoverflow.com.
Several things occurred to me while reading it. I want to focus on the technical issue, but I can't help but mention a couple ancillary things that got me here. Before I start, I want people to keep in mind that this post is not meant to be a criticism of anyone. It is a criticism of certain ideas.
It bothers me that the "accepted answer" for the posted C# question (the question was: "Is there a more elegant way to do this?") on stack overflow basically boiled down to: "Try Haskell".
Really? That's not an acceptable answer. Not just because I disagree with it. But because it didn't actually answer the question. And do you really think the original poster--who marked that as the accepted answer--is now using Haskell to solve his problem? I doubt it.
Accompanying the answer was a link to a blog post, providing further details as to why people should avoid using this pattern.
It's an interesting read. And it's a valid opinion. And I couldn't disagree more.
Here's the problem, as envisioned by Eric Lippert:
public abstract class Animal<T> where T : Animal<T>
{
   public abstract void MakeFriends(T animal);
}

public class Cat : Animal<Cat>
{
   public override void MakeFriends(Cat cat) { }
}
The problem with this, according to Eric, is that we can still create a class that would violate our intentions, such as this:
public class EvilDog : Animal<Cat>
{
   public override void MakeFriends(Cat cat) { }
}
Now an EvilDog can make friends with a Cat!
You see, some people mistakenly think that when we defined the class Animal<T> with the constraint that T be a subclass of Animal<T>, that T must be the subclass itself, not another sister class underneath Animal<T>.
But this isn't a flaw in the pattern!
It's no different than this:
public class Cat
{
   public void MakeFriends(Cat cat) { }
}

public class EvilDog : Cat
{
}
Oh no! Even without using generics and the curiously recurring template pattern we can create an EvilDog that can make friends with a Cat!
Anyway...
I use the pattern quite regularly. It can be an enormously helpful tool. Like anything, it can be misused.
If it's imperative that T be the defined subclass, and it's seen as likely that developers won't realize that, you can mitigate that problem by doing something as simple as:
protected Animal()
{
   if (!(this is T))
      throw new ApplicationException("Can't do that!");
}
Problem (if there ever was one) solved.

Friday, November 5, 2010

Logverse Available for Purchase!

Because some people have expressed interest in purchasing the source code for Logverse rather than paying a monthly subscription fee, I've decided to add that option.
If you're interested, contact me through the Logverse site for more details.

Friday, September 3, 2010

Tweeting Logger (Twitter Logger Updated -- Using OAuth)

Twitter now requires OAuth in order to use their API. I thought it might be nice to provide a new Logger that tweets using Twitter's OAuth.

Unfortunately, compared to the original TwitterLogger, the code has ballooned quite a bit. However, if you want to use one of the available Twitter API libraries, then most of the code here would go away and you'd be back to a tiny Logger subclass that Tweets.

But if you want to avoid adding yet another DLL to your application and you have limited needs to integrate with Twitter--such as just tweeting, then this should get you started.

Check out the Twitter help for obtaining a Consumer Key, a Consumer Secret, an Access Token, and an Access Token Secret.

Notice that there is only one method that is overridden. The rest of the code is there to support OAuth. Remember, this is intended to be neither a complete OAuth implementation, nor a complete Twitter API integration. It's just a simple TweetingLogger. And frankly, it could be improved quite a bit. (e.g. Separating out the tweeting specifics into another class, parameterizing the constants, etc.) But it's good enough to get you started.


public class TweetingLogger : Logger
    {
        const string TweetUrl = "http://api.twitter.com/1/statuses/update.json";

        const string ConsumerKey = "[Your Consumer Key]";
        const string ConsumerSecret = "[Your Consumer Secret]";
        const string AccessToken = "[Your Access Token]";
        const string AccessTokenSecret = "[Your Access Token Secret]";

        protected string GetOAuthUrlEncode(string aValue)
        {
            // Thanks to Stephen Denton for this url encode

            var newValue = System.Web.HttpUtility.UrlEncode(aValue).Replace("+", "%20");

            // UrlEncode escapes with lowercase characters (e.g. %2f) but oAuth needs %2F
            newValue = System.Text.RegularExpressions.Regex.Replace(newValue, "(%[0-9a-f][0-9a-f])", c => c.Value.ToUpper());

            // these characters are not escaped by UrlEncode() but needed to be escaped
            newValue = newValue.Replace("(", "%28").Replace(")", "%29").Replace("$", "%24").Replace("!", "%21").Replace("*", "%2A").Replace("'", "%27");

            // these characters are escaped by UrlEncode() but will fail if unescaped!
            newValue = newValue.Replace("%7E", "~");

            return newValue;
        }

        protected string GetTimeStamp()
        {
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return Convert.ToInt64(ts.TotalSeconds).ToString();
        }

        protected string GetNonce()
        {
            return Guid.NewGuid().ToString();
        }

        protected string GetOAuthBaseString(string anHttpMethod, string aBaseUri, SortedDictionary<string, string> someParameters)
        {
            var paramStrings = new List<String>();
            foreach (string key in someParameters.Keys)
                paramStrings.Add(key + "=" + someParameters[key]);
            return anHttpMethod.ToUpper() + "&" + GetOAuthUrlEncode(aBaseUri) + "&" + GetOAuthUrlEncode(String.Join("&", paramStrings.ToArray()));
        }

        protected string GetAuthSignature(SortedDictionary<string,string> someParameters)
        {
            var hmacsha1 = new System.Security.Cryptography.HMACSHA1(Encoding.ASCII.GetBytes(ConsumerSecret + "&" + AccessTokenSecret));
            var bytes = hmacsha1.ComputeHash(Encoding.ASCII.GetBytes(GetOAuthBaseString("POST", TweetUrl, someParameters)));
            return Convert.ToBase64String(bytes);
        }

        protected SortedDictionary<string, string> GetOAuthParameters()
        {
            return new SortedDictionary<string,string>()
            {
                { "oauth_nonce", GetNonce() },
                { "oauth_consumer_key", ConsumerKey },
                { "oauth_signature_method", "HMAC-SHA1" },
                { "oauth_timestamp", GetTimeStamp() },
                { "oauth_token", AccessToken },
                { "oauth_version", "1.0" },
                //{ "oauth_signature", GetAuthSignature() },    // add later in process
            };
        }

        protected string GetOAuthString(SortedDictionary<string, string> baseParameters, SortedDictionary<string, string> additionalParameters)
        {
            var sb = new StringBuilder();
            sb.Append("OAuth ");

            var paramList = new List<string>();
            foreach (string key in baseParameters.Keys)
                paramList.Add(key + "=" + "\"" + baseParameters[key] + "\"");

            sb.Append(string.Join(",", paramList.ToArray()));

            var allParameters = new SortedDictionary<string, string>(baseParameters);
            foreach (KeyValuePair<string, string> kv in additionalParameters)
                allParameters.Add(kv.Key, kv.Value);

            sb.Append(",oauth_signature=\"" + GetOAuthUrlEncode(GetAuthSignature(allParameters)) + "\"");

            return sb.ToString();
        }

        protected override bool DoLog(LogEntry aLogEntry)
        {
            // without this, you may receive a '417 Expectation Failed' error
            ServicePointManager.Expect100Continue = false;

            using (WebClient wClient = new WebClient())
            {
                var parameters = GetOAuthParameters();
                var additionalParameters = new SortedDictionary<string, string>() { { "status", GetOAuthUrlEncode(aLogEntry.Message) } };
                var theString = GetOAuthString(parameters, additionalParameters);
                wClient.Headers.Add("Authorization", theString);

                var nvc = new NameValueCollection();
                nvc["status"] = aLogEntry.Message;
                wClient.UploadValues(TweetUrl, nvc);
            }

            return true;
        }

    }

Wednesday, March 24, 2010

Logging to Logverse

If you haven't heard of Logverse, it's a service that allows applications to log to it. And just about anything can be logged to it--errors, user actions, debug information... whatever you want.

It's perfect for applications out in the field, like desktop and mobile applications. But even web or other server applications can take advantage of it.

You don't need to use a logging framework to use Logverse. But if you use The Object Guy's Logging Framework, it is extremely easy to create a logger that logs to Logverse.

I have two very small classes here. The first is just a utility class that will write to Logverse.

public class LogverseWriter
{
   private readonly Guid LogId;
   private readonly string SubscriptionKey;

   public LogverseWriter(string aLogId, string aSubscriptionKey)
   {
      LogId = new Guid(aLogId);
      SubscriptionKey = aSubscriptionKey;
   }

   public bool Write(Dictionary<string, string> values)
   {
      var logEntryInfo = new Logverse.LogEntryInfo()
         {
           LogId = LogId,
           SubscriptionKey = SubscriptionKey,
           ClientTimeStampUtc = DateTime.UtcNow
         };

      logEntryInfo.Fields = values;

      var service = new Logverse.LogServiceClient();
      var result = service.Log(logEntryInfo);
      service.Close();

      return result.Result == Logverse.LogResult.EResult.Logged;
   }
}

The second class I have is the LogverseLogger.

public class LogverseLogger : Logger
{
   private readonly LogverseWriter LogverseWriter;

   public LogverseLogger(string aLogId, string aSubscriptionKey)
   {
      LogverseWriter = new LogverseWriter(aLogId, aSubscriptionKey);
   }

   protected override bool DoLog(LogEntry aLogEntry)
   {
      return LogverseWriter.Write(
              new Dictionary<string, string>()
              {
                { "Category", aLogEntry.Category != null ? aLogEntry.Category.ToString() : "" },
                { "Severity", aLogEntry.SeverityString },
                { "Message",  aLogEntry.Message }
              });
   }

   public static void Create(string aLogId, string aSubscriptionKey)
   {
      ConfigLogger.Instance.AddLogger("logverse_" + aLogId, new LogverseLogger(aLogId, aSubscriptionKey));
   }
}

The Logverse logger can't currently be configured using the web.config (or app.config), so at the beginning of the application I call the Create method. Here's an example for a web application. This code would go into global.asax.cs.

protected void Application_Start(object sender, EventArgs e)
{
   LogverseLogger.Create("<mylogid>", "<mysubscriptionkey>");
}

WOW!

Now that was easy. And now I can log to Logverse. Pretty cool, huh?

One might ask, "What about exception handling?" Good question. Actually, it isn't required in the Logger, because that's handled automatically by the framework. (Cool!) However, if you were to use the LogverseWriter class independently, then you'd probably would want to put some exception handling in it. The only reason I didn't was so that I'd have an excuse to talk about it in this paragraph. :)

Friday, March 19, 2010

Logverse Videos



Sunday, March 14, 2010

Logverse

Logverse is coming...