Download pulse

A real world controller - download users as an Excel workbook

A simple start

Let's start with a controller which is only slightly more complicated than the HelloWorldController. We will create a Hibernate transaction and load all User entities. Finally the number of users gets written to the log file.

All imports are omitted for brevity, the complete source code of the UserListController can be found at the end of this page.

package org.torweg.pulse.component.example;

// imports omitted for brevity
import ... 

public class UserListController extends Controller {

	private static final Logger LOGGER = LoggerFactory
			.getLogger(UserListController.class);

	@Action("downloadExcel")
	public final void downloadUsersExcel() {
		Session s = Lifecycle.getHibernateDataSource().createNewSession();
		Transaction tx = s.beginTransaction();
		try {
			
			@SuppressWarnings("unchecked")
			List<User> users = s.createCriteria(User.class).list();
			LOGGER.info("{} user(s) found", users.size());
			
			tx.commit();
		} catch (Exception e) {
			tx.rollback();
			throw new PulseException("Error: " + e.getLocalizedMessage(), e);
		} finally {
			s.close();
		}

	}
}

There are probably two noteworthy things about the above code:

  • The use of the Lifecycle singleton: the pulse Lifecyle is a central entry point to many resources such as the Bundles, MailQueue, global JAXBContext, or in our case a Hibernate Session. You should get a hold of its public static methods.
  • The loading of the users which is plain old Hibernate code using the Criteria API and doing correct exception handling which can be used as general template.

Now add the controller to the bundle.xml.

<controllers>
    <controller class="org.torweg.pulse.component.example.HelloWorldController"/>
    <controller class="org.torweg.pulse.component.example.UserListController"/>
</controllers>

Restart the webapp, enter URL in browser and look into log file.

?
The URL would be:
http://localhost/pulse/Pulsar/.Example.downloadExcel../
More information on how the URLs for pulse are built.

Your log file should contain a line similar to this:

2010-09-24 20:04:38 INFO  [0:0:0:0:0:0:0:1] org.torweg.pulse.component.example.
UserListController - 1 user(s) found

Producing a download with pulse

Next thing we will enhance the downloadUsersExcel method to produce an empty Excel workbook as a download before we finally fill in the data.

In order to interact with the browser the method needs to get hold of the ServiceRequest. It encapsulates the communication channel between the client and the server. Getting the ServiceRequest couldn't be easier, simply add it as a method parameter and your done.

@Action("downloadExcel")
public final void downloadUsersExcel(final ServiceRequest request) {
	Session s = Lifecycle.getHibernateDataSource().createNewSession();
	Transaction tx = s.beginTransaction();
	try {

		@SuppressWarnings("unchecked")
		List<User> users = s.createCriteria(User.class).list();
		LOGGER.info("{} user(s) found", users.size());

		// create empty Excel workbook
		Workbook workbook = new XSSFWorkbook();

		// write workbook to buffer
		FastByteArrayOutputStream buffer = new FastByteArrayOutputStream();
		workbook.write(buffer);

		// wrap buffer into a SerializableDataSource
		String filename = "users.xlsx";
		SerializableDataSource dataSource = new SerializableDataSource(
				buffer.getByteArray(), filename, MimeMap.getInstance()
				.getMimeType(filename));
		
		// create a DownloadEvent
		DownloadEvent downloadEvent = new DownloadEvent(dataSource,
				Event.Disposition.ATTACHED, CacheMode.NONE);
		
		// add event to EventManager
		EventManager eventManager = request.getEventManager();
		eventManager.addEvent(downloadEvent);

		tx.commit();
	} catch (Exception e) {
		tx.rollback();
		throw new PulseException("Error: " + e.getLocalizedMessage(), e);
	} finally {
		s.close();
	}
}

The above code still starts by loading the users from Hibernate. Then it creates a new Workbook using Apache POI. The workbook is written to a buffer, gets wrapped by a SerializableDataSource which gets passed to a DownloadEvent. The DownloadElement is added to the ServiceRequest's EventManager which will take care of the output.

Classes to be noted

Take your time to look at the classes and interfaces listed above, especially at the methods and constructors used by the code.

Adding the information to the Excel workbook

Adding the information to the workbook is rather simple and straight forward Apache POI code:

// create empty Excel workbook
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("users");

// add users
for (User user : users) {
	Row row = sheet.createRow(sheet.getLastRowNum() + 1);
	row.createCell(row.getLastCellNum() + 1).setCellValue(user.getId().toString());
	row.createCell(row.getLastCellNum() + 1).setCellValue(user.getName());
	row.createCell(row.getLastCellNum() + 1).setCellValue(user.getEmail());
	if (user.getLastLoginTime() != null) {
		row.createCell(row.getLastCellNum() + 1).setCellValue(
				user.getLastLoginTime().toString());
	}
}

// write workbook to buffer
FastByteArrayOutputStream buffer = new FastByteArrayOutputStream();
workbook.write(buffer);

What we do is, we create a spreadsheet "users" in the empty workbook. The we iterate over the users adding a new row to the sheet for every user. The row is filled with the user's ID, the user name, the user's e-mail address and if available the user's last login time. Then the workbook is written to the buffer.

The complete code

package org.torweg.pulse.component.example;

import java.util.List;

import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.torweg.pulse.accesscontrol.User;
import org.torweg.pulse.annotations.Action;
import org.torweg.pulse.bundle.Controller;
import org.torweg.pulse.invocation.lifecycle.Lifecycle;
import org.torweg.pulse.service.PulseException;
import org.torweg.pulse.service.event.DownloadEvent;
import org.torweg.pulse.service.event.Event;
import org.torweg.pulse.service.event.EventManager;
import org.torweg.pulse.service.request.CacheMode;
import org.torweg.pulse.service.request.ServiceRequest;
import org.torweg.pulse.util.MimeMap;
import org.torweg.pulse.util.io.FastByteArrayOutputStream;
import org.torweg.pulse.util.io.SerializableDataSource;

public class UserListController extends Controller {

	private static final Logger LOGGER = LoggerFactory
			.getLogger(UserListController.class);

	@Action("downloadExcel")
	public final void downloadUsersExcel(final ServiceRequest request) {
		Session s = Lifecycle.getHibernateDataSource().createNewSession();
		Transaction tx = s.beginTransaction();
		try {

			@SuppressWarnings("unchecked")
			List<User> users = s.createCriteria(User.class).list();
			LOGGER.info("{} user(s) found", users.size());

			// create empty Excel workbook
			Workbook workbook = new XSSFWorkbook();
			Sheet sheet = workbook.createSheet("users");

			// add users
			for (User user : users) {
				Row row = sheet.createRow(sheet.getLastRowNum() + 1);
				row.createCell(row.getLastCellNum() + 1).setCellValue(
						user.getId().toString());
				row.createCell(row.getLastCellNum() + 1).setCellValue(
						user.getName());
				row.createCell(row.getLastCellNum() + 1).setCellValue(
						user.getEmail());
				if (user.getLastLoginTime() != null) {
					row.createCell(row.getLastCellNum() + 1).setCellValue(
							user.getLastLoginTime().toString());
				}
			}

			// write workbook to buffer
			FastByteArrayOutputStream buffer = new FastByteArrayOutputStream();
			workbook.write(buffer);

			// wrap buffer into a SerializableDataSource
			String filename = "users.xlsx";
			SerializableDataSource dataSource = new SerializableDataSource(
					buffer.getByteArray(), filename, MimeMap.getInstance()
							.getMimeType(filename));

			// create a DownloadEvent
			DownloadEvent downloadEvent = new DownloadEvent(dataSource,
					Event.Disposition.ATTACHED, CacheMode.NONE);

			// add event to EventManager
			EventManager eventManager = request.getEventManager();
			eventManager.addEvent(downloadEvent);

			tx.commit();
		} catch (Exception e) {
			tx.rollback();
			throw new PulseException("Error: " + e.getLocalizedMessage(), e);
		} finally {
			s.close();
		}
	}
}