This tutorial demonstrates the dependency injection of a Connection. The Connection is provided by a ManagedObjectSource implementation (i.e. ConnectionManagedObjectSource).
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.
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 Connection is as follows.
<objects> <managed-object source="net.officefloor.jdbc.ConnectionManagedObjectSource" /> <managed-object source="net.officefloor.jdbc.DataSourceManagedObjectSource"> <property-file path="datasource.properties" /> </managed-object> </objects>
The Connection is provided by a ManagedObjectSource implementation that closes the connection on completion of the request. This depends on the DataSource to provide a managed Connection.
Properties to configure the ManagedObjectSource implementations can be provided in the above file or within a properties file.
The contents of configured property file is as follows.
datasource.class = org.h2.jdbcx.JdbcDataSource url = jdbc:h2:mem:exampleDb;DB_CLOSE_DELAY=-1 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 of other objects. 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 an OfficeFloor lightweight process to service the HTTP request.
The ClassManagedObjectSource is available to provide the typical POJO 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 Row[] getRows(Connection connection) throws SQLException { // Obtain the row instances try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM EXAMPLE ORDER BY ID")) { ResultSet resultSet = statement.executeQuery(); List<Row> rows = new LinkedList<Row>(); while (resultSet.next()) { rows.add(new Row(resultSet.getInt("ID"), resultSet.getString("NAME"), resultSet.getString("DESCRIPTION"))); } // Return the row instances return rows.toArray(new Row[rows.size()]); } }
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 Connection 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, Connection connection) throws SQLException { // Add the row try (PreparedStatement statement = connection .prepareStatement("INSERT INTO EXAMPLE (NAME, DESCRIPTION) VALUES ( ?, ? )")) { statement.setString(1, row.getName()); statement.setString(2, row.getDescription()); statement.executeUpdate(); } }
The method requires two parameters to be dependency injected. The Connection 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 @Data @AllArgsConstructor @NoArgsConstructor public class Row implements Serializable { private static final long serialVersionUID = 1L; private int id; private String name; private String description; }
The delete row functionality is similar to the add functionality.
public void deleteRow(@HttpQueryParameter("id") String id, Connection connection) throws SQLException { // Obtain the identifier int rowId = Integer.parseInt(id); // Delete the row try (PreparedStatement statement = connection.prepareStatement("DELETE FROM EXAMPLE WHERE ID = ?")) { statement.setInt(1, rowId); statement.executeUpdate(); } }
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.
The unit test requests the page and then adds a row and deletes a row.
@RegisterExtension public final MockWoofServerExtension server = new MockWoofServerExtension(); @Test public void testInteraction(DataSource dataSource) throws Exception { try (Connection connection = dataSource.getConnection()) { // Request page this.server.send(MockWoofServer.mockRequest("/example")); // Add row (will pick up parameter values from URL) MockWoofResponse response = this.server .send(MockWoofServer.mockRequest("/example+addRow?name=Daniel&description=Founder")); assertEquals(303, response.getStatus().getStatusCode(), "Should follow POST then GET pattern"); assertEquals("/example", response.getHeader("location").getValue(), "Ensure redirect to load page"); // Ensure row in database PreparedStatement statement = connection.prepareStatement("SELECT * FROM EXAMPLE WHERE NAME = 'Daniel'"); ResultSet resultSet = statement.executeQuery(); assertTrue(resultSet.next(), "Should find row"); assertEquals("Founder", resultSet.getString("DESCRIPTION"), "Ensure correct row"); // Delete row this.server.send(MockWoofServer.mockRequest("/example+deleteRow?id=" + resultSet.getInt("ID"))); // Ensure row is deleted resultSet = statement.executeQuery(); assertFalse(resultSet.next(), "Row should be deleted"); } }
The next tutorial covers start up ordering of ManagedObjectSource instances.