Switching easily between Java JDKs on Mac OS X

I just came upon a neat little trick to easily switch what JDK you are using on the command line on Mac OS X (10.9.2 is the version I am using), courtesy of this AskDifferent answer.

First, add the following aliases in ~/.profile file:

alias setJdk6='export JAVA_HOME=$(/usr/libexec/java_home -v 1.6)'
alias setJdk7='export JAVA_HOME=$(/usr/libexec/java_home -v 1.7)'
alias setJdk8='export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)'

Open a new terminal window or use source ~/.profile to load the aliases.

Now you can easily switch between the installed JDKs:

> setJdk6

We can verify that Maven picks this up correctly:

> mvn --version
Apache Maven 3.0.4 (r1232337; 2012-01-17 09:44:56+0100)
Maven home: /Applications/maven/apache-maven-3.0.4
Java version: 1.6.0_65, vendor: Apple Inc.
Java home: /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
Default locale: en_US, platform encoding: MacRoman
OS name: "mac os x", version: "10.9.2", arch: "x86_64", family: "mac"

To go to JDK 7:

> setJdk7

And Maven shows:

> mvn --version
Apache Maven 3.0.4 (r1232337; 2012-01-17 09:44:56+0100)
Maven home: /Applications/maven/apache-maven-3.0.4
Java version: 1.7.0_45, vendor: Oracle Corporation
Java home: /Library/Java/JavaVirtualMachines/jdk1.7.0_45.jdk/Contents/Home/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "mac os x", version: "10.9.2", arch: "x86_64", family: "mac"

This works just as well to switch to the just released JDK 8:

> setJdk8

Maven output:

> mvn --version
Apache Maven 3.0.4 (r1232337; 2012-01-17 09:44:56+0100)
Maven home: /Applications/maven/apache-maven-3.0.4
Java version: 1.8.0, vendor: Oracle Corporation
Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "mac os x", version: "10.9.2", arch: "x86_64", family: "mac"

Voila, simpel and effective!

 

 

Posted in Programming | Tagged , , , | Leave a comment

Synchronizing LiquidPlanner with Exchange calendars

Quite some time ago I wrote Groovy code to synchronize Atlassian JIRA with LiquidPlanner. You can find my 2 part blog post on that here and here. In case you don’t know LiquidPlanner, be sure to check it out, it is the best project management/scheduling tool I know.

One thing that was missing is the integration of the holidays into LiquidPlanner. Since the holidays are already in everybody’s Outlook Calendar, I could get the needed information from there.
At first, I was afraid that it would be difficult to do from Groovy/Java, but it turns out if your Microsoft Exchange version is fairly recent, there is a very nice Java library from Microsoft itself you can use. It is called the EWS Java API which stands for Exchange Web Services Java API. Basically, it is a Java library that uses the Web Services API from Exchange, a perfect fit for what I needed to do.

Step 1. Connect to Exchange and get appointments

To connect to exchange we just need the credentials of a domain user. The auto discovery mechanism will automatically find the Exchange server:

private static ExchangeService createExchangeService( LiquidPlannerToolsConfiguration configuration )
{
	logger.info( "Connecting to Exchange..." )
	ExchangeService service = new ExchangeService( ExchangeVersion.Exchange2010_SP2 )
	ExchangeCredentials credentials = new WebCredentials( configuration.exchangeUser, 
								configuration.exchangePassword, 
								configuration.exchangeDomain )
	service.setCredentials( credentials )
	service.autodiscoverUrl( configuration.exchangeAutodiscoverEmail )
	return service
}

The LiquidPlannerToolsConfiguration class is a simple POJO that contains the configuration parameters for the script. Now that we are connected, we can query Exchange for the calendar of a user:

private static def getAppointments( ExchangeService service, String emailAddress )
{
	def Mailbox mailbox = new Mailbox( emailAddress )
	def folderId = new FolderId( WellKnownFolderName.Calendar, mailbox )

	def startDate = DateTime.now().toDate()
	def endDate = DateTime.now().plusMonths( 6 ).toDate()

	def allAppointments = service.findAppointments( folderId, new CalendarView( startDate, endDate ) )
	def relevantAppointments = allAppointments.findAll { Appointment appointment -> appointment.duration.hours >= 4 }
	logger.debug "Found ${allAppointments.items.size()} appointments of which ${relevantAppointments.size()} are relevant"
	return relevantAppointments
}

With the ExchangeService and the email address of a user, we ask for all the appointments in the coming 6 months. Using Groovy’s findAll() method, I filter out appointments that are shorter than 4 hours. I only want to put half days and days that people are not in the office into LiquidPlanner.

Step 2. Create appointments in LiquidPlanner

Now that I have all the relevant appointments, I need to create an entry for each of those in LiquidPlanner:

private static def createAppointmentInLP( LiquidPlannerService liquidPlannerService,
					Appointment appointment,
					LiquidPlannerUserVacationPackage userVacationPackage )
{
	liquidPlannerService.createAppointment( userVacationPackage,
						getAppointmentIdForExternalReference( appointment ),
						appointment.subject,
						new DateTime( appointment.start, getTimeZone( appointment.startTimeZone ) ),
						new DateTime( appointment.end, getTimeZone( appointment.endTimeZone ) ) )
}

private static String getAppointmentIdForExternalReference( Appointment appointment )
{
	// LiquidPlanner has trouble matching the id afterwards if there is a '+' in there, so we replace it with something else to work around it.
	return appointment.id.uniqueId.replace( '+', "_" )
}

Everything in LiquidPlanner has a field ‘External Reference’ which is great if you have to integrate with external systems. Here, I use this field to put in the unique id that exchange associates with each calendar entry (You would be amazed that there are enough unique id’s in the world for all the meetings). I can use this id later to know if an appointment is already created in LiquidPlanner or not.

Note: I had issues with putting in the unique id from Exchange in the External Reference field of LiquidPlanner. I could not get back entries if the reference contained a plus (+) sign, so I replace it with an underscore just to avoid that problem.

The LiquidPlannerService hides all the HTTP/JSON stuff to interact with the LiquidPlanner API. This is the createAppoinment method:

public void createAppointment( LiquidPlannerUserVacationPackage userVacationPackage, String exchangeAppointmentId, String subject, DateTime startDateTime, DateTime endDateTime )
	{
		String requestPath = 'workspaces/' + LP_WORKSPACE_ID + '/events/';
		liquidPlanner.request( Method.POST, ContentType.JSON ) { req ->
			uri.path = requestPath;

			def startTime = LP_DATE_FORMATTER.print( startDateTime )
			def endTime = LP_DATE_FORMATTER.print( endDateTime )
			logger.debug "$startTime - $endTime : ${subject}"
			body = [event: [
					name: subject,
					owner_id: userVacationPackage.ownerId,
					parent_id: userVacationPackage.id,
					external_reference: exchangeAppointmentId,
					start_date: startTime,
					finish_date: endTime]]

			response.success = { resp, json ->
				logger.debug "Succesfully created appointment in LP"
			}
			response.failure = { resp ->
				throw new RuntimeException( "Unable to create appointment in LP: ${resp.status} - ${requestPath}" )
			}
		}

		Thread.sleep( m_sleepBetweenLPRequests );
		// Sleep a bit to avoid hitting the liquidplanner server too fast (See http://www.liquidplanner.com/api-guide/technical-reference/request-throttling.html)

	}

The liquidPlanner is a Groovy HTTP Builder object. The LiquidPlannerUserVacationPackage represents a vacation in the bounded context of LiquidPlanner (I had just finished reading Implementing Domain-Driven Design when I implemented this, and incorporating some of the ideas really made my code a lot better):

import net.sf.json.JSONObject

class LiquidPlannerUserVacationPackage
{
	String id
	String ownerId
	String name
	String emailAddress

	public static LiquidPlannerUserVacationPackage fromJSON( JSONObject jsonObject )
	{
		return new LiquidPlannerUserVacationPackage(
				id: jsonObject.id,
				ownerId: jsonObject.owner_id,
				name: jsonObject.name,
				emailAddress: jsonObject.external_reference )
	}
}

Step 3. Prepare LiquidPlanner so the script has enough information

To make all of this work, there is some preparation in LiquidPlanner needed.
First, you need to create a top-level package that will have all the vacations. Below that, I create a package per user that is in LiquidPlanner. There are a lot more users in Exchange than there are people using LiquidPlanner, so it makes no sense to try to autogenerate this from Exchange in our case.
Each ‘user’ package will have the email address of the person set as ‘External Reference’. The script will use that to connect to Exchange to get the appointments of each user.
Note that all the users will need to have shared their calendar with the user you use to connect to Exchange initially, otherwise, it cannot work!

This is the code that retrieves all the LiquidPlanner packages (1 per user):

public Set<LiquidPlannerUserVacationPackage> getVacationPackages()
{
	def JSONObject outlookCalendarFolder = liquidPlanner.get( path: 'workspaces/' + LP_WORKSPACE_ID + '/packages', query: ['filter[]': ['name="Vacations"']] );
	logger.debug "Outlook calendars package found under id " + outlookCalendarFolder.id

	def JSONArray userFolders = liquidPlanner.get( path: 'workspaces/' + LP_WORKSPACE_ID + "/treeitems/" + outlookCalendarFolder.id, query: ['depth': '1'] ).children
	return userFolders.findAll {
		!(it.external_reference instanceof JSONNull) && isNotBlank( it.external_reference )
	}.collect {
		LiquidPlannerUserVacationPackage.fromJSON( it )
	}
}

What this does is first searching for a package called ‘Vacations’. Then it takes all the children at the first depth level, which are our user packages. The returned JSON is then converted into LiquidPlannerUserVacationPackage so that the rest of the script does not need to know that we are using a JSON REST API to talk to LiquidPlanner.

To check if an appointment already exists in LiquidPlanner, we need this piece of code:

public boolean doesAppointmentExist( String exchangeAppointmentId )
{
	def queryFilter = 'external_reference="' + exchangeAppointmentId + '"'
	JSONArray appointmentInLP = liquidPlanner.get( path: 'workspaces/' + LP_WORKSPACE_ID + '/events', query: ['filter[]': [queryFilter]] );

	def result = appointmentInLP.size() > 0
	if (!result)
	{
		logger.debug "Could not find appointment with id ${exchangeAppointmentId} in LP"
	}
	return result;
}

Notice how we can directly get the event in LiquidPlanner with the matching external reference. I use this to avoid creating new entries in LiquidPlanner for appointments that already exist.

This is it. This post has showed you the most important bits and pieces to synchronize LiquidPlanner with calendars in Microsoft Exchange.

Posted in Programming | Tagged , , | Leave a comment

Creating a REST web application in 4 classes

After visiting Spring Exchange in London, I wanted to try Spring Boot and Spring Data. I managed to it this weekend and was quite impressed.

First, I needed an entity class:

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class SensorEndpoint
{
	@Id
	@GeneratedValue()
	private long id;
	private String name;
	private String endpointUrl;

	// for hibernate
	public SensorEndpoint()
	{
	}

	public SensorEndpoint( String name, String endpointUrl )
	{
		this.name = name;
		this.endpointUrl = endpointUrl;
	}

        // Getters and Setters omitted
}

My entity just has an id which will be used as primary key in the database, a descriptive name and an other String (purpose of this one is not important now).

Now I want a Repository to store and retrieve these objects in the database. Spring Data makes this super easy. I only have to create an interface, implementation is done automatically by Spring Data:

import org.springframework.data.repository.CrudRepository;

import java.util.List;

public interface SensorEndpointRepository extends CrudRepository<SensorEndpoint,Long>
{
	List<SensorEndpoint> findByName(String name);
}

This is all that needs to be done. The ‘findByName’ method is optional. The CrudRepository already allows the most important operations like getting all, getting one by primary key, saving one, …

Now we need to expose this over HTTP REST API. For this we create a Controller:

import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@RestController
public class HelloController
{
	@Autowired
	private SensorEndpointRepository m_repository;

	@RequestMapping("/")
	public String index()
	{
		return "Greetings from Spring Boot!";
	}

	@RequestMapping(value="/endpoints", method = RequestMethod.GET)
	public List<SensorEndpoint> getAllEndpoints()
	{
		Iterable<SensorEndpoint> all = m_repository.findAll();
		return Lists.newArrayList( all );
	}

	@RequestMapping(value="/endpoints/{id}", method = RequestMethod.GET)
	public SensorEndpoint getEndpoint( @PathVariable("id") long id )
	{
		return m_repository.findOne( id );
	}

	@RequestMapping(value="/endpoints/add", method=RequestMethod.POST)
	public SensorEndpoint addEndpoint( @RequestParam("name") String name,
									   @RequestParam("url") String endpointUrl )
	{
		return m_repository.save( new SensorEndpoint( name, endpointUrl ) );
	}
}

The controller is annotated with @RestController to tell Spring that this class is a web controller for REST. Using the @RequestMapping, @PathVariable and @RequestParam annoations makes it great to define the URLs in a very simple way. With this controller the following URLs can be hit:

* http://localhost:8080/endpoints -> Will return all the objects in the repository in JSON
* http://localhost:8080/endpoints/1 -> Will return the object with primary key 1 in JSON
* http://localhost:8080/endpoints/add -> if you do a POST with a ‘name’ and ‘url’ request parameters, it will be added to the database.

Note how I can just return the object or a list of objects and it gets marshalled into JSON automatically.

The last class we need is to start the application:

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jmx.export.annotation.AnnotationMBeanExporter;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.util.Arrays;

import static org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType.H2;

@Configuration
@EnableAutoConfiguration
@ComponentScan
@EnableJpaRepositories
public class Application
{
	@Bean
	public DataSource dataSource()
	{
		return new EmbeddedDatabaseBuilder().setType( H2 ).build();
	}

	@Bean
	public LocalContainerEntityManagerFactoryBean entityManagerFactory( DataSource dataSource, JpaVendorAdapter jpaVendorAdapter )
	{
		LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
		lef.setDataSource( dataSource );
		lef.setJpaVendorAdapter( jpaVendorAdapter );
		lef.setPackagesToScan( "hello" );
		return lef;
	}

	@Bean
	public JpaVendorAdapter jpaVendorAdapter()
	{
		HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
		hibernateJpaVendorAdapter.setShowSql( false );
		hibernateJpaVendorAdapter.setGenerateDdl( true );
		hibernateJpaVendorAdapter.setDatabase( Database.H2 );
		return hibernateJpaVendorAdapter;
	}

	@Bean
	public PlatformTransactionManager transactionManager()
	{
		return new JpaTransactionManager();
	}

	public static void main( String[] args )
	{
		ApplicationContext ctx = SpringApplication.run( Application.class, args );

                // Put in some test data
		SensorEndpointRepository bean = ctx.getBean( SensorEndpointRepository.class );
		bean.save( new SensorEndpoint( "Kortrijk", "http://www.kortrijk.be/api") );
		bean.save( new SensorEndpoint( "Gent", "http://www.gent.be/api") );
	}

}

This uses Spring Boot and Spring java configuration to bootstrap the application. The final piece of the puzzel is the Maven pom.xml with the dependencies (Note that Gradle can also be used, but I am more familiar with Maven):

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.springframework</groupId>
    <artifactId>gs-spring-boot</artifactId>
    <version>0.1.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>0.5.0.M6</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>1.4.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>4.0.0.RC1</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>4.2.1.Final</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.3.172</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>12.0</version>
        </dependency>

    </dependencies>

    <properties>
        <start-class>hello.Application</start-class>
    </properties>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <url>http://repo.spring.io/libs-snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-snapshots</id>
            <url>http://repo.spring.io/libs-snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>
</project>

We depend on 2 Spring Boot starter projects: spring-boot-starter-web and spring-boot-starter-actuator. Next to that we need Spring Data, so we pull in ‘spring-data-jpa’, ‘spring-orm’ and ‘hibernate-entitymanager’. As a database, I use an embedded H2 database. If you want to run this example with MySQL, just import the MySQL driver instead.

To run the project, import the Maven pom in IntelliJ IDEA and run the ‘Application’ class. After that go to one of the URLs I mentioned and you should see the JSON in your browser. The HTTP POST can easily be done from the built-in REST client in IntelliJ.

Posted in Programming | Tagged , | 2 Comments

JIRA Database Values 3.1 released

I have just released the version 3.1 of the JIRA Database values plugin. It fixes the issue that sometimes the AJAX style input did not work in the create issue screen. The plugin is compatible with Atlassian JIRA 6.x.

See https://marketplace.atlassian.com/plugins/org.deblauwe.jira.plugin.database-values-plugin for more info and download.

Posted in Programming | Tagged | Leave a comment

Database Values Plugin upgraded for JIRA 6

I just finished upgrading the JIRA Database Values Plugin for JIRA 6. Download it from https://marketplace.atlassian.com/plugins/org.deblauwe.jira.plugin.database-values-plugin

Posted in Uncategorized | Tagged , | Leave a comment

Upgrading your plugin to JIRA 5.1 presentation slides

Last week, I gave a presentation on upgrading the JIRA Database values plugin to JIRA 5.1 for the Atlassian Belgian User Group. For those that are interested, the slides can be viewed here: https://docs.google.com/presentation/d/100KHFe-oxlVmud6nhSbgdjUaXFO2Jnf7qPcDhZpPGDo/pub?start=false&loop=false&delayms=3000

Posted in Programming | Tagged | Leave a comment

JIRA Database Values Plugin for JIRA 5.1 released!

I just released the new version of the JIRA Database Values Plugin. It is now compatible with JIRA 5.1 and 5.2. Most notable change is that under the hood jQuery is used now instead of Scriptaculous.

See Atlassian Marketplace for the download.

The source code has also been migrated to the excellet Bitbucket service. Sources can be found at https://bitbucket.org/wimdeblauwe/jdvp and the documentation at the embedded wiki: https://bitbucket.org/wimdeblauwe/jdvp/wiki/Home

This version would not have been possible without the people that donated on GoFundMe so a big thank you to them all!

Enjoy it!

Wim

PS: One breaking change is that linking of multiple custom fields is no longer supported. It never worked really well anyway and I had to remove it to support the inline editing capabilities of JIRA 5.1.

Posted in Uncategorized | Tagged , | 3 Comments