Retrieving the servlet context path from a Spring web application - spring

I would like to be able to dynamically retrieve the "servlet context path" (e.g. http://localhost/myapp or http://www.mysite.com) for my spring web application from a Service spring bean.
The reason for this is that I want to use this value in email that are going to be sent to users of the website.
While it would be pretty easy to do this from a Spring MVC controller, it is not so obvious to do this from a Service bean.
Can anyone please advise?
EDIT: Additional requirement:
I was wondering if there wasn't a way of retrieving the context path upon startup of the application and having it available for retrieval at all time by all my services?

If you use a ServletContainer >= 2.5 you can use the following code to get the ContextPath:
import javax.servlet.ServletContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component
#Component
public class SpringBean {
#Autowired
private ServletContext servletContext;
#PostConstruct
public void showIt() {
System.out.println(servletContext.getContextPath());
}
}

As Andreas suggested, you can use the ServletContext. I use it like this to get the property in my components:
#Value("#{servletContext.contextPath}")
private String servletContextPath;

I would avoid creating a dependency on the web layer from your service layer. Get your controller to resolve the path using request.getRequestURL() and pass this directly to the service:
String path = request.getRequestURL().toString();
myService.doSomethingIncludingEmail(..., path, ...);

If the service is triggered by a controller, which I am assuming it is you can retrieve the path using HttpSerlvetRequest from the controller and pass the full path to the service.
If it is part of the UI flow, you can actually inject in HttpServletRequest in any layer, it works because if you inject in HttpServletRequest, Spring actually injects a proxy which delegates to the actual HttpServletRequest (by keeping a reference in a ThreadLocal).
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
public class AServiceImpl implements AService{
#Autowired private HttpServletRequest httpServletRequest;
public String getAttribute(String name) {
return (String)this.httpServletRequest.getAttribute(name);
}
}

With Spring Boot, you can configure the context-path in application.properties:
server.servlet.context-path=/api
You can then get the path from a Service or Controller like this:
import org.springframework.beans.factory.annotation.Value;
#Value("${server.servlet.context-path}")
private String contextPath;

Related

Jersey JAX-RS: extend logging over resource-class dispatching

I am using Jersey JAX-RS for a REST-service.
I a running it in SE-Deployment with GrizzlyHttp inside my programm.
I have also registered a logger that prints out the HTTP-request and the response.
What I am missing is logging "between" the HTTP-server and the resource-class.
I'd like to see log-entries if no path matches or if a path matches but the parameter did not or similar cases. Today I need to check my annotations and compare it to the logged HTTP-parameters. A bit more logging arround that would be helpful.
Is that possible?
You can achieve it by using a pre-matching filter.
A pre-matching filter is a filter that gets invoked before the JAR-RS runtime tries to match the request with a resource. This type of filter lets you log the requested URI even though it may not match with any of your resources. You annotate a filter with the #PreMatching annotation, and that is all you have to do to have the filter get invoked before the resource matching phase.
Note:: If you want your filter to be discoverable by JAX-RS runtime during the provider scanning phase, then you need to annotate it with the #Provider annotation. Alternatively, you can manually register it in your application set-up (as I have done, that's why in the example below I haven't annotated the filter with #Provider).
Here's a very simple example of a pre-matching filter.
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import java.io.IOException;
#PreMatching
public class PreMatchingFilterExample implements ContainerRequestFilter {
private final static Logger LOG = LogManager.getLogger(PreMatchingFilterExample.class);
#Context
UriInfo uriInfo;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
LOG.info("Resource not found: " + uriInfo.getAbsolutePath());
LOG.info("Path params: " + uriInfo.getPathParameters());
LOG.info("Query params: " + uriInfo.getQueryParameters());
}
}

CDI Interceptors and Jersey JAX/RS

Using CDI API and Weld 3.0.x as the impl.
I have a simple CDI managed bean/#Named that has a method marked to be intercepted. The interceptor is a simple logging like interceptor. This all just works fine and as expected in a 'container' (like a jboss or wildfly); it also works in a Java SE program (booting CDI via SeContainerInitializer, etc).
Now using the SAME exact managed bean and interceptor, BUT the injection point of the managed bean is in a very simple JAX/RS controller (Jersey Runtime 2.27 with jersey-cdi2-se); the interceptor doesn't fire.
In this environment, the injection resolves fine, as do most other simple cdi things, like producers and post constructs, observes, etc. The rest end-point method can invoke the injectable's methods, but the interceptor will not fire.
There is no servlet or EE container in play; just Jersey and CDI/Weld -- so it is much like JavaSE case - nothing is booting a jetty/grizzly or other simple bootable HTTP container thing.
A bit more backstory:
Basically, I have a vanilla CDI BDU/JAR with some #Nameds in it; some of which have some interceptor annotations. The DBU doesn't contain an impl for the Interceptor annotations. I want to be able include the BDU in few different projects while allowing the host project to provide a specific implementation of interceptors (or omit it) that makes the most sense for the project.
This strategy works OK for host projects that target an EE container or JavaSE. Now I have this odd JavaSE/JAX-RS/Jersey mashup; everything but the interceptor works. It is like there is some interference in the jersey-cdi2-se stuff (or somewhere); or maybe some 'switch' that needs to be thrown. Weld bootstrap logging suggests that interceptor is in play.
Is there something special or other trick to getting CDI interceptors to work along side JAX-RS/Jersey?
This odd SE/jax-rs mashup is really just using AWS servless java container -- so the "http container" is provided by AWS API Gateway and proxied lambda.
(NOTE: I am aware it maybe questionable to use some/all/any of the above techs in a FAAS/Lambda environment. I will tackle the 'should I do this' just after I finish answering the 'can I do this'.)
EDIT & Update:
Is the interceptor enabled? Yes as far as I can tell, it is. I've scraped the following bits out of log (after turning on a bit of WELD logging):
[main] DEBUG org.jboss.weld.Bootstrap - WELD-000105: Enabled interceptor types for Weld BeanManager for /var/task [bean count=5]:
- class org.jboss.weld.contexts.activator.ActivateRequestContextInterceptor,
- class org.jboss.weld.contexts.activator.CdiRequestContextActivatorInterceptor,
- class org.jboss.weld.environment.se.contexts.activators.ActivateThreadScopeInterceptor,
- class com.pin.faas.MyTransactionalInterceptor
.....
[main] DEBUG org.jboss.weld.Bootstrap - WELD-000107: Interceptor: Interceptor [class com.pin.faas.MyTransactionalInterceptor intercepts #MyTransactional
As for some bits of code:
The interceptor binding:
package com.pin.api.annotation;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import javax.interceptor.InterceptorBinding;
#Inherited
#InterceptorBinding
#Target({ TYPE, METHOD })
#Retention(RUNTIME)
public #interface MyTransactional
{
}
The interceptor impl:
package com.pin.faas;
import java.util.logging.Logger;
import javax.interceptor.*;
import com.pin.api.annotation.MyTransactional;
#Interceptor
#MyTransactional
public class MyTransactionalInterceptor
{
private static final Logger LOGGER = Logger.getLogger("DateServices");
public MyTransactionalInterceptor()
{
LOGGER.info("TransactionalInterceptor constructed");
}
#AroundInvoke
public Object handleTransactionBoudary(InvocationContext context)
throws Exception
{
LOGGER.info("handleTransactionBoudary called!");
return context.proceed();
}
}
The CDI managed bean that has an interceptor marker:
package com.pin.services.impl.businesslogic;
import com.pin.api.DatesService;
import com.pin.api.businesslogic.validations.BadCodeException;
import javax.inject.Inject;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
import com.pin.api.annotation.MyTransactional;
#Named
#RequestScoped
public class DatesServiceImpl implements DatesService
{
#Inject
private SomeDao dao;
#Override
#MyTransactional
public String insertRecordIntoXferLog(Integer exceptionCode)
throws BadCodeException
{
dao.insertABrnch();
if(exceptionCode !=null && -1 == exceptionCode)
{
throw new BadCodeException("exception trigger value happenend, tossing an exception and expecting a rollback");
}
return("inserted");
}
}
Finally the JAX-RS controller injects a service:
package com.pin.api.rest;
import javax.enterprise.context.RequestScoped;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.logging.Logger;
import javax.inject.Inject;
import com.pin.api.DatesService;
import com.pin.api.businesslogic.validations.BadCodeException;
import java.util.logging.Level;
#Path("/tx")
#RequestScoped
public class TxTestController
{
private static final Logger LOGGER = Logger.getLogger("DateServices");
#Inject
private DatesService service;
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response testAtTransactional(#QueryParam("c") Integer code)
{
Integer val = 1;
LOGGER.info("testAtTransactional invoked");
if(null == code)
{
LOGGER.info("testAtTransactional DID NOT get a code value, assuming value = " + val);
}
else
{
val = code;
LOGGER.info("testAtTransactional DID get a code value = " + val);
}
String result = "called service.insertRecordIntoXferLog(" + val + ")";
try
{
service.insertRecordIntoXferLog(val);
}
catch(BadCodeException bce)
{
result = bce.getMessage();
}
catch(Exception other)
{
result = other.getMessage();
LOGGER.log(Level.SEVERE,"ERROR",other);
}
return Response.status(200).entity(result).build();
}
}
UPDATES
As per some comments I added an #Priority annotation. That didn't change anything. I also adjusted the packaging in a variety of ways in case there was some issue with BDU/Jar loading and CDI bean initialization; these packaging changes made no difference.
I think there is interference with JAX-RS CDI2-SE and interceptors. Looking at how jax-rs-cdi2-se boots a CDI container, it does NOT do anything for interceptors. Maybe it shouldn't need too, as the interceptor is in beans.xml. However in a standalone SE CDI2 sample (no jax-rs - just a console app), I found I had to explicitly enable the interceptor via initializer.enableInterceptors() before the interceptor would work. Explicitly enabling interceptors was needed even with the beans.xml interceptor activation. Maybe this is more of a JAX-RS issue or a Weld issue?

Jersey and EJB stateless example, EJB instance is null

I am making a simple RESTful service using JAVAX RS and EJB in order to create a singleton object as a global variable.
I initiated the service as follows:
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;
final ResourceConfig rc = new ResourceConfig().packages("service_folder");
GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
I am currently just testing the #Stateless example.
Firstly, I create a class in a random folder, and the content as follows:
import javax.ejb.Stateless;
import java.util.Date;
#Stateless
public class Service {
public Date getCurrentDate(){
return new Date();
}
}
Then I create the resource file in the service_folder mentioned in the very beginning:
#Stateless
#Path("current")
public class ServiceFacade {
#EJB
Service service;
#GET
public String getDate(){
return service.getCurrentDate().toString();
}
}
The current situation is that whenever I access BASE_URI/current, grizzly simply throw an error, and the reason is because service in getDate() is null.
My guess is that during the grizzly init., the Service class bean isn't really registered yet.
Please let me know where did I do wrong, thanks!

How can I add a session/login servlet to get a session object for a Spring - Apache CXF backend?

I have a whole lot of REST webservices developed with CXF and managed in a spring application container, and I have been tasked with creating a servlet that will create a session object that will store attributes which will be read within the Jax-RS services.
I have tried just adding a simple JEE servlet to collect the parameters and create the session object, but then I couldn't figure out how to inject that session object for use throughout the application. I have also tried adding spring-mvc and springweb to the application, and using a dispatcher servlet, which should make it easier to get the session object in each of the webservice beans. Adding two dependencies for a simple servlet seems like overkill, and moreover, spring-mvc doesn't seem to play nice with Jax-RS, the "/rest/" path that I had all the services running on before seems to be taken over by spring-mvc (jax-rs can no longer initialize endpoints on that path) even though I am using a completely different path for the session servlet.
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
#RequestMapping("/session")
public class SessionController {
#RequestMapping(method = RequestMethod.GET)
public String doGet(ModelMap model) {
return "sessionPage";
}
#RequestMapping(method = RequestMethod.POST)
public String doPost(ModelMap model) {
return "redirect:mainpage";
}
}
At the moment the frontend is in React.js, all interactions with the backend are through REST service calls. I've been trying to add a "login" page in a JSP, it's not really a login page yet, it just needs to get the userId for the moment and make that available to the backend.
import org.apache.cxf.Bus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
#Configuration
#ImportResource({"classpath:META-INF/cxf/cxf.xml", "classpath:META- INF/cxf /cxf-servlet.xml"})
public class CXFSetup {
#Autowired
private Bus cxfBus;
}
I have tried just adding a simple JEE servlet to collect the
parameters and create the session object, but then I couldn't figure
out how to inject that session object for use throughout the
application.
You could inject the current request object to your rest service and retrieve the session from it
import javax.ws.rs.core.Context;
import javax.servlet.http.*;
#Path("/yourPath")
public class SomeService {
#Context HttpServletRequest request;
#POST
#Consumes("application/json")
#Produces("application/json")
public Object serviceMethod(){
HttpSession session = request.getSession(false);
session.getAttribute(.....)

Injection of HttpServletRequest in GWTP ActionHandler using Spring

I have implemented my GWT application using Spring + GWTP.
I want to access HttpServletRequest object into my ActionHandler class.
The ServerModule is Spring Configuration class (using #Configuration Annotation).
Now problem is how can I inject the current HttpServletRequest, ServletContext, ServletConfig in my ActionHandler using Spring.
Following is the definition of ServerModule:
#Configuration
#Import(DefaultModule.class)
public class ServerModule extends HandlerModule
{
#Bean
public UserVerficationActionHandler getUserVerificationActionActionHandler()
{
return new UserVerficationActionHandler();
}
}
In above Example I just want to inject the HttpServletRequest using Spring.
Any guidance on this highly appreciated.
Thanks.
The RequestProvider is your solution. It's a class in gwt-dispatch-server jar.
DefaultModule provides the RequestProvider bean so that you can just inject it into places you need it.
Take a look at the sourcec code for com.gwtplatform.dispatch.server.spring.configuration.DefaultModule which creates the RequestProvider as a DefaultRequestProvider which then defers to RequestContextHolder to do the work.
See the link for what you need to add to your web.xml to get this to work.

Resources