Embedded Tomcat 7 passing Spring application context to servlets - java

I would like to add web interface to my Java application, so that I can manipulate it's state using HTTP.
I have added to application context a Spring bean for some class that starts embedded Tomcat. This class of course has access to context that creates it. But I would like to store this context somehow in Tomcat class (org.apache.catalina.startup.Tomcat) so that later in can be retrieved in Servlets, so that I can do something like this:
public SomeClass extends extends HttpServlet {
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ApplicationContext appContext = getContextStoredEarlierInTomcatClass();
SomeBeanFromContext sbfc = appContext.getBean("sbfc", ApplicationContext.class);
sbfc.setSomeProperty(newValue);
}
}
Any idea how I could achieve it?
Thanks!

Classes including Servlets do not require an ApplicationContext to obtain references to String beans. This is done using dependency injection
#Controller
#RequestMapping("/mypage")
public class SomeClass {
#Autowired
private SomeBeanFromContext sbfc;
#RequestMapping(value = "/individualRequest", method = RequestMethod.GET)
public String doIndividualRequest(HttpServletRequest request) {
sbfc.setSomeProperty(newValue);
...
}
}
Spring MVC offers a complete method of injecting beans into target web controller classes using #Controller annotated classes.

Related

How to access the same instance of a bean from HttpServlet?

I am somewhat new to Spring Framework. I have a web application written with Spring (4.2.1). I'm trying to expose metrics using Micrometer library and will be scraping with Prometheus.
The relevant structure of the application is this:
- core-module (JAR)
- webservice-module (WAR)
I created a PrometheusService class which is a bean defined in core-module. Defined inside the bean is the PrometheusMeterRegistry and Counter:
#Service
public class PrometheusService {
private static PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
private static Counter newAssetCounter = Counter
.builder("new_asset_counter")
.description("count of created assets")
.tags("region", "na")
.register(registry);
public PrometheusService() {
new JvmMemoryMetrics().bindTo(registry);
new DiskSpaceMetrics(new File("/")).bindTo(registry);
new ProcessorMetrics().bindTo(registry);
new UptimeMetrics().bindTo(registry);
}
public static PrometheusMeterRegistry getRegistry() {
return registry;
}
public Counter getNewAssetCounter() {
return this.newAssetCounter;
}
}
I created MetricsResource which is an HttpServlet that exposes the /metrics endpoint. When trying to #Autowire the PrometheusService bean, it was always null here. A quick search told me that HttpServlet isn't managed by Spring. If I wanted to #Autowire, I needed to add something like this:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(webApplicationContext);
Now, I was able to #Autowire the PrometheusService bean within the Servlet.
The Counter defined in the bean gets incremented within the core-module. The MetricsResource doGet method writes the metrics stored in the PrometheusMeterRegistry.
#WebServlet("/metrics")
public class MetricsResource extends HttpServlet {
private PrometheusService promService; // #Autowired
private PrometheusMeterRegistry registry;
#Override
public void init() throws ServletException {
super.init();
// SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(webApplicationContext);
// promService = (PrometheusService) getServletContext().getAttribute("prometheusService");
// WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
// AutowireCapableBeanFactory ctx = context.getAutowireCapableBeanFactory();
// ctx.autowireBean(this);
}
#Override
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws IOException {
resp.setStatus(HttpServletResponse.SC_OK);
resp.setContentType(TextFormat.CONTENT_TYPE_004);
registry = promService.getRegistry();
Writer writer = resp.getWriter();
try {
registry.scrape(writer);
writer.flush();
} finally {
writer.close();
}
}
}
The problem though, is that the value of the Counter is always 0 at the /metrics endpoint.
No matter if it's #Autowired or if I'm manually trying to get the bean.
How could this be? My PrometheusService bean is a singleton. Even the PrometheusMeterRegistry and the Counter are marked static, so why am I getting a different object in my servlet? After some more searching, I found that Spring will create one singleton bean per container. So what I'm assuming is happening here is there are two containers or contexts. A main application context and a servlet context.
Some things I've tried:
Making PrometheusService implement ApplicationContextAware
Using a ServiceLocator class that implements ApplicationContextAware and returns beans
Adding context-params to web.xml
Using ServletContextAttributeExporter in app-context.xml
Using WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext())
I continue to get a new instance of the object. All I want to do is be able to create and expose custom metrics with Micrometer. Is my approach flawed? How can I access the correct bean from within my HttpServlet?
Spring dependency injection to other instance
http://senthadev.com/sharing-spring-container-between-modules-in-a-web-application.html
Spring dependency injection to other instance
ApplicationContext and ServletContext
Try processInjectionBasedOnServletContext(Object target, ServletContext servletContext).

Using Spring Data Rest RepositoryEntityLinks outside of Controller

I would like to use the RepositoryEntityLinks class to get the link to a resource at various places in my code as per section 12.1 of the current Spring Data Rest manual
12.1. Programmatic Links Sometimes you need to add links to exported resources in your own custom built Spring MVC controllers. There are
three basic levels of linking available:
...
3 Using Spring Data REST’s implementation of RepositoryEntityLinks.
http://docs.spring.io/spring-data/rest/docs/current/reference/html/#_programmatic_links
I note the docs refer explicitly to "...your own custom built Spring MVC controllers" and it would seem that is the only place it is available. I would like to use the configured instance in a Spring Security AuthenticationSuccessHandler however the application fails to start with the error:
No qualifying bean of type[org.springframework.data.rest.webmvc.support.RepositoryEntityLinks] found
I have been able to successfully inject it to a controller as expected.
Can I use the RepositoryEntityLinks class outside of a Spring MVC Controller?
public class RestAuthenticationSuccessHandler implements AuthenticationSuccessHandler
{
#Autowired
private RepositoryEntityLinks entityLinks;
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException
{
//do something with entityLinks
}
}
Yes, You can. I have successfully used it in Assembler which generates links from HATEOAS model. Altough there may be some restrictions on where RepositoryEntityLinks class can be injected, for sure it can be used outside of Controllers.
Below you can see my working example. If anyone wnders this class extends ResourceAssemblerSupport which is part of spring-hateoas module. Maybe that's the thing that enables injection here.
#Component
public class UserAssembler extends ResourceAssemblerSupport<UserEntity, UserResource> {
#Autowired
private RepositoryEntityLinks repositoryEntityLinks;
public UserAssembler() {
super(UserController.class, UserResource.class);
}
#Override
public UserResource toResource(UserEntity userEntity) {
Link userLink = repositoryEntityLinks.linkToSingleResource(UserEntity.class, userEntity.getId());
Link self = new Link(entryLink.getHref(), Link.REL_SELF);
return new UserResource(userEntity, self);
}
}
The following works for me:
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class RestApiIntegrationTests {
#Autowired
private RepositoryEntityLinks repositoryEntityLinks;
#BeforeEach
public void initServletRequestAttributes() {
MockHttpServletRequest request = new MockHttpServletRequest();
ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(requestAttributes);
}
#Test
void test() {
System.out.println(repositoryEntityLinks.linkToCollectionResource(SomeClass.class));
}
}
The code is based on spring-data-rest-tests-core: AbstractControllerIntegrationTests, TestMvcClient.

Session context is null JBOSS 7.1

UPDATE QUESTION:
I used JBOSS Develper Studio 8, JBOS server 7.1 based on jre 1.7 I have one J2EE project with ejb and web projects. In ejb project I have two identical ejb 3.1 In web project I have only one servlet. This servlet call simple test method in first ejb and then in second ejb. First thing in test method is dependency injection for resource session context via this code
#Resource
private SessionContext context;
First ejb works ok, but second (and any following) return null for session context. This is the comlete code:
FirstServlet.java
#WebServlet("/FirstServlet")
public class FirstServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
#EJB
FirstEJB firstEJB = new FirstEJB();
SecondEJB secondEJB = new SecondEJB();
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.println(firstEJB.helloFirst());
out.println(secondEJB.helloSecond());
}
}
FirstEJB.java
#Stateless
public class FirstEJB {
#Resource
private SessionContext contextFirst;
public String helloFirst(){
System.err.println(contextFirst.toString());
return "Hello from FirstEJB";
}
}
SecondEJB.java
#Stateless
public class SecondEJB {
#Resource
private SessionContext contextSecond;
public String helloSecond(){
System.err.println(contextSecond.toString());
return "Hello from SecondEJB";
}
}
Can anybody knows where is the problem.
The first rule for using injection is that your server (otherwise known as the "container") creates the objects that you inject.
In your case the lifecycle of each EJB instance is under the complete control of the container.
In your code, the firstEJB instance that is created by you is replaced by another instance that is created by the container. The secondEJB instance remains the one created by you (it's missing the #EJB annotation), so it has not been subjected to proper lifecycle management and has not been fully constructed with it's #Resource injection processed.
Therefore, a simple change to your servlet code:
#WebServlet("/FirstServlet")
public class FirstServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
#EJB
private FirstEJB firstEJB;
#EJB
private SecondEJB secondEJB;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.println(firstEJB.helloFirst());
out.println(secondEJB.helloSecond());
}
}
should give you the desired result.

Add dynamically-created Spring bean instance to application context for autowired dependency

I need to create a new EmployeeInfoCache instance (not a singleton) from info in the HttpServletRequest that is used to get info from an external app. I then want to give this object as a dependency to non-web-layer objects (where it will be set for all #Autowired references). EmployeeInfoCache itself has no web-layer dependencies (e.g. HttpServletRequest).
Can this be done? I thought about writing a spring interceptor which does the following but I don't know what to do to put an object in the spring context such that it will be used to resolve all #Autowired dependencies.
e.g.
public class MyInterceptor extends HandlerInterceptorAdapter
{
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception
{
- use info from HttpServletRequest to make calls to external app
- create EmployeeInfoCache object w/ this info
- add EmployeeInfoCache to spring application context where it will be used for resolution of #Autowired
}
}
And the remaining code:
// Assume don't have 'Component' or a similar annotation?
public class EmployeeInfoCache
{
...
}
// REST controller that calls the business logic method
#Controller
MyController
{
#Autowired
private MyBusinessObjectInterface myBusinessObject;
#RequestMapping(...)
public #ResponseBody MyResult myMethod(#RequestBody MyObject myObject, HttpServletRequest request, HttpServletResponse response)
{
myBusinessObject.doIt();
}
}
// Non-web-layer code that uses EmployeeInfoCache
#Service(...)
MyBusinessObject implements MyBusinessObjectInterface
{
// I want the EmployeeInfoCache instance created in MyInterceptor to be autowired here
#Autowired
private EmployeeInfoCache employeeInfoCache;
#Override
public void doIt()
{
employeeInfoCache.getName();
}
}
It sounds like you want to use a factory pattern. Have a Spring bean which is the factory method that returns the Cache.
http://kh-yiu.blogspot.in/2013/04/spring-implementing-factory-pattern.html

Gwt Controller With Annotation Approach

I already integrated GWT with Spring MVC by implementing the Controller which is called
by DispatcherServlet using SimpleUrlHandlerMapping.
public class GwtRpcController extends RemoteServiceServlet implements Controller,
ServletContextAware {
#Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
super.doPost(request, response);
return null;
}
}
I want to use the new approach with the annotation #Controller, like below:
#Controller
public class GwtRpcController extends RemoteServiceServlet implements
ServletContextAware {
}
In this case there will be no handleRequest method, where should I do super.doPost(request, response); ?
See annotated web mvc controllers in spring 2.5
You should not to do doPost/doGet manually, just define controller method with corresponding parameters, return value and annonations for url mapping.

Categories