Multiple ContainerRequestFilter for Jersey - rest

We are planning on using Jersey's reference implementation for our REST APIs. As a prototype effort, I was also playing around with the ContainerRequestFilters and I implemented multiple of them. Is there a way in which we can control the order in which these filters are executed?
The scenario that I am thinking over here is to ensure that the security filter must be the first one to run, and if required establish the SecurityContext and then execute other filters.

Yes you can control this with the javax.annotation.Priority attribute and the default javax.ws.rs.Priorities. For example if you wanted:
Logging filter always runs first
Authentication filter should run next
Authorization filter should run next
Custom filter should always run after others
You could do:
#Priority(Integer.MIN_VALUE)
public class CustomLoggingFilter implements ContainerRequestFilter
{
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
// DO LOGGING HERE, THIS RUNS FIRST
}
}
#Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter
{
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
String authHeader = requestContext.getHeaderString(HttpHeaders.WWW_AUTHENTICATE);
// DO AUTHENTICATION HERE, THIS RUNS SECOND
}
}
#Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter
{
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
String authHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
// DO AUTHORIZATION HERE, THIS RUNS THIRD
}
}
#Priority(Priorities.USER)
public class MyAwesomeStuffFilter implements ContainerRequestFilter
{
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
// DO AWESOME STUFF HERE, THIS RUNS LAST
}
}

Related

How to send email from a servlet using threads or executor service?

Where executor service should be declared so it is available to other servlets and not new thread gets created for every new request
Can I do something like this and whenever need to send email, forward request to this servlet
Can you please suggest better design to use ExecutorService in servlet or any other way to send email from servlet?
public class EmailTestServlet extends HttpServlet
{
ExecutorService emailThreadPool = null;
public void init()
{
super.init();
emailThreadPool = Executors.newFixedThreadPool(3);
}
protected void doGet(HttpServletRequest request,HttpServletResponse response)
{
sendEmail(); //it will call emailThreadPool.execute();
}
public void destroy()
{
super.destroy();
}
}
Depends on whether CDI is available at your environment. It is available out the box in normal Jakarta EE servers, but in case of barebones servletcontainers such as Tomcat or Jetty you'd need to manually install and configure it. It's relatively trivial though and gives a lot of benefit: How to install and use CDI on Tomcat?
Then you can simply create an application scoped bean for the job like below:
#ApplicationScoped
public class EmailService {
private ExecutorService executor;
#PostConstruct
public void init() {
executor = Executors.newFixedThreadPool(3);
}
public void send(Email email) {
executor.submit(new EmailTask(email));
}
#PreDestroy
public void destroy() {
executor.shutdown();
}
}
In order to utilize it, simply inject it in whatever servlet or bean where you need it:
#WebServlet("/any")
public class AnyServlet extends HttpServlet {
#Inject
private EmailService emailService;
#Override
protected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
Email email = new Email();
// ...
emailService.send(email);
}
}
In case you find yourself in the unfortunate situation that you cannot use CDI, then you'll have to remove the #ApplicationScoped annotation from the EmailService class and reinvent the wheel by simulating whatever CDI is doing under the covers by manually fiddling with ServletContext#get/setAttribute() to simulate an application scoped bean. It might look like this:
#WebListener
public class ApplicationScopedBeanManager implements ServletContextListener {
#Override
public void contextCreated(ServletContextEvent event) {
EmailService emailService = new EmailService();
emailService.init();
event.getServletContext().setAttribute(EMAIL_SERVICE, emailService);
}
#Override
public void contextDestroyed(ServletContextEvent event) {
EmailService emailService = (EmailService) event.getServletContext().getAttribute(EMAIL_SERVICE);
emailService.destroy();
}
}
In order to utilize it, rewrite the servlet as follows:
#WebServlet("/any")
public class AnyServlet extends HttpServlet {
private EmailService emailService;
#Override
public void init() {
emailService = (EmailService) getServletContext().getAttribute(EMAIL_SERVICE);
}
#Override
protected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
Email email = new Email();
// ...
emailService.send(email);
}
}
See also:
What is recommended way for spawning threads from a servlet in Tomcat
How to run a background task in a servlet based web application?
Background process in Servlet

Jersey Server Request/Response filter thread safe

I want to add some MDC logging to all my REST requests and I am using jersey. I have seen some examples of this online but I can't find any info on the thread safety of doing this:
https://coderwall.com/p/qjwyya/jax-rs-mapped-diagnostic-context-filter
#Provider
public class HttpMDCRequestListener implements ContainerRequestFilter, ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
MDC.put("test", "blah");
}
#Override
public void filter(ContainerRequestContext containerRequestContext,
ContainerResponseContext containerResponseContext) throws IOException {
MDC.clear();
}
Is there a new instance of this class for each request? I want to make sure I am clearing the MDC on the same thread as the request.
thanks

How to pass value from rest service to camel route?

I have exposed as service as below
restConfiguration().component("servlet").bindingMode(RestBindingMode.json);
rest("/batchFile").consumes("application/json").post("/routeStart").type(BatchFileRouteConfig.class).to("startRouteProcessor");
Based upon the request from rest service,i would start camel route in processor as below
#Component("startRouteProcessor")
public class StartRouteProcessor implements Processor {
public void process(Exchange exchange) throws Exception {
BatchFileRouteConfig config = exchange.getIn().getBody(BatchFileRouteConfig.class);
String routeId = config.getRouteId();
String sourceLocation = config.getSourceLocation();
exchange.getContext().startRoute(routeId);
}
}
I need to pass the sourceLocation from above bean to below route
#Component
public class FileReaderRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("file:sourceLocation")
.log("File Reader Route route started");
}
}
Above is sample code..request you to help me in passing the sourcelocation from StartRouteProcessor to FileReaderRoute
This is not possible, since in your example is FileReaderRoute already started at the time of calling batchFile endpoint.
You can do it in slightly different way.
Extract your FileReaderRoute to direct. Something like:
#Component
public class FileReaderRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct:fileReaderCommon")
.log("File Reader Route route started");
}
}
And then you can create new route at runtime:
#Component("startRouteProcessor")
public class StartRouteProcessor implements Processor {
public void process(Exchange exchange) throws Exception {
BatchFileRouteConfig config = exchange.getIn().getBody(BatchFileRouteConfig.class);
exchange.getContext().addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
from("file:"+config.getSourceLocation())
.routeId(config.getRouteId())
.to("direct:fileReaderCommon");
}
});
}
}
Do not forget to take sufficient sanitizing of input, since you are allowing user to create file consumer based on user input. In your approach, there is a high risk of path traversal attack.

How to trigger a CLIENT_ERROR or SERVER_ERROR with entity?

I'm trying to debug an web application running on Glassfish and I want to cause the server to return a CLIENT_ERROR or SERVER_ERROR.
The returned javax.ws.rs.core.Response to the calling server should include an entity. What is the best way to do this?
Create a filter and make it return the required Response:
javax.ws.rs.container.ContainerRequestFilter
#Provider
public class RequestFilter implements ContainerRequestFilter {
/** {#inheritDoc} */
#Override
public void filter(final ContainerRequestContext req) throws IOException {
if (req.getUriInfo().getPath().toLowerCase().contains("pathToMatch")) {
final Response newResp = Response.status(500).entity("<test>test</test>").type(MediaType.valueOf(MediaType.TEXT_HTML)).build();
req.abortWith(newResp);
}
}
}

When custom AuthenticationEntryPoint is enabled, PUT requests throw InsufficientAuthenticationException: Full authentication is required

This question is related to this one
I defined my own AuthenticationEntryPoint. When enabled, I receive an exception when trying to execute put requests:
org.springframework.security.authentication.InsufficientAuthenticationException: Full authentication is required to access this resource
But it doesn't happen otherwise.
Does anybody know why and how to fix it?
If it's needed more configuration information, let me know.
This is my configuration:
#Configuration
#Order(1)
public static class RestWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
...
#Override
protected void configure(HttpSecurity http) throws Exception {
...
http
.authorizeRequests()
.antMatchers("/rest/**").hasAnyRole(Sec.ADMIN,Sec.SUPER_USER)
...
.and().exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint)
If I comment out the last line ("authenticationEntryPoint..."), my PUT requests work just fine.
I need to use that EntryPoint in order to prevent the redirection to the login form since this is a REST service.
My RestAuthenticationEntryPoint class is:
#Component( "restAuthenticationEntryPoint" )
public final class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
#SuppressWarnings("unused")
private final Logger logger = Logger.getLogger(getClass());
#Override
public void commence(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
}
}