Dependency Injection of Managed Object Tutorial

This tutorial demonstrates the dependency injection of a DataSource. The DataSource is provided by a ManagedObjectSource implementation (i.e. DataSourceManagedObjectSource).

A ManagedObjectSource enables injection of customised objects. The customised objects have greater access to OfficeFloor/WoOF functionality than plain old java objects (POJOs). OfficeFloor/WoOF, however, supports both as POJOs are simpler to implement.

The example used in this tutorial is the following simple application to manage rows within a database table.

DatabaseHttpServer screen shot.

Download Tutorial Source

Configuring Objects

Objects for dependency injection are configured in the application.objects file contained at the root of the class path.

Providing this file is optional. It is anticipated that features such as WoOF annotations and SupplierSource implementations will provide the necessary dependencies for running a web application. See the other tutorials for more information. The file is however supported to extend WoOF web applications with additional custom dependencies.

The configuration of the dependency injected DataSource is as follows.

<objects>

	<managed-object source="net.officefloor.plugin.jdbc.datasource.DataSourceManagedObjectSource" type="javax.sql.DataSource">
		<property-file path="datasource.properties" />
	</managed-object>

</objects>

The DataSource is provided by a ManagedObjectSource implementation. The type attribute is necessary as OfficeFloor will dependency inject by matching on type.

Properties to configure the ManagedObjectSource implementations can be provided in the above file or within a properties file. The reason for using a property file is that different property files can be used for each environment. See the System property on the AutoWirePropertiesImpl to specify an environment directory location to find the property files.

The contents of DataSource property file is as follows.

data.source.class.name = org.hsqldb.jdbc.jdbcDataSource
database = jdbc:hsqldb:mem:exampleDb
user = sa

Objects to be dependency injected within OfficeFloor are made available by ManagedObjectSource implementations. Many dependency injection frameworks are based solely on the object's Class and its necessary dependency injection configuration. OfficeFloor goes beyond this by providing more capabilities to the object such as invoking processes. For example the socket listener within a stand-alone WoOF HTTP Server is actually a HttpServerSocketManagedObjectSource that invokes a task to service the HTTP request.

The ClassManagedObjectSource is available to provide the typical POJO dependency injection.

Setup the database

The following is the application.woof configuration for the application.

application.woof configuration.

The configuration identifies to trigger the setup of the database on start-up of the application. The following is the code triggered which creates the table and inserts a row.

public class Setup {

	public void setupDatabase(DataSource dataSource) throws Exception {

		Connection connection = dataSource.getConnection();
		try {
			connection
					.createStatement()
					.execute(
							"CREATE TABLE EXAMPLE ( ID IDENTITY PRIMARY KEY, NAME VARCHAR(20), DESCRIPTION VARCHAR(256) )");
			connection
					.createStatement()
					.execute(
							"INSERT INTO EXAMPLE ( NAME, DESCRIPTION ) VALUES ( 'WoOF', 'Web on OfficeFloor' )");
		} finally {
			connection.close();
		}
	}

}

The dependencies to inject by OfficeFloor is defined by the method's parameters. OfficeFloor will reflectively interrogate the method and inject the necessary dependencies on invoking it.

The above method is invoked on start-up of the application. Covered later in this tutorial is dependency injecting the DataSource to service a request.

Dependency Injection

The following is the content of the template.

<html>
	<body>
		<br />
		<table border="1">
			<tr>
				<td>Name</td>
				<td>Description</td>
				<td>Remove</td>
			</tr>
			<!-- {Rows} -->
			<tr>
				<td>${name}</td>
				<td>${description}</td>
				<td><a href="#{deleteRow}?id=${id}">delete</a></td>
			</tr>
			<!-- {EndRows} -->
		</table>
		<br />
		<form action="#{addRow}" method="POST">
			Name: <input name="name" type="text" />
			Description: <input name="description" type="text" />
			<input type="submit" value="Add" />
		</form>
	</body>
</html>

The table data is provided by the following method.

public class Template {

	public Row[] getRows(DataSource dataSource) throws SQLException {

		Connection connection = dataSource.getConnection();
		try {
			
			// Obtain the row instances
			ResultSet resultSet = connection.createStatement().executeQuery(
					"SELECT * FROM EXAMPLE ORDER BY ID");
			List<Row> rows = new LinkedList<Row>();
			while (resultSet.next()) {
				rows.add(new Row(resultSet.getInt("ID"), resultSet
						.getString("NAME"), resultSet.getString("DESCRIPTION")));
			}
			resultSet.close();

			// Return the row instances
			return rows.toArray(new Row[rows.size()]);
			
		} finally {
			connection.close();
		}
	}

As the method is matched to the template by name, OfficeFloor uses the method's parameters to identify the necessary dependencies to be injected. In this case the only dependency is the DataSource which was configured above.

WoOF auto-wires dependency injection based on type. Auto-wiring dependencies based on type is adequate (and much easier) for the majority of applications. WoOF's underlying OfficeFloor framework does provide manual dependency configuration, however this is seldom used as OfficeFloor allows qualifying dependencies for auto-wiring.

The handling of #{addRow} submission is via the following method.

	public void addRow(Row row, DataSource dataSource) throws SQLException {
		
		Connection connection = dataSource.getConnection();
		try {

			// Add the row
			PreparedStatement statement = connection
					.prepareStatement("INSERT INTO EXAMPLE (NAME, DESCRIPTION) VALUES ( ?, ? )");
			statement.setString(1, row.getName());
			statement.setString(2, row.getDescription());
			statement.executeUpdate();

		} finally {
			connection.close();
		}
	}

The method requires two parameters to be dependency injected. The DataSource is dependency injected as above. The Row object below is also dependency injected by its WoOF annotation. See the other tutorials for more details on WoOF annotations.

@HttpParameters
public class Row implements Serializable {

	private int id;

	private String name;

	private String description;

	public Row() {
	}

	public Row(int id, String name, String description) {
		this.id = id;
		this.name = name;
		this.description = description;
	}

	public int getId() {
		return this.id;
	}

	public void setId(String id) {
		this.id = Integer.parseInt(id);
	}

	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getDescription() {
		return this.description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

}

The delete row functionality is similar to the add functionality.

	public void deleteRow(Row row, DataSource dataSource) throws SQLException {

		Connection connection = dataSource.getConnection();
		try {

			// Delete the row
			PreparedStatement statement = connection
					.prepareStatement("DELETE FROM EXAMPLE WHERE ID = ?");
			statement.setInt(1, row.getId());
			statement.executeUpdate();

		} finally {
			connection.close();
		}
	}

After the add or delete method is executed the template is rendered again for the response back to the client. The rendering of the page executes the getRows(...) method again to display the changes within the table.

Unit Test

The unit test requests the page and then adds a row and deletes a row.

	private final CloseableHttpClient client = HttpTestUtil.createHttpClient();

	public void testInteraction() throws Exception {

		// Request page
		this.doRequest("http://localhost:7878/example.woof");

		// Add row (will pick up parameter values from URL)
		this.doRequest("http://localhost:7878/example-addRow.woof?name=Daniel&description=Founder");

		// Delete row
		this.doRequest("http://localhost:7878/example-deleteRow.woof?id=1");
	}

	private void doRequest(String url) throws Exception {
		HttpResponse response = this.client.execute(new HttpGet(url));
		assertEquals("Request should be successful", 200, response
				.getStatusLine().getStatusCode());
		response.getEntity().writeTo(System.out);
	}

Next

The next tutorial covers managing transactions.