A Zookeeper Import Utility in C# and .NET

Implemented below is an import utility for Zookeeper implemented in C#.  It uses the Org.Apache.ZooKeeper library for .NET (“ZooKeeperNet”).


using System;
using System.Collections.Generic;
//using System.Linq;
using System.Text;
//using System.Threading.Tasks;
using Org.Apache.Zookeeper.Data;
using ZooKeeperNet;

namespace ZookeeperImportUtility
{
	class ZookeeperImportUtility
	{
		#region Variables

		bool isDebugModeEnabled;
		Dictionary<string, string> variableLookup;
		Dictionary<string, string> settingLookup;
		string zookeeperConnectString;

		#endregion Variables

		#region Main

		static void Main(string[] args)
		{
			new ZookeeperImportUtility().parseAndSaveSettings(args);
		}

		#endregion Main

		#region Instance Methods

		void displayAndValidateInput()
		{
			Console.WriteLine("Zookeeper Connect String: " + this.zookeeperConnectString);

			string variableNamesAndValues = "";
			foreach (string variableName in this.variableLookup.Keys)
			{
				string variableValue = this.variableLookup[variableName];
				variableNamesAndValues += variableName + "=\"" + variableValue + "\" ";
			}
			Console.WriteLine("Variables: " + variableNamesAndValues);

			string settingNamesAndValues = "";
			foreach (string settingName in this.settingLookup.Keys)
			{
				string settingValue = this.settingLookup[settingName];
				settingNamesAndValues += settingName + "=\"" + settingValue + "\" ";
			}
			Console.WriteLine("Settings: " + settingNamesAndValues);

			if (this.zookeeperConnectString == null)
			{
				throw new Exception("The Zookeeper connect string must be specified using the -zookeeper switch.");
			}
			else if (this.settingLookup.Count == 0)
			{
				throw new Exception("The name and value of at least one setting must be specified using the -setting switch.");
			}

		}

		string evaluateVariables(string stringToEvaluate)
		{
			string[] variableIndicators = { "[", "]" };

			string[] stringSplitOnVariableIndicators = stringToEvaluate.Split
			(
				variableIndicators,
				StringSplitOptions.None
			);

			for (int v = 1; v < stringSplitOnVariableIndicators.Length; v += 2)
			{
				string variableName = stringSplitOnVariableIndicators[v];
				string variableTag = variableIndicators[0] + variableName + variableIndicators[1];
				string variableValue = this.variableLookup[variableName];

				stringToEvaluate = stringToEvaluate.Replace
				(
					variableTag,
					variableValue
				);
			}

			return stringToEvaluate;
		}

		void parseAndSaveSettings(string[] arguments)
		{
			Console.WriteLine("Zookeeper Setting Import Utility");
			Console.WriteLine("================================");
			Console.WriteLine("Program begins: " + DateTime.Now.ToString());

			try
			{
				this.parseArguments(arguments);
				this.displayAndValidateInput();
				this.saveSettings();
			}
			catch (Exception ex)
			{
				Console.WriteLine("ERROR: " + ex.Message);
			}

			if (this.isDebugModeEnabled == true)
			{
				Console.Write("Running in debug mode.  Press a key to continue:");
				Console.ReadKey();
			}

			Console.WriteLine("Program ends: " + DateTime.Now.ToString());
		}

		void parseArguments(string[] arguments)
		{
			Dictionary<string, string> aliasToCanonicalArgumentNameLookup = new Dictionary<string, string>()
			{
				{ "-debug", "debug" },
				{ "-d", "debug" },
				{ "-set", "set" },
				{ "-s", "set" },
				{ "-variable", "variable" },
				{ "-v", "variable" },
				{ "-zookeeper", "zookeeper" },
				{ "-zk", "zookeeper" },
			};

			Dictionary<string, int> argumentNameToSubargumentCountLookup = new Dictionary<string, int>()
			{
				{ "debug", 0 },
				{ "set", 2 },
				{ "variable", 2 },
				{ "zookeeper", 1 },
			};

			Dictionary<string, string[]> argumentNamesSpecifiedToSubargumentLookup = new Dictionary<string, string[]>();

			for (int i = 0; i < arguments.Length; i++)
			{
				string argument = arguments[i].Trim();
				if (aliasToCanonicalArgumentNameLookup.ContainsKey(argument) == true)
				{
					string argumentName = aliasToCanonicalArgumentNameLookup[argument];					

					int numberOfSubarguments = argumentNameToSubargumentCountLookup[argumentName];
					string[] subarguments = new string[numberOfSubarguments];
					for (var j = 0; j < numberOfSubarguments; j++)
					{
						string subargument = arguments[i + j + 1].Trim();
						subarguments[j] = subargument;
					}

					argumentNamesSpecifiedToSubargumentLookup.Add(argumentName, subarguments);
					
					i += numberOfSubarguments;					
				}
				else
				{
					throw new Exception("Unrecognized command-line argument: " + argument);
				}
			}

			if (argumentNamesSpecifiedToSubargumentLookup.ContainsKey("zookeeper") == false)
			{
				const string zookeeperConnectStringDefault = "localhost:2181";
				Console.WriteLine("No argument 'zookeeper' specified, defaulting to: " + zookeeperConnectStringDefault);
				argumentNamesSpecifiedToSubargumentLookup["zookeeper"] = new string[] { zookeeperConnectStringDefault };
			}

			this.settingLookup = new Dictionary<string, string>();
			this.variableLookup = new Dictionary<string, string>();

			foreach (var argumentName in argumentNamesSpecifiedToSubargumentLookup.Keys)
			{
				string[] subarguments = argumentNamesSpecifiedToSubargumentLookup[argumentName];
				
				if (argumentName == "debug")
				{
					this.isDebugModeEnabled = true;
				}
				else if (argumentName == "set")
				{
					string settingName = subarguments[0];
					string settingValue = subarguments[1];
					this.settingLookup.Add(settingName, settingValue);
				}
				else if (argumentName == "variable")
				{
					string variableName = subarguments[0];
					string variableValue = subarguments[1];
					this.variableLookup.Add(variableName, variableValue);
				}
				else if (argumentName == "zookeeper")
				{
					this.zookeeperConnectString = subarguments[0];
				}
			}
		}

		void saveSettingValueToPath(ZooKeeper zookeeper, string settingValue, string settingPath)
		{
			Console.WriteLine("Saving setting: " + settingPath + "=\"" + settingValue + "\"");

			// todo - Not sure what to use here.
			List<ACL> acl = new List<ACL>()
			{
				new ACL(Perms.ALL, Ids.ANYONE_ID_UNSAFE),
			};

			const string directoryDelimiter = "/";
			if (settingPath.Contains(directoryDelimiter) == true)
			{
				string[] settingPathDirectoryAncestry = settingPath.Split
				(
					new string[] { directoryDelimiter },
					StringSplitOptions.RemoveEmptyEntries
				);

				string directoryPathSoFar = "";
				for (int i = 0; i < settingPathDirectoryAncestry.Length - 1; i++)
				{
					string settingPathDirectoryName = settingPathDirectoryAncestry[i];
					directoryPathSoFar += directoryDelimiter + settingPathDirectoryName;
					Stat directoryExisting = zookeeper.Exists(directoryPathSoFar, false);
					if (directoryExisting == null)
					{
						zookeeper.Create
						(
							directoryPathSoFar,
							null, // data
							acl,
							CreateMode.Persistent
						);
					}
				}
			}

			Stat settingExisting = zookeeper.Exists(settingPath, false);
			if (settingExisting != null)
			{
				zookeeper.Delete(settingPath, 0);
			}

			byte[] settingValueAsBytes = UTF8Encoding.Default.GetBytes(settingValue);

			zookeeper.Create
			(
				settingPath, settingValueAsBytes, acl, ZooKeeperNet.CreateMode.Persistent
			);
		}

		void saveSettings()
		{
			ZooKeeper zookeeper = new ZooKeeperNet.ZooKeeper
			(
				zookeeperConnectString,
				new TimeSpan(0, 0, 30), // timeout
				null // watcher
			);

			foreach (string settingName in this.settingLookup.Keys)
			{
				string settingValue = this.settingLookup[settingName];

				string settingNameEvaluated = this.evaluateVariables(settingName);
				string settingValueEvaluated = this.evaluateVariables(settingValue);

				this.saveSettingValueToPath
				(
					zookeeper, settingValueEvaluated, settingNameEvaluated
				);
			}
		}

		#endregion Instance Methods
	}
}

This entry was posted in Uncategorized and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s