Tuesday, June 5, 2012

Generating sender-reciever pairs from a list of names (Secret Santa)

A colleague asked me to create a program, to generate sender-reciever pairs from a list of names, for a game we are playing in the office (resembles "Secret Santa"). Each person should be a sender and a reciever.
Since the colleague is not a develpoer and works with Windows I chose to implement it in JavaScript, so all she'd need is to open a browser.

You can see the code below and give it a try here.



<html> <head> <script type="text/javascript"> function makePairs() { var namesTextArea = document.getElementsByName("listOfNames")[0].value; var names = namesTextArea.split('\n'); if ( names.length % 2 != 0 ) { alert("there is an odd number of names, please make sure the number of names is even"); return false; } var html = ""; var senders = []; var recipients = []; while ( names.length > 0 ) { var first_index = Math.random() * names.length; sender = names.splice(first_index, 1); var second_index = Math.random() * names.length; recipient = names.splice(second_index, 1); html += sender + ":" + recipient + "\n"; senders.push( sender ); recipients.push( recipient ); } // make each recipient a sender and vice versa while ( senders.length > 0 ) { var sender_index = Math.random() * senders.length; newRecipient = senders.splice(sender_index, 1); var recipient_index = Math.random() * recipients.length; newSender = recipients.splice(second_index, 1); html += newSender + ":" + newRecipient + "\n"; } document.getElementsByName("pairs")[0].value = html; return false; } </script> </head> <body> <table> <tr> <td valign="top">Put a list with an even number of names here:</td> <td valign="top"><textarea id="listOfNames" name="listOfNames" cols="30" rows="50"></textarea></td> <td valign="top"><input type="button" value="Create pairs" onclick="javascript:return makePairs()"></td> <td valign="top"><textarea id="pairs" name="pairs" cols="30" rows="50"></textarea></td> </tr> </table> </body> </html>


Monday, March 5, 2012

Setting up a multi tenant environment using the Spring Framework

In this post I'll describe how to set up a multi tenant application using the Spring Framework (I am using Spring 3).
I'll use a pure Java application, but the concepts work well in enterprise apps.

The requirements are simple:
  • Single application to serve all the tenants
  • Every tenant uses the same wiring, each tenant has different properties 
  • Have common properties and wiring that can be shared by all the tenants
    • Allow each tenant to override the common settings
The solution:
  • Create a Spring ApplicationContext for every tenant
    • Set the PropertyConfigurer in runtime
Let's see this in action:

public class MultiTenantSpringExample {
 
 private ClassPathXmlApplicationContext commonCtx; // use ClassPathXmlApplicationContext instead of ApplicationContext so we can destory them
 private List tenantContexts;

 public void init( List tenants ) {

  tenantContexts = new ArrayList( tenants.size() );
  // create a common application context, shared among all the tenants
  commonCtx = new ClassPathXmlApplicationContext( "/commonContext.xml" );
  // set up all the tenants
  for ( String tenant : tenants ) {
   // for each tenant create a Spring ApplicationContext
   ClassPathXmlApplicationContext tenantCtx = new ClassPathXmlApplicationContext();
   tenantCtx.setParent( commonCtx );
   tenantCtx.setConfigLocation( "/tenantContext.xml" );
   TenantPropertyPlaceholderConfigurer beanFactoryPostProcessor = new TenantPropertyPlaceholderConfigurer( tenant );
   tenantCtx.addBeanFactoryPostProcessor( beanFactoryPostProcessor );
   tenantCtx.refresh();
   tenantContexts.add( tenantCtx );
  }
 }
 
 public void destroy() {
  // destroy the tenant contexts
  for ( ClassPathXmlApplicationContext tenantContext : tenantContexts ) {
   tenantContext.destroy();
  }
  tenantContexts.clear();
  // destroy the common context
  commonCtx.destroy();
 }
 
 public static void main( String[] args ) {
  MultiTenantSpringExample example = new MultiTenantSpringExample();
  example.init( Arrays.asList( "a", "b" ) );
  example.destroy();
 }
}

On line 18 I create the common wiring, that is shared among tenants, and set it as the parent for the tenant application context on line 23.
On line 25-27 I inject the PropertyPlaceholderConfigurer, which is created differently for every tenant.

public class TenantPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
 public TenantPropertyPlaceholderConfigurer( String tenant ) {
  super();
  setIgnoreResourceNotFound( true ); // this makes the common file and tenant file optional
  // prepare the default properties
  String defaultPropertiesResourcePath = "/global.properties";

  Resource defaultPropertiesResource = new ClassPathResource( defaultPropertiesResourcePath );
  // prepare tenant properties
  String tenantPropertiesResourcePath = '/' + tenant + ".properties";
  Resource tenantPropertiesResource = new ClassPathResource( tenantPropertiesResourcePath );
  // set the locations
  Resource[] locations = new Resource[] { defaultPropertiesResource, tenantPropertiesResource };
  setLocations( locations );
 }
}


The TenantPropertyPlaceholderConfigurer uses classpath resources, using a common properties file, shared for all the tenants, and a per-tenant properties file.

To complete the example I created a simple class, A, holding an int, and printing the int in the print() method.
public class A {
 private int i;
 
 public void setI( int i ) {
  this.i = i;
 }
 
 public int getI() {
  return i;
 }
 
 public void print() {
  System.out.println( "Example property: " + i );
 }
}


And the resource files: commonContext.xml, empty in the example but can be used for sharing wiring among tenants
<beans>
</beans>


tenantContext.xml, creates an instance of A for every tenant, each one with different property values, and calls the print() method after constructing the object to print the value.
<beans> 
 <bean class="A" id="a" init-method="print">
  <property name="i" value="${tenant-i}">
 </property></bean>
</beans>


And two matching properties files: a.properties
tenant-i = 1

b.properties
tenant-i = 2

When executing the 'main' method the program outputs "1" for tenant 'a' and "2" for tenant 'b'

Tuesday, February 21, 2012

Perforce and diff emails

After working with Subversion, and getting used to the colorful post-commit diff email, I wanted to set up a similar setting in Perforce.
Unfortunately I couldn't find post-commit hooks in Perforce.
So I came up with the following steps to generate the diff on the client side:
  1. Create a "Review" trigger in Perforce for the branches you want to monitor.
    In the visual Perforce client click "Connection" > "Edit current user" > "Reviews" tab, now right click and "Include" every branch you want to be notified about.
    This sends an email about every changelist submitted to Perforce, without specifying the changelist content
  2. Get the jar, or source code, that creates the HTML diff.

    I am aware that there are tools to convert diff 2 HTML, but since my team is using Java (thus they have a JRE), mostly on Windows (so no Python), I wanted a Java convertor.
     
  3. Use a VBA script in Outlook to convert the triggered email to a diff of the changelist content.
    The script fires up the local Perforce client and uses the command "p4 describe " to create the diff, uses the Java class to convert the diff to HTML and replaces the email body with the HTML diff.
    Open the script below in a text editor:

    Sub P4Diff(MyMail As MailItem)
    
        Dim strSplit As Variant
        Dim changelist As String
        Dim sOutput As String
        Dim sOutputErr As String
        Dim sP4Cmd As String
        Dim sJavaCmd As String
        Dim sJarPath As String
        Dim sP4Port As String
        Dim sP4User As String
        Dim sP4Password As String
    
        ' error handling directive
        On Error GoTo errMyErrorHandler
    
        ' Variables that need to be set per environment
        sP4Cmd = "C:\Progra~1\Perforce\p4.exe"
        sJavaCmd = "C:\Java\jre6\bin\java.exe"
     sJarPath = "C:\SomePath\p4diff.jar"
     sP4Port = "yourperforce:1667"
        sP4User = "youruser"
        sP4Password = "yourpassword"
        
        ' parse the changelist from the email subject
        strSplit = Split(MyMail.Subject, " ")
        changelist = strSplit(2)
        
        ' create a shell
        Set wshShell = VBA.CreateObject("WScript.Shell")
        ' set the p4 environment variables
        Set processEnvVars = wshShell.Environment("PROCESS")
        processEnvVars("P4PORT") = sP4Port
        processEnvVars("P4USER") = sP4User
        processEnvVars("P4PASSWD") = sP4Password
        ' execute the diff and wait for it to finish
        Set oExec = wshShell.Exec("%COMSPEC% /c " & sP4Cmd & " describe " & changelist & " | " & sJavaCmd & " -jar " & sJarPath)
        Do While oExec.Status = WshRunning
            If oExec.StdOut.AtEndOfStream = 0 Then
                sOutput = sOutput & oExec.StdOut.ReadLine()
            End If
        Loop
    
        If oExec.StdOut.AtEndOfStream = 0 Then
            sOutput = sOutput & oExec.StdOut.ReadLine()
        End If
        
        ' Read the diff result and set in the email body
        sOutputErr = oExec.StdErr.ReadAll()
        MyMail.HTMLBody = sOutput & sOutputErr
        MyMail.Save
    
        Exit Sub
    
    errMyErrorHandler:
      MsgBox Err.Description, _
        vbExclamation + vbOKCancel, _
        "Error: " & CStr(Err.Number)
    
    End Sub
    

    Change the script variables to match your environment.

    Install the VBA script:
    1.  In Outlook click "Tools" > "Macro" > "Visual Basic Editor"
    2. Open "ThisOutlookSession"
    3. Paste the edited script from your text editor into the VB editor and save
    4. Close the VBA editor and text editor

    Change the Outlook Macro security so it will be able to run the script

  4. Create a rule in Outlook to run the script when the email subject contains "PERFORCE change".
    For some reason this doesn't work well if you run the script AND move the email to a different folder