Using the Exchange Management Shell from C#

I’m wanting to build on my previous post titled “Using PowerShell from managed code” to show how to invoke the Exchange 2010 Management Shell from managed code.

Since Exchange 2007, the primary management tool for Exchange is the Exchange Management Shell (EMS). The EMS is really just a PowerShell session with Exchange
specific extensions loaded. So in addition to the normal commands like Get-ChildItem and Get-WmiObject we now also get things like Get-Mailbox and Get-ExchangeServer when the EMS is loaded.

So we’ve seen from my previous post that PowerShell can be invoked from within managed (C#). Question is: Can we invoke the Exchange Management Shell from managed code? Affirmative! We do as follows:

Initialize a new C# project as described in: “Using PowerShell from managed code”

Create an empty PowerShell pipeline:

PowerShell powershell = PowerShell.Create();

Load the Exchange Management Shell snap-in into the session:

  PSSnapInException ex;
  powershell
    .Runspace
    .RunspaceConfiguration
    .AddPSSnapIn(
       "Microsoft.Exchange.Management.PowerShell.E2010", 
       out ex);
  if (ex != null)
    throw ex;

Execute the RemoteExchange.ps1 script in the Exchange installation’s bin directory through a dot source:

powershell.AddScript(
  ". $env:ExchangeInstallPath\\bin\\RemoteExchange.ps1");

Create a remote powershell session to the local Exchange server through the Connection-ExchangeServer cmdlet:

powershell.AddScript("Connect-ExchangeServer -auto");

Invoke the added commands and prepare the PowerShell session for further use:

powershell.Invoke();
powershell.Streams.ClearStreams();
powershell.Commands.Clear();

At this point we are ready to execute Exchange Management Shell commands:

powershell.AddScript("Get-Mailbox");

Now before we start writing lots of code lets first build a small wrapper class again:

public class EmsSession
{
    protected readonly PowerShell powershell;

    public EmsSession()
    {
        powershell = PowerShell.Create();
        PSSnapInException ex;
        PSSnapInException ex;
        powershell
          .Runspace
          .RunspaceConfiguration
          .AddPSSnapIn(
             "Microsoft.Exchange.Management.PowerShell.E2010", 
             out ex);
        if (ex != null)
          throw ex;
        powershell.AddScript(
          ". $env:ExchangeInstallPath\\bin\\RemoteExchange.ps1");
        powershell.AddScript("Connect-ExchangeServer -auto");
        powershell.Invoke();
        powershell.Streams.ClearStreams();
        powershell.Commands.Clear();
    }

    public void RunScript(string scriptText, 
        Action<Collection<PSObject>> handler)
    {
        powershell.Streams.ClearStreams();
        powershell.Commands.Clear();
        powershell.AddScript(scriptText, true);
        var results = powershell.Invoke();
        ThrowIfError();
        if (handler != null)
        {
           handler(results);
        }
    }

    public string RunScript(string scriptText)
    {
        var result = new StringBuilder();
        RunScript(scriptText, results =>
        {
            foreach (var line in results)
            {
                if (line != null)
                {
                    result.AppendLine(line.ToString());
                }
            }
        });
        return result.ToString().TrimEnd();
    }

    private void ThrowIfError()
    {
        var errors = powershell.Streams.Error;
        if (errors.Count > 0)
        {
            var e = errors[0].Exception;
            powershell.Streams.ClearStreams();
            throw e;
        }
    }
}

Add some syntactic sugar through extension methods:

public static class EmsExtentionMethods
{
    private static readonly EmsSession emsSession 
       = new EmsSession();

    public static string EmsExec(this string commands)
    {
        return emsSession.RunScript(commands);
    }

    public static void EmsExec(this string commands,
        Action<Collection<PSObject>> handler)
    {
        emsSession.RunScript(commands, handler);
    }
}

… and enjoy playing with the Exchange Management Shell like so:

var mailBoxGuid = 
    @"$guid = Get-Mailbox tom | select Guid
      $guid.Guid"
    .EmsExec();
Console.WriteLine("GUID for Tom's mailbox: {0}", mailBoxGuid);
Posted in Uncategorized by Hannes de Jager. 1 Comment

Using PowerShell from managed code

PowerShell… the gracious gift to Sys-Admins from Jeffrey Snover and his team at Microsoft… It needs no introduction and is certainly one of those tools that, once you start using it, you can’t imagine life without it anymore.

But hey, why should Sys-Admins have all the fun with this mighty automation framework and developers be left out in the cold? After all Windows PowerShell provides a hosting mechanism with which the Windows PowerShell runtime can be embedded inside other applications. In fact, this is what the Exchange Management Console does in 2007 and 2010 and also System Center Virtual Machine Manager – It sits on top of Powershell (as dictated by the Microsoft’s Common Engineering Criteria for server products) and delegates all the earth shaking stuff to PowerShell.

Pulling the Powershell engine into your managed code not only allows your application to leverage all of its built-in super-powers and all the extended functionality available from providers like Exchange, MS SQL and SCVMM but it also allows you to leverage all the knowledge and investments made by your system administrator – his code becomes your toolbox!!

So lets see how this can be done:

Pick your favorite .NET language (I’ll be using C#) and add a reference to the System.Management.Automation assembly in your project.

using System;
using System.Collections.ObjectModel;
using System.Management.Automation;

On my machine this assembly was located at C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0\

Now create an empty PowerShell pipeline by instantiating the System.Management.Automation.PowerShell class:

PowerShell powershell = PowerShell.Create();

Add some commands to the pipeline:

powershell.AddCommand("Get-Process");

or a more elaborate script:

powershell.AddScript("Get-Process | where { $_.CPU -gt 30 }");

Call the Invoke method to run the commands of the pipeline:

Collection<PSObject> result = powershell.Invoke();

and do something with the result:

foreach (PSObject process in result)
{
  Console.WriteLine("Name: {0}, ID: {1}",
    process.Members["ProcessName"].Value,
    process.Members["Id"].Value
  );
} 

Its that simple!

If you want to see if something went wrong:

var errors = powershell.Streams.Error;
if (errors.Count > 0)
{
  var e = errors[0].Exception;
  Console.WriteLine(
    "Hmmm, something went wrong. PowerShell gave me this: {0}", 
    e.Message
  );
}

And if you want to execute some more PowerShell with the same object:

powershell.Streams.ClearStreams();
powershell.Commands.Clear();
// ... Go again here...

And there we have the basics that enable us to build a little wrapper class for our reuse:

public class PowershellSession
{
    protected readonly PowerShell powershell;

    public PowershellSession()
    {
        powershell = PowerShell.Create();
    }

    public void RunScript(string scriptText, 
        Action<Collection<PSObject>> handler)
    {
        powershell.Streams.ClearStreams();
        powershell.Commands.Clear();
        powershell.AddScript(scriptText, true);
        var results = powershell.Invoke();
        ThrowIfError();
        if (handler != null)
        {
           handler(results);
        }
    }

    public string RunScript(string scriptText)
    {
        var result = new StringBuilder();
        RunScript(scriptText, results =>
        {
            foreach (var line in results)
            {
                if (line != null)
                {
                    result.AppendLine(line.ToString());
                }
            }
        });
        return result.ToString().TrimEnd();
    }

    private void ThrowIfError()
    {
        var errors = powershell.Streams.Error;
        if (errors.Count > 0)
        {
            var e = errors[0].Exception;
            powershell.Streams.ClearStreams();
            throw e;
        }
    }
}

Add some syntactic sugar through extension methods:

public static class PowershellExtentionMethods
{
    private static readonly PowershellSession powerShellSession 
       = new PowershellSession();

    public static string PowershellExec(this string commands)
    {
        return powerShellSession.RunScript(commands);
    }

    public static void PowershellExec(this string commands,
        Action<Collection<PSObject>> handler)
    {
        powerShellSession.RunScript(commands, handler);
    }
}

… and we have ourselves a framework for playing with PowerShell like so:

var outlookPid = 
    @"$id = Get-Process | where { $_.ProcessName -eq 'OUTLOOK' } | Select id
      $id.Id"
    .PowershellExec();
Console.WriteLine("Process ID for Outlook: {0}", outlookPid);

or how about something a little bit more involved:

Process explorerProcess = null;
"Get-Process | where { $_.ProcessName -eq 'explorer' }"
    .PowershellExec(result =>
        explorerProcess = (Process)result[0].BaseObject);
Console.WriteLine("Killing {0} process with PID {1}...", 
    explorerProcess.ProcessName,
    explorerProcess.Id);
explorerProcess.Kill();
Tags: ,
Posted in C# PowerShell by Hannes de Jager. 1 Comment

The power of PHP annotations

I was quite intrigued when I learned a while back that PHP documentation comments are available via reflection. This is the first language where I’ve seen this. This means that given the following code:

class AnyClass {
  /**
   * @param string $s
   */
  public function anyFunc($s) {
    // Any code
  } 
}

You can actually get a hold of the comment block above anyFunc from your PHP script:

function getComment() {
	$c = new ReflectionClass('AnyClass');
	$m = $c->getMethod('anyFunc');
	return $m->getDocComment();        
}

With a little parsing effort this effectively gives PHP what Java calls annotations and C# calls Attributes – A means to add meta data to your code that can be used in your logic. Not sure PHP have a name for this, but I’ll call it PHP annotations.

function getAnnotations() {
	$c = new ReflectionClass('AnyClass');
	$m = $c->getMethod('anyFunc');
	$s = $m->getDocComment();        
	$s = str_replace('/*', '', $s);
	$s = str_replace('*/', '', $s);
	$s = str_replace('*', '', $s);
	$aTags = explode('@', $s);
	return $aTags;
}

From this I was able to build a small little MVC framework that power-use these PHP annotations. For example here is a MVC controller action function:

/**
 * @param code is required
 * @param name is required
 * @param query is required
 * @loadview stored-query-save
 * @ajaxify 
 */
public function storedQuerySaveAction() {
  // At this point the framework made 
  // sure the HTTP request contains 
  // something in the code, name and 
  // query variables
          
  // When this action function 
  // returns the framework will 
  // render a view called stored-query-save
  // and the @ajaxify annotation will
  // cause the framework to add
  // some jQuery magic to the 
  // output to provide a better user
  // experience through the use
  // of ajax techniques.
}
Tags:
Posted in PHP by Hannes de Jager. 7 Comments

Using COM from Java via JNA

According to the COM spec a COM interface pointer is just a pointer to a pointer that points to an array of function pointers. So I was wondering: Can I call these function pointers from Java somehow? Not really to get any usefulness from it, but rather to learn a bit of COM and have a bit of fun.

Now JNA is a great library that I’ve used a couple of times. It allows you to call code in native DLL’s by only writing Java code, no JNI. JNA also comes with alot of advanced tools to manipulate native memory and pointers from right within Java. Theoretically it should be able to do COM invocations. I went on and tried it out, and this is what I came up with. It actually works:

The IUnknown interface

@IId("00000000-0000-0000-C000-000000000046")
public interface IUnknown {
    int QueryInterface(
       Guid riid, 
       PointerByReference ppvObject);
    int AddRef();
    int Release();
}

And a bit of code to create a COM object given a CLSID and return an IUnkown interface for it :

public static IUnknown createInstance(Guid clsId) {
    IId iidA = IUnknown.class.getAnnotation(IId.class);
    if (iidA == null)
        throw new IllegalArgumentException("Interface needs to be annotated with an IId");
    Guid iId = new Guid(iidA.value());
    Ole32 ole32 = WindowsJna.OLE32.get();
    PointerByReference p = new PointerByReference();
    int res = ole32.CoCreateInstance(clsId, null, Ole32.CLSCTX_INPROC, iId, p);
    if (res != Ole32.S_OK)
        throw new WinApiException(res);
    final Pointer interfacePointer = p.getValue();
    final Pointer vTablePointer = interfacePointer.getPointer(0);
    final Pointer[] vTable = new Pointer[3];
    vTablePointer.read(0, vTable, 0, 3);
    return new IUnknown() {
        public int QueryInterface(Guid riid, PointerByReference ppvObject) {
            Function f = Function.getFunction(vTable[0], Function.ALT_CONVENTION);
            return f.invokeInt(new Object[] { interfacePointer, riid, ppvObject });
        }
        public int AddRef() {
            Function f = Function.getFunction(vTable[1], Function.ALT_CONVENTION);
            return f.invokeInt(new Object[] { interfacePointer });
        }
        public int Release() {
            Function f = Function.getFunction(vTable[2], Function.ALT_CONVENTION);
            return f.invokeInt(new Object[] { interfacePointer });
        }
    };
}

The next step would be to implement IDispatch. If I have that then I can perhaps throw in some Dynamic Proxy magic again to map Java interfaces to IDispatch interfaces using JNA in the background.

Edit: I’m adding more code here as requested by Jarvichi

WinApiException looks like this:

/**
 * Exception generated because a Windows API function failed with an error code.
 *  
 * @author Hannes de Jager
 * @since 11 Apr 2011
 */
public class WinApiException extends RuntimeException {

	private int errorCode;
	
	public WinApiException() {
		this(WindowsJna.KERNEL32.get().GetLastError());
	}
	
	public WinApiException(int code) {
		super("Error "+code+": "+formatMessageFromHR(code));
		this.errorCode = code;
	}
	
	public int getErrorCode() {
		return errorCode;
	}
	
	public static String formatMessageFromHR(int code) {
		PointerByReference buffer = new PointerByReference();       
		if (0 == WindowsJna.KERNEL32.get().FormatMessage(0x00000100	| 0x00001000 | 0x00000200,	null, code,	0, buffer, 0, null)) {
			return "";
		}         
		String s = buffer.getValue().getString(0, ! Boolean.getBoolean("w32.ascii"));
		s = s.replace(".\r",".").replace(".\n",".");
		WindowsJna.KERNEL32.get().LocalFree(buffer.getValue());
		return s;      
	}
	
	public static IOException asNewIoException() {
		return new IOException(new WinApiException().getMessage());
	}
	
}

IID is my own custom annotation. It looks like this:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface IId {
  public String value();
}

Also this class here contains the code for OLE32. (Where you will find Ole32.CLSCTX_INPROC):

/**
 * JNA Interface to the Windows API functions in Ole32.dll which implements the COM library functions.
 *  
 * @author Hannes de Jager - With some help from http://code.google.com/p/bridj/
 * @since 2011-11-10
 */
public interface Ole32 extends StdCallLibrary {

	public static final int
      CLSCTX_INPROC_SERVER            = 0x1,
      CLSCTX_INPROC_HANDLER           = 0x2,
      CLSCTX_LOCAL_SERVER             = 0x4,
      CLSCTX_INPROC_SERVER16          = 0x8,
      CLSCTX_REMOTE_SERVER            = 0x10,
      CLSCTX_INPROC_HANDLER16         = 0x20,
      CLSCTX_RESERVED1                = 0x40,
      CLSCTX_RESERVED2                = 0x80,
      CLSCTX_RESERVED3                = 0x100,
      CLSCTX_RESERVED4                = 0x200,
      CLSCTX_NO_CODE_DOWNLOAD         = 0x400,
      CLSCTX_RESERVED5                = 0x800,
      CLSCTX_NO_CUSTOM_MARSHAL        = 0x1000,
      CLSCTX_ENABLE_CODE_DOWNLOAD     = 0x2000,
      CLSCTX_NO_FAILURE_LOG           = 0x4000,
      CLSCTX_DISABLE_AAA              = 0x8000,
      CLSCTX_ENABLE_AAA               = 0x10000,
      CLSCTX_FROM_DEFAULT_CONTEXT     = 0x20000,
      CLSCTX_ACTIVATE_32_BIT_SERVER   = 0x40000,
      CLSCTX_ACTIVATE_64_BIT_SERVER   = 0x80000,
      CLSCTX_ENABLE_CLOAKING          = 0x100000,
      CLSCTX_PS_DLL                   = 0x80000000;

    public static final int
      CLSCTX_INPROC           = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER,
      CLSCTX_ALL              = (CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER),
      CLSCTX_SERVER           = (CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER);

    public static final int 
      S_OK                   = 0,
      S_FALSE                = 1,
      REGDB_E_CLASSNOTREG    = 0x80040154,
      CLASS_E_NOAGGREGATION  = 0x80040110,
      CO_E_NOTINITIALIZED    = 0x800401F0;

    public static final int E_UNEXPECTED      = 0x8000FFFF;
    public static final int E_NOTIMPL         = 0x80004001;
    public static final int E_OUTOFMEMORY     = 0x8007000E;
    public static final int E_INVALIDARG      = 0x80070057;
    public static final int E_NOINTERFACE     = 0x80004002;
    public static final int E_POINTER         = 0x80004003;
    public static final int E_HANDLE          = 0x80070006;
    public static final int E_ABORT           = 0x80004004;
    public static final int E_FAIL            = 0x80004005;
    public static final int E_ACCESSDENIED    = 0x80070005;

    public static final int DISP_E_BADVARTYPE = -2147352568;
    public static final int DISP_E_NOTACOLLECTION = -2147352559;
    public static final int DISP_E_MEMBERNOTFOUND = -2147352573;
    public static final int DISP_E_ARRAYISLOCKED = -2147352563;
    public static final int DISP_E_EXCEPTION = -2147352567;
    public static final int DISP_E_TYPEMISMATCH = -2147352571;
    public static final int DISP_E_BADINDEX = -2147352565;
    public static final int DISP_E_BADCALLEE = -2147352560;
    public static final int DISP_E_OVERFLOW = -2147352566;
    public static final int DISP_E_UNKNOWNINTERFACE = -2147352575;
    public static final int DISP_E_DIVBYZERO = -2147352558;
    public static final int DISP_E_UNKNOWNLCID = -2147352564;
    public static final int DISP_E_PARAMNOTOPTIONAL = -2147352561;
    public static final int DISP_E_PARAMNOTFOUND = -2147352572;
    public static final int DISP_E_BADPARAMCOUNT = -2147352562;
    public static final int DISP_E_BUFFERTOOSMALL = -2147352557;
    public static final int DISP_E_UNKNOWNNAME = -2147352570;
    public static final int DISP_E_NONAMEDARGS = -2147352569;
    
    public static final int COINIT_APARTMENTTHREADED  = 0x2;      // Apartment model
    public static final int COINIT_MULTITHREADED      = 0x0;      // OLE calls objects on any thread.
    public static final int COINIT_DISABLE_OLE1DDE    = 0x4;      // Don't use DDE for Ole1 support.
    public static final int COINIT_SPEED_OVER_MEMORY  = 0x8;
    
    
    public HResult CoCreateGuid(Pointer pointer);
    public HResult CoInitialize(Pointer pvReserved);
    public HResult CoInitializeEx(Pointer pvReserved, int dwCoInit);
    public HResult CoUninitialize();
    HResult CoCreateInstance(
    		  Guid rclsid,
    		  Pointer pUnkOuter,
    		  int dwClsContext,
    		  Guid riid,
    		  PointerByReference ppv
    		);
    HResult CoGetClassObject(
    		  Guid rclsid,
    		  int dwClsContext,
    		  Pointer pServerInfo, //__in_opt  COSERVERINFO *pServerInfo
    		  Guid riid,
    		  PointerByReference ppv
    		);
    
    HResult CLSIDFromProgID(String lpszProgID, Guid lpclsid);
    HResult ProgIDFromCLSID(Guid clsid, Pointer lplpszProgID);
    
    HResult CoMarshalInterThreadInterfaceInStream(
    		  Guid riid,
    		  Pointer pUnk, // __in   LPUNKNOWN pUnk
    		  PointerByReference ppStm //__out  LPSTREAM *ppStm
    		);
    HResult CoGetInterfaceAndReleaseStream(
    		  Pointer pStm, //__in   LPSTREAM
    		  Guid iid,
    		  PointerByReference ppv //__out  LPVOID *ppv
    		);
    
    
    
    void CoTaskMemFree(Pointer pv);

    Pointer CoTaskMemAlloc(int cb);

    
}
Tags: , ,
Posted in Java by Hannes de Jager. 12 Comments

Data objects for the lazy: A function to auto implement a Java interface

One of the things I like about a language like Scala is how little you have to type to get a data class with getters and setters for each field. Now in Java this is a bit more painful:

class Person {
	
	private String name;
	private String surname;
	private int age;
	
	public String getName() {
		return name;
	}
	
	public String getSurname() {
		return surname;
	}
	
	public int getAge() {
		return age;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public void setSurname(String surname) {
		this.surname = surname;
	}
	
	public void setAge(int age) {
		this.age = age;
	}
}

All I wanted to say here is that I want a container that stores read/write values for the name, surname and age of a person. Why do I have to type so much? Hmmm, perhaps I can say it a like so:

interface Person {
	public String getName();
	public void setName(String name);
	public String getSurname();
	public void setSurname(String surname);
	public int getAge();
	public void setAge(int age);
}

Brilliant, except that I can’t store anything in it. Its like having an bright idea with no implementation. But I’ve stated what I want did I not, why can’t the compiler fill in the details for me? Is there not a magic wand method that I can pass this interface to and get an implementation back or can’t I have someone type the details out for me? But even if someone over eager typed it out for me then I will have the clutter of the implementation which is just saying again what I already asked the compiler for.

Hmm I just want a container. Perhaps I should just use a map?

		Map<String, Object> person = new HashMap<String, Object>();
		person.put("name", "Bob");
		person.put("surname","Dylan");
		person.put("age", 23);

Hey if this was Javascript I could use a map like an object:

   person.name = "Bob";
   person.surname = "Dylan";
   person.age = 23;

This makes sense because isn’t an object doing the same thing as a map conceptually i.e. mapping a string (the field name) to a value? But unfortunately this is not how Java sees things. Hey but what if we blend the concept of a HashMap and a Java interface in a Dynamic Proxy blender to give us a magic wand? Hmm:

class LazyProgrammerToolBox {

	/**
	 * Creates an object that implements the specified interface
   * and whose data values associated with getter/setter
   * methods are stored in a map.
	 *
	 * @param <T>
	 *          The interface type
	 * @param interfaceClass
	 *          The interface class
	 * @return A new instance of type T
	 */
	public static <T> T getMapBackedImplementation(Class<T> interfaceClass) {
    int c = interfaceClass.getDeclaredMethods().length;
		return getMapBackedImplementation(interfaceClass,
				new HashMap<String, Object>(c));
	}

	/**
	 * Creates an object that implements the specified interface
   * and whose data values associated with getter/setter
   * methods are stored in the specified map.
	 *
	 * @param <T>
	 *          The interface type
	 * @param interfaceClass
	 *          The interface class
	 * @param container
	 *          The map where the properties will be stored.
	 * @return A new instance of type T
	 */
	public static <T> T getMapBackedImplementation(Class<T> interfaceClass,
			final Map<String, Object> container) {
		InvocationHandler handler;
		if (container == null) {
			// Create a black-hole handler...
			handler = new InvocationHandler() {
				public Object invoke(Object proxy, Method method, Object[] args)
						throws Throwable {
					return method.getReturnType().cast(null);
				}
			};
		} else {
			handler = new InvocationHandler() {

				public Object invoke(Object proxy, Method method, Object[] args)
						throws Throwable {
					if (method.getReturnType() != void.class) {
						String s = method.getName();
						if (s.equals("toString"))
							return container.toString();
						boolean g = s.startsWith("get");
						boolean i = !g || s.startsWith("is");
						s = g ? s.replaceFirst("get", "") 
								  : i ? s.replaceFirst("is", "")
								  : s;
						if (g || i) {
							s = Character.toLowerCase(
									s.charAt(0)) + s.substring(1);
						}
						return container.get(s);
					} else {
						if (args.length > 0) {
							String s = method.getName();
							boolean set = s.startsWith("set");
							if (set) {
								s = s.replaceFirst("set", "");
								s = Character.toLowerCase(
										s.charAt(0)) + s.substring(1);
							}
							container.put(s, args[0]);
						}
						return null;
					}
				}
			};
		}

		return interfaceClass.cast(Proxy.newProxyInstance(
				interfaceClass.getClassLoader(), 
				new Class[] { interfaceClass },
				handler));
	}

} 

Lets see how do I use this wand:

  Person p = LazyProgrammerToolBox.getMapBackedImplementation(Person.class);
  p.setName("Bob");
  p.setSurname("Dylan");
  p.setAge(23);

Aah, thats better :-)

Tags:
Posted in Java by Hannes de Jager. 2 Comments

Java: Transforming an Iterator over X into an Iterator over Y

I recently got exited about some new uses for an old design pattern, the Iterator. The implementation of Generics that we have since Java 5 stimulated this idea: To take an iterator over some data set and convert it into an iterator over that same data set, but that yields an interpreted value on each iteration. This is accomplished by using another design pattern that I power use these days dubbed the Transform. A transform is a method that takes an input and produces a transformed output value. See the Txf interface below.

The code below shows the basic concept. It takes an iterator over file names and converts it to an iterator over file sizes (the sizes of those files)

class Iterx {

 public interface Txf<I,O> {
   /** Takes some input and produces a transformed/interpreted output value. */
   public O transform(I input);
 } 

 /** Changes/transforms the iteration variable of the supplied iterator according to the supplied transformation */
 public static <I,O> Iterator<O> transform(final Iterator<I> iter, final Txf<I, O> txf) {
  return new Iterator<O>() {
   @Override
   public boolean hasNext() {
    return iter.hasNext();
   }
   @Override
   public O next() {
    return txf.transform(iter.next());
   }
   @Override
   public void remove() {
    iter.remove();
   }
  };
 }

 /** Converts an iterator to an iterable so that it can be used in for each loops. */
 public static <T> Iterable<T> from(final Iterator<T> itr) {
  return new Iterable<T>() {
   @Override
   public Iterator<T> iterator() {
    return itr;
   }
  };
 }

 public static void main(String[] args) {
  List<String> s = new ArrayList<String>();
  s.add("a.txt");
  s.add("b.txt");
  s.add("c.txt");
  Iterator<Long> sizeIter = Iterx.transform(s.iterator(), new Txf<String, Long>() {
   @Override
   public Long transform(String input) {
    return new File(input).length();
   }
  });

  long totalSize = 0;
  for (long fSize : Iterx.from(sizeIter)) {
   totalSize += fSize;
  }
  System.out.println("Total File Size: " + totalSize);
 }
}

From this concept I was able to build a library that allows me to write code like the following:

Iterx
    .from(myIter)
    .select(someTransformation)
    .where(someCondition);
Tags: , ,
Posted in Java by Hannes de Jager. 2 Comments