How to embed Jetty into Spring and make it use the same AppContext it was embedded into? - spring

I have a Spring ApplicationContext where I declare Jetty server bean and start it. Inside Jetty I have a DispatcherServlet and a couple of controllers. How to make that DispatcherServlet and its controllers use beans from the same ApplicationContext where Jetty is declared?
In fact, in that outer context I have a couple of daemon-like beans and their dependencies. Controllers inside Jetty use the same dependencies, so I'd like to avoid duplicating them inside and outside Jetty.

I did this a while ago.
Spring's documentation suggests that you use a ContextLoaderListener to load the application context for servlets. Instead of this Spring class, use your own listener. The key thing here is that your custom listener can be defined in the Spring config, and can be aware of the application context it's defined in; so instead of loading a new application context, it just returns that context.
The listener would look something like this:
public class CustomContextLoaderListener extends ContextLoaderListener implements BeanFactoryAware {
#Override
protected ContextLoader createContextLoader() {
return new DelegatingContextLoader(beanFactory);
}
protected BeanFactory beanFactory;
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
and the DelegatingContextLoader does this:
public class DelegatingContextLoader extends ContextLoader {
protected BeanFactory beanFactory;
public DelegatingContextLoader(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
#Override
protected WebApplicationContext createWebApplicationContext(ServletContext servletContext, ApplicationContext parent) throws BeansException {
return new GenericWebApplicationContext((DefaultListableBeanFactory) beanFactory);
}
}
It's a bit messy, and can probably be improved, but this did work for me.

Related

Why Servlet Filter can be Spring Bean?

What i know about Filter and Interceptor is that Filters as J2EE Specifications are part of the webserver and not the Spring framework. So some older articles explain that it is impossible to register filters as Spring Bean while Interceptor is possible.
But the results I got when I tested today is that Filters can be Spring Bean and also inject Spring Bean on Filters are possible too like Interceptors.
(I tested on SpringBoot Framework)
#Component
public class CustomFilterTest implements Filter {
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws ServletException, IOException {
chain.doFilter(request, response);
}
#Override
public void init(final FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
#Override
public void destroy() {
Filter.super.destroy();
}
}
#RestController
#RequiredArgsConstructor
public class ProductController {
private final CustomFilterTest customFilterTest;
#GetMapping("/test")
public ResponseEntity<Void> temp() {
System.out.println(customFilterTest);
return ResponseEntity.noContent().build();
}
}
Can anyone please explain to me?
We have to make a distinction between a regular Spring application and a Spring Boot application here. As with both, you can register a servlet filter as a bean, but the mechanism is a bit different.
Spring Framework
In plain Spring use the DelegatingFilterProxy to achieve this. The task of the DelegatingFilterProxy is to look for a bean with the same name as the filter in the root application context (the ApplicationContext registered through the ContextLoaderListener). This bean has to be your managed servlet filter.
#Configuration
#EnableWebMvc
public class WebConfiguration {
#Bean
public void YourFilter myFilter() { ... }
}
Then for the web application you would register a DelegatingFilterProxy with the name myFilter to make this work.
public class MyWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
public void onStartup(ServletContext servletContext)
throws ServletException {
super.onStartup(servletContext);
servletContext.addFilter("myFilter", DelegatingFilterProxy.class);
}
Spring Boot
In Spring Boot it is a bit different as Spring Boot is also in control of your servlet container, like Tomcat. It basically means that Tomcat is also a managed bean in your ApplicationContext and Spring Boot can inject dependencies into it. So when Spring Boot detects a bean for the servlet filter it will automatically add it to the filter chain (without the need of a DelegatingFilterProxy).
Which means only an #Bean for your filter is needed.
#Configuration
public class WebConfiguration {
#Bean
public YourFilter myFilter() { ... }
}
Additionally you can configure things like URLs etc. by adding an additional FilterRegistrationBean for this filter.
Conclusion
For plain Spring the DelegatingFilterProxy has been around since Spring 1.2 which was released in 2005. This means if you are reading really, really, really old articles (before 2005) this was true, however with the addition of the DelegatingFilterProxy, this isn't anymore. With the release of Spring Boot, this became even a lesser issue, is it more or less is the only way to register a filter (as a managed bean).

Springboot>WebServlet - Pass spring container

I have springBoot standalone application. I used #SpringBootApplication, #ServletComponentScan annotations in my standalone application. All my components, beans getting initialized in spring container and prints in the application startup.
Inside my servlet, i invoke handler and beans were coming as null. How do i pass spring container through my servlet ?
#SpringBootApplication
#ServletComponentScan
public class AStandaloneApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(AStandaloneApplication.class, args);
}
}
#WebServlet("/ba")
public class BAServlet extends SpeechletServlet {
#Autowired
private BASpeechletHandler bASpeechletHandler;
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
this.setSpeechlet(bASpeechletHandler);
}
}
public class BASpeechletHandler implements Speechlet {
#Autowired
private BSEngine bSEngine;
#Autowired
private IBotResponseObjToAlexaSpeechletResponseObj botResponseObjToAlexaSpeechletResponseObj;
}
The bASpeechletHandler is null in servlet, if i instatiate object in my servlet for bASpeechletHandler and move on then components, services and repository inside bASpeechletHandler also null.
Thanks.
1.Add the packages to component scan - similar to this
#ServletComponentScan(basePackages="org.my.pkg")
2.Add one of the #Component annotations into your BASpeechletHandler class.
This will make that class eligible for auto-discovery of beans.
May be i little complication in asking. I found the solution. In Web applicationContext i pinged the spring context and got the bean.
private ApplicationContext appContext;
private BASpeechletHandler bASpeechletHandler;
public void init(ServletConfig config) throws ServletException {
super.init();
appContext = (ApplicationContext) config.getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
bASpeechletHandler = (bASpeechletHandler) appContext.getBean("bASpeechletHandler");
}
Thanks.

Dynamic context - autowiring

I am dynamically adding contexts to the application context with the following code:
#Component
#Scope("singleton")
public class DynamicContextLoader implements ApplicationContextAware {
private static ApplicationContext context;
private Map<String, InterfacePropertyDto> contextMap;
#Autowired
IJpaDao jpaDao;
#PostConstruct
public void init() {
contextMap = (Map<String, InterfacePropertyDto>) context.getBean("contextMap");
contextMap.forEach((contextName, property) -> {
String p = jpaDao.getProperty(property.getPropertyName(), property.getPropertyType());
if (p != null) {
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[]{"/META-INF/spring/integration/" + contextName},
false, context);
ctx.refresh();
}
});
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
}
This works well and all the beans defined in the new context are created. However the #Autowired does not work for any of these new beans.
For example a bean defined in the new context as:
<bean id="outboundContractJdbcFileMapper" class="com.......integration.model.contract.ContractMapper"/>
has the following autowiring:
public class ContractMapper implements RowMapper<ContractFile> {
#Autowired
IIntegrationDao integrationDao;
#Override
public ContractFile mapRow(ResultSet rs, int rowNum) throws SQLException {
......
}
}
At runtime the outboundContractJdbcFileMapper property integrationDao is null.
Is there a way to force the autowiring to occur when the beans are created? I was hoping that ctx.refresh() would do this.
That doesn't work automatically for the ClassPathXmlApplicationContext. You have to add <context:annotation-config/> to that child context as well:
<xsd:element name="annotation-config">
<xsd:annotation>
<xsd:documentation><![CDATA[
Activates various annotations to be detected in bean classes: Spring's #Required and
#Autowired, as well as JSR 250's #PostConstruct, #PreDestroy and #Resource (if available),
JAX-WS's #WebServiceRef (if available), EJB 3's #EJB (if available), and JPA's
#PersistenceContext and #PersistenceUnit (if available). Alternatively, you may
choose to activate the individual BeanPostProcessors for those annotations.
Note: This tag does not activate processing of Spring's #Transactional or EJB 3's
#TransactionAttribute annotation. Consider the use of the <tx:annotation-driven>
tag for that purpose.
See javadoc for org.springframework.context.annotation.AnnotationConfigApplicationContext
for information on code-based alternatives to bootstrapping annotation-driven support.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>

how to get beans from application context loaded by contextloaderlistener?

I'm new to Spring/Spring Mvc and here's my problem. In my webapp, besides the spring-servlet.xml , I have a jdbc.xml which define beans like datasource, dao ... Before using contextloaderlistener , I load my jdbc.xml inside the Controller's constructor like this ApplicationContext context = new ClassPathXmlApplicationContext("jdbcbeans.xml") then get the beans from that. But since I'm using contextloaderlistener to load the file, how can I get the reference to the context ? I was able to set up everything using those #Autowired things but I just want to know is there any way to do that ?
You can use WebApplicationContextUtils.
ApplicationContext context;
context = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
See here for details.
You can do the following to get an instance of Application Context
in case of a container managed bean use ApplicationContextAware interface
public class MyBean implements ApplicationContextAware {
private static ApplicationContext context;
public void setApplicationContext(ApplicationContext acontext) throws BeansException {
context = context;
}
public static ApplicationContext getApplicationContext() {
return context;
}
}
Or you can write the following
#Autowired
private ApplicationContext Context;
An instance of the Application Context will be autowired.

Correct usage subclassing a spring ContextLoader for testing

For the integration tests for my spring application with junit I am subclassing org.springframework.test.context.ContextLoader, because I want to use a already present XmlWebApplicationContext for wiring up my test class like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader=MyContextLoader.class)
#Transactional
public class MyTest {
#Autowired
public AccountDao accountDao;
}
The implementation of my ContextLoader is as follows:
public class MyContextLoader implements ContextLoader {
#Override
public String[] processLocations(Class<?> clazz, String... locations) {
return locations;
}
#Override
public ApplicationContext loadContext(String... locations) throws Exception {
try {
// Start Embedded Tomcat
EmbeddedTomcat tomcat = new EmbeddedTomcat("mas", 8080);
tomcat.launch();
Context rootContext = tomcat.getRootContext();
ContextLoaderListener contextLoaderListener = (ContextLoaderListener) rootContext.getApplicationLifecycleListeners()[0];
XmlWebApplicationContext context = (XmlWebApplicationContext) contextLoaderListener.getContext();
GenericApplicationContext c = new GenericApplicationContext(context);
AnnotationConfigUtils.registerAnnotationConfigProcessors(c);
//context.refresh();
//context.registerShutdownHook();
return context;
}
catch(Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
When putting a breakpoint in the loadContext(...) method I can call getBean(AccountDao.class) and everything works fine. However, it seems that my test class actually is not autowired. I debugged a little and stepped through the spring code and it seems that in the method AbstractAutowireCapableBeanFactory.populateBean(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw) the PropertyValues are not set for my class Test.
Maybe, am I setting up the Annotation Processing wrong?
Information to the code: As you might guess and see, I am doing an integration test and therefore starting an embedded tomcat server in order to test my RESTful webservice. How getting the application context with a "hack" from an embedded tomcat is shown in my post here: Getting Access to Spring with Embedded Tomcat 6
I am looking forward to your replies.
Erik
I think the problem here is that you're creating a new GenericApplicationContext, which is not the one that Spring uses to autowire the test bean.

Resources