Today is Thursday, 21st January 2021

Author Archive


Deploying To Tomcat using WAR files

When you deploy your Java web apps, you can make use of a Web Application Archive (WAR) file.

The Web Application Archive (WAR) file is a compressed version of your web application. It uses the zip file format but the file has the .war extension.

The best way to visualize it is think of your “webapp” directory being compressed as a zip file with the .war extension.

This includes all of your web pages, images, css etc. It also includes the WEB-INF directory which includes your classes in WEB-INF/classes and supporting JAR files in WEB-INF/lib.

The WAR file format is part of the Java EE / Servlet specification. As a result, all Java EE servers support this format (ie jboss, weblogic, websphere, glassfish and tomcat).

Below, I provide the steps on how to create a WAR file using Maven. I also show how to deploy the WAR file on Tomcat.

1. In your IDE, stop Tomcat

2. Run Maven: $ mvn clean package

3. This generates a file in sub-directory: target/YOURAPP.war

4. Outside of IDE, start Tomcat
– If you are using MS Windows, then you should find it on the Start menu

5. Make sure Tomcat is up and running by visiting: http://localhost:8080

6. Deploy your new WAR file by copying it to <tomcat-install-directory>\webapps

Give it about 10-15 seconds to make the deployment. You’ll know the deployment is over because you’ll see a new folder created in webapps … with your WAR file name.

7. Visit your new app. If your war file was: mycoolapp.war then you can access it with:  http://localhost:8080/mycoolapp/



Spring REST API: How to disable HTTP Delete/Put methods

Question

I had a student ask the question:
How can I disable HTTP Options/Delete/Put methods? At the application level it should support only GET and POST requests. Other request methods like put/delete need to be blocked. I am using Spring Boot, Spring MVC and Rest services.

Answer

One possible solution is to make use of Spring Interceptors. Spring Interceptors can intercept HTTP requests for an application and perform pre-processing and post-processing.

For this solution, we will create an interceptor that will pre-process the HTTP requests. If the request does not match our approved HTTP methods then we will reject the requests. In this case, we will only approve HTTP GET and POST requests.

Development Process

Here’s the development process
1. Create a sample REST API
2. Develop a Spring Interceptor
3. Configure the Spring Interceptor for URL mappings

Source Code

All of the solution source code is available for download:
spring-rest-api-block-http-methods.zip

Development

Let’s walk through the development process.

1. Create a sample REST API

For this example, I’ll create a simple REST API

  • GET: /api/demo
  • POST: /api/demo
  • PUT: /api/demo
  • DELETE: /api/demo
  • PATCH: /api/demo

We want to create code that will only allow GET and POST requests to the /api/demo endpoint.

Let’s start by creating a simple REST API. You can use the Spring Initializr website to create a Spring Boot application. For the dependency, be sure to select Spring Web.

Once you have Spring Boot application generated and imported into your IDE, then you can start creating a simple REST API.

Here is the code for our REST API: /api/demo

package com.example.demoapi.controller;

import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;

@RestController
@RequestMapping("/api/demo")
public class DemoController {

    @GetMapping
    public String doGet(HttpServletRequest request) {
        return "in request: "  + request.getMethod();
    }

    @PostMapping
    public String doPost(HttpServletRequest request) {
        return "in request: "  + request.getMethod();
    }

    @PutMapping
    public String doPut(HttpServletRequest request) {
        return "in request: "  + request.getMethod();
    }

    @DeleteMapping
    public String doDelete(HttpServletRequest request) {
        return "in request: "  + request.getMethod();
    }

    @PatchMapping
    public String doPatch(HttpServletRequest request) {
        return "in request: "  + request.getMethod();
    }

}

This code simply create a REST endpoint for /api/demo. It supports the HTTP methods: GET, POST, PUT, DELETE, PATCH and OPTIONS. The methods simply return the HTTP method that is begin processed.

2. Develop a Spring Interceptor

In this section, let’s create an interceptor that will pre-process the HTTP requests. If the request does not match our approved HTTP methods then we will reject the requests. In this case, we will only approve HTTP GET and POST requests.

package com.example.demoapi.interceptor;

import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class DemoInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String theMethod = request.getMethod();

        if (HttpMethod.GET.matches(theMethod) || HttpMethod.POST.matches(theMethod)) {
            // GET, POST methods are allowed
            return true;
        }
        else {
            // everything else is not allowed
            response.sendError(HttpStatus.METHOD_NOT_ALLOWED.value());
            return false;
        }
    }
}

As you can see, the code checks the method being processed. If the HTTP method is GET or POST then it returns true. This tells the Spring framework that this interceptor has passed successfully and the request can continue processing.

If the HTTP method is not GET or POST then the interceptor send a a response error code: 405 – Method Not Allowed. The method also returns false which tells the Spring framework to that this interceptor did not pass and the the request should not continue processing further to the API.

3. Configure the Spring Interceptor for URL Mappings

The final step in development is to configure the Spring Interceptor for given URL mappings. Here’s the code:

package com.example.demoapi.config;

import com.example.demoapi.interceptor.DemoInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class DemoConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new DemoInterceptor()).addPathPatterns("/api/demo*/**");
    }
}

This code adds the interceptor to the registry of interceptors. The interceptor is mapped to the path pattern: /api/demo*/**. This will match on any requests for api/demo and also sub-directories. This syntax makes use of wildcards with the Ant patch matcher syntax.

If you want to apply to the entire application, you can use /*.

Unit Testing

Now that the code is created, let’s test it with Spring Boot Unit testing. This collection of tests with access the end point using different HTTP request methods.

package com.example.demoapi;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

@SpringBootTest
@AutoConfigureMockMvc
class DemoapiApplicationTests {

    @Test
    public void getRequestShouldReturn_ok_200(@Autowired MockMvc mockMvc) throws Exception {

        mockMvc.perform(MockMvcRequestBuilders.get("/api/demo"))
				.andExpect(MockMvcResultMatchers.status().isOk());
    }

    @Test
    public void postRequestShouldReturn_ok_200(@Autowired MockMvc mockMvc) throws Exception {

        mockMvc.perform(MockMvcRequestBuilders.post("/api/demo")
				.content("{\"name\": \"demo\"}"))
				.andExpect(MockMvcResultMatchers.status().isOk());
    }

    @Test
    public void putRequestShouldReturn_notallowed_405(@Autowired MockMvc mockMvc) throws Exception {

        mockMvc.perform(MockMvcRequestBuilders.put("/api/demo/1")
				.content("{\"name\": \"demo\"}"))
				.andExpect(MockMvcResultMatchers.status().isMethodNotAllowed());
    }

    @Test
    public void deleteRequestShouldReturn_notallowed_405(@Autowired MockMvc mockMvc) throws Exception {

        mockMvc.perform(MockMvcRequestBuilders.delete("/api/demo/1")
				.content("{\"name\": \"demo\"}"))
 				.andExpect(MockMvcResultMatchers.status().isMethodNotAllowed());
    }

    @Test
    public void patchRequestShouldReturn_notallowed_405(@Autowired MockMvc mockMvc) throws Exception {

        mockMvc.perform(MockMvcRequestBuilders.patch("/api/demo/1")
				.content("{\"name\": \"demo\"}"))
				.andExpect(MockMvcResultMatchers.status().isMethodNotAllowed());
    }

    @Test
    public void optionsRequestShouldReturn_notallowed_405(@Autowired MockMvc mockMvc) throws Exception {

        mockMvc.perform(MockMvcRequestBuilders.options("/api/demo"))
				.andExpect(MockMvcResultMatchers.status().isMethodNotAllowed());
    }

}

For the HTTP methods GET and POST, the application should work successfully and return status code of 200 – Ok.

For the HTTP methods: PUT, DELETE, PATCH and OPTIONS, the application should return 405 – Not Allowed.

You can run the unit test with the following command:

mvn clean test

After running the tests, you should see these test results:

...
...
[INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0 ...
...
 [INFO] 
 [INFO] Results:
 [INFO] 
 [INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0
 [INFO] 
 [INFO] ------------------------------------------------------------------------
 [INFO] BUILD SUCCESS

Run The Application

Now, you can run the application with the command:

mvn spring-boot:run

Test with curl

Now that the application is up and running you can test the application with any REST client testing tool such as Postman etc. In this example, I’ll make use of the curl utility.

Testing GET requests
curl http://localhost:8080/api/demo

The GET request will return successfully.

Testing POST requests
curl -X POST  http://localhost:8080/api/demo

The POST request will return successfully.

Testing PUT, DELETE, PATCH and OPTIONS request
curl -X DELETE  http://localhost:8080/api/demo
curl -X PUT  http://localhost:8080/api/demo
curl -X PATCH  http://localhost:8080/api/demo
curl -X OPTIONS  http://localhost:8080/api/demo

These methods will each return error code 405 – Method Not Allowed.

{
  "timestamp": "2020-01-06T20:12:26.747+0000",
  "status": 405,
  "error": "Method Not Allowed",
  "message": "No message available",
  "path": "/api/demo"
}

Summary

In this solution, we made use of a Spring Interceptorto pre-process the HTTP requests. If the request did not match our approved HTTP methods then werejected the request. In our case, we only approved HTTP GET and POST requests.

This is just a basic solution. However, if you have more advanced requirements such as access control, rate limiting, security, analytics, etc … then I would recommend an API management tools. API management tools can help you with this functionality. These tools provide this functionality out of the box and minimizes the amount of custom code you have to develop. To help you get started, you can use 3Scale. 3Scale is an open-source API Management tool.

Source code

Just a reminder, the source code is available for download: spring-rest-api-block-http-methods.zip



How to Deploy Spring Boot and AWS Elastic Beanstalk

Overview of Steps

  • AWS EBS expects for your apps to listen on port 5000
  • Update your Spring Boot application.properties to use: server.port=5000
  • Select Web App > Platform Java
  • Upload the JAR file

Create basic Spring Boot app

  1. Start with a simple REST Spring Boot app.
  2. In application.properties, change port to 5000: server.port=5000
  3. Open terminal window
  4. cd project directory
  5. mvn clean package
  6. java -jar target/myapp.jar
  7. Test it locally: http://localhost:5000

Deploy on AWS

  1. Log into to AWS
  2. Navigate to Elastic Beanstalk
  3. Create a new application
  4. Select app type: Web Application
  5. Give it the name: simple-boot-demo
  6. Create a new environment
  7. For platform, select: Java
  8. Select option to Upload your JAR file.
    Note: the screen says only WAR and ZIP files, but it does in fact accept JAR files
  9. Upload your JAR file:  target/myapp.jar
  10. Create the application
  11. Once app is created, then visit the app URL.
  12. You will see your Spring Boot app up and running.