This tutorial demonstrates running within Google App Engine.
The configuration is just like any other Servlet application deployed to Google App Engine:
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> </web-app>
<?xml version="1.0" encoding="utf-8"?> <appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> <runtime>java8</runtime> <threadsafe>true</threadsafe> <warmup-requests-enabled>false</warmup-requests-enabled> </appengine-web-app>
Note: see Google App Engine documentation for further configuration.
To configure to run within GCP, simply add the following dependency:
<dependency> <groupId>net.officefloor.server</groupId> <artifactId>officeserver_appengine</artifactId> </dependency>
This then runs the application as any other Servlet application within the Google App Engine.
Google App Engine tooling can be used as normal to deploy the application:
<plugin> <groupId>com.google.cloud.tools</groupId> <artifactId>appengine-maven-plugin</artifactId> <configuration> <deploy.projectId>${project.artifactId}</deploy.projectId> <deploy.version>${project.version}</deploy.version> </configuration> </plugin>
Again the Google App Engine tooling can be used for testing. This allows testing simple applications:
@RegisterExtension public final HttpClientExtension client = new HttpClientExtension(false, 8481); @Test public void ensureGetResource() throws Exception { this.doTest("/index.html", "<html><body>Hello from GCP</body></html>"); } @Test public void ensureRestEndPoint() throws Exception { this.doTest("/rest", "{\"message\":\"Hello from GCP\"}"); } private void doTest(String path, String entity) throws IOException { // Obtain the resource HttpGet get = new HttpGet(this.client.url(path)); HttpResponse response = this.client.execute(get); // Ensure obtained resource String actualEntity = EntityUtils.toString(response.getEntity()); assertEquals(200, response.getStatusLine().getStatusCode(), "Should be successful: " + actualEntity); assertEquals(entity, actualEntity); }
However, most applications will require a data store. To use a local data store requires starting the data store from the command line. This disallows easy use within continuous integration/delivery pipelines.
OfficeFloor provides the following plugin to undertake integration testing within the Google App Engine emulator with a local data store:
<plugin> <groupId>net.officefloor.maven</groupId> <artifactId>officefloor-appengine-maven-plugin</artifactId> <configuration> <port>8481</port> </configuration> <executions> <execution> <id>start-gcp</id> <phase>pre-integration-test</phase> <goals> <goal>start</goal> </goals> </execution> <execution> <id>stop-gcp</id> <phase>post-integration-test</phase> <goals> <goal>stop</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> <executions> <execution> <goals> <goal>integration-test</goal> <goal>verify</goal> </goals> </execution> </executions> </plugin>
To access the data store started, the following test dependency needs to be added:
<dependency> <groupId>net.officefloor.maven</groupId> <artifactId>officefloor-appengine-maven-plugin</artifactId> <scope>test</scope> </dependency>
This allows the application to be integration tested as follows:
@Test public void ensureDatastoreEndPoint() throws Exception { // Obtain the datastore from integration setup Datastore datastore = IntegrationAppEngine.getDatastore(); // Initialise datastore ObjectifyService.init(new ObjectifyFactory(datastore)); ObjectifyService.register(Post.class); Post post = new Post(null, "TEST MESSAGE"); Map<Key<Post>, Post> data = ObjectifyService.run(() -> { return ObjectifyService.ofy().save().entities(post).now(); }); assertEquals(1, data.size(), "Should persist data to retrieve"); // Ensure able to retrieve the data HttpGet get = new HttpGet(this.client.url("/post/" + post.getId())); HttpResponse response = this.client.execute(get); // Ensure obtained resource String actualEntity = EntityUtils.toString(response.getEntity()); assertEquals(200, response.getStatusLine().getStatusCode(), "Should be successful: " + actualEntity); Post retrievePost = new ObjectMapper().readValue(actualEntity, Post.class); assertEquals(post.getMessage(), retrievePost.getMessage(), "Incorrect post retrieved"); }
Furthermore, Google AppEngine emulator only runs HTTP. There is no secure port. This makes testing secure end points difficult.
To enable testing of secure connection end points, the OfficeFloor plugin provides a Filter that flags all requests to be secure (avoiding the need for redirects).
This enables the following:
@Test public void ensureSecureEndPoint() throws Exception { // Obtain the secure resource (made accessible for testing) HttpGet get = new HttpGet("http://localhost:8481/secure"); HttpResponse response = this.client.execute(get); // Ensure obtained resource String actualEntity = EntityUtils.toString(response.getEntity()); assertEquals(200, response.getStatusLine().getStatusCode(), "Should be successful: " + actualEntity); assertEquals("{\"message\":\"Secure hello from GCP\"}", actualEntity); }
Note: for running with the Google IDE plugins, the dependency can be also added to the class path to avoid secure redirects:
<dependency> <groupId>net.officefloor.server</groupId> <artifactId>officeserver_appengineemulator</artifactId> <scope>test</scope> </dependency>
The next tutorial covers integrating DynamoDB into OfficeFloor.