I'm trying to inject a Spring-managed bean and a string value into a servlet. The servlet is defined in context.xml as following:
<bean name="gwtlogging" class="com.somepackage.MyRemoteLogger">
<property name="symbolMapsDirectory" value="/WEB-INF/deploy/gwt/symbolMaps/"/>
<property name="serializationPolicyResolver" ref="serializationPolicyResolver"/>
</bean>
I came across using this method:
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,
config.getServletContext());
}
This works, but has some disadvantages:
I want to inject beans as defined in context.xml and not per autowiring
I need to inject a string value (not a bean), which seems impossible this way
Ideally I want that all values defined in xml will be somehow injected into servlet instance.
If it's available to you, you can declare a Servlet bean in your XML configuration file, use Spring's WebApplicationInitializer (or write your own ServletContainerInitializer) to load the XML file in a WebApplicationContext, retrieve the Servlet bean, and add it to the ServletContext with add(String, Servlet).
The Servlet instance will have been completely prepared through Spring and will be used by your Servlet container.
Related
I am using Hibernate 4.3.7.Final and Log4j2 in my Spring MVC webapp, published via Tomcat 7. All configuration is done via JavaConfig (i.e. there is no web.xml or other XML config files).
By default the Hibernate logging does not go through Log4j, for reasons explained in the Apache wiki. In order to resolve this I need to create a system setting as follows:
System.setProperty("org.jboss.logging.provider", "slf4j");
As my application is a webapp there is no Main thread, and as a result I an unsure where to put this System.setProperty call. Any advice would be appreciated.
You could define this system property in context listener which is the first entry point as below:
#WebListener
public class ContextListenerExample implements ServletContextListener {
public void contextInitialized(ServletContextEvent e){
System.setProperty("org.jboss.logging.provider", "slf4j");
}
}
You could even define system property using spring as below:
<bean id="setupJBossLoggingProperty"
class="org.springframework.batch.support.SystemPropertyInitializer"
p:keyName="org.jboss.logging.provider" p:defaultValue="slf4j"/>
And then you could say something like:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
depends-on="setupJBossLoggingProperty">
...
So this means system property will be setted first and then hibernate bean is going to be initialised.
If you are using some WebApplicationInitializer implementation to bootstrap your Spring application (which I assume you are since you have no web.xml) you could put it in onStartup() method like this:
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
System.setProperty("org.jboss.logging.provider", "slf4j");
}
In my SpringMVC project I need a certain tactic to call a class which does parse a xml file which does own my RMI server ip & port & the url must be relative to servletContext url :
HttpServletRequest request;
request.getServletContext().getRealPath("/WEB-INF/LABLAB/RMI-Config.xml")
I want to load these classes when I start my application in tomcat not when I call a Controller Class because my application depend to my RMI so before anything else I have to parse my file & using the IP & PORT fields to start connecting to my RMI & then call the rmi method to do some stuffs later on ...
now
how I'm gonna do it ? please tell me how I can initialize an instance of HttpServletRequest & give an intial value when I'm not on the Controller classes as well .
Thank you
You are using Spring, then you can create a class and implement IntializingBean. If you want to get hold of ServletContext you can simple use #Autowired annotation in your initializing bean. For e.g.:
#Component
public class SomeBean implements InitializingBean {
#Autowired
private ServletContext context;
public void afterPropertiesSet() throws Exception {
String path = context.getRealPath("/WEB-INF/LABLAB/RMI-Config.xml");
//do something.
}
}
As per docs:
IntializingBean - Interface to be implemented by beans that need to
react once all their properties have been set by a BeanFactory.
Or take a look here how to do this using ServletContextListener.
You need to implement the ServletContextListener interface and refer to it from your web.xml:
<listener>
<listener-class>InitializingListener</listener-class>
</listener>
The interface has a contextInitialized(ServletContextEvent sce) method, in which you can call sce.getServletContext(), so you don't need a HttpServletRequest.
If this doesn't work out, because you also need to access some of your Spring beans from the initializing class, then forget about implementing the ServletContextListener interface, and do the following instead:
Instantiate your initialization class via Spring (make a bean of that type).
Make the class implement ServletContextAware. This will cause Spring to inject the ServletContext into your class.
Define an init method on that bean (use the init-method bean attribute, or define a #PostConstruct annotated method)
Make all your Controller beans depend on the initializing bean by using the depends-on bean attribute. As a result, the initialaizing bean will be created before any Controller bean.
Without the last step, it cannot be guaranteed that your controllers won't start processing requests before the initialization bean finishes its work. However, specifying the depends-on attribute on each and every controller bean is also problematic, especially that they are usually created by applying the #Controller annotation instead of xml configuration. A nice workaround is described in this post.
When I previously developed servlet applications without Spring, I could read configuration parameters like this in my servlet:
#Override public void init() throws ServletException {
ServletContext sc = getServletContext();
String someSetting = sc.getInitParameter("someSetting");
}
However, I'm developing an application with Spring MVC now, which means I no longer have a servlet myself, but use Spring's DispatcherServlet.
Now, how can I access those init parameters in spring-servlet.xml to pass them to one of my beans?
I'm using Spring 3, and am a n00b with it. Every time I see a bean without an id, my mind flips XD
I guess you can #Inject a ServletContext in your controllers, and take the params from there.
Of better, use #Value together with a specificPropertyPlaceholderConfigurer. See this answer
Do you really need these to be init-parameters in the Servlet context?
In Spring you can just inject values into your beans from a properties file using PropertyPlaceholderConfigurer.
I have two Spring contexts declared in my application - one for Spring-MVC requests, and another for Flex/BlazeDS messagebroker requests, mapped to different url-patterns:
<servlet-mapping>
<servlet-name>spring-mvc</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>flex</servlet-name>
<url-pattern>/messagebroker/*</url-pattern>
</servlet-mapping>
There's a common context configuration declared, (/WEB-INF/applicationContext.xml) and then each of the two contexts have their own configurations declared in spring-mvc-servlet.xml and flex-servlet.xml respectively.
Inside flex-servlet.xml I have beans declared which are specific to the flex context. However, when a call comes in to http://localhost/messagebroker/* I'm getting errors that those beans aren't available.
The code in question is inside a custom Spring component, so directly references the WebApplicationContext in order to access the declared beans:
public ISerializer getSerializer(Object source,boolean useAggressiveSerialization)
{
ServletContext ctx = FlexContext.getServletContext();
WebApplicationContext springContext = WebApplicationContextUtils.getRequiredWebApplicationContext(ctx);
String serializerBeanName = springContext.getBeanNamesForType(ISerializer.class);
}
This approach works when I'm running with a single context. However it needs to also support where there are multiple contexts running.
Setting a breakpoint, I see that the value of springContext is the root context, with a single configLocation - /WEB-INF/applicationContext.xml
I'm asssuming that this is the problem - as the ISerializer that the above code requires is declared in flex-servlet.xml.
How do I modify the above code to support both scenarios? (Single context, and multiple contexts)?
EDIT:
The code shown above sits inside a ManageableComponentFactoryBean, which appears to operate as a custom bean factory. It seems that the ApplicationContextAware interface is not honoured on generated classes. Eg:
<bean id="dpHibernateRemotingAdapterComponentFactory"
class="org.springframework.flex.core.ManageableComponentFactoryBean">
<constructor-arg
value="org.dphibernate.adapters.RemotingAdapter" />
<property name="properties">
<value>
{"dpHibernate" :
{
"serializerFactory" : "org.dphibernate.serialization.SpringContextSerializerFactory"
}
}
</value>
</property>
</bean>
The code quoted above sits inside the org.dphibernate.serialization.SpringContextSerializerFactory. Making this SpringContextSerializerFactory implement ApplicationContextAware has no impact.
If flex is a DispatcherServlet, and for some reason you can't follow Tomás Narros's suggestion, you can obtain a context associated with the current DispatcherServlet using RequestContextUtils.getWebApplicationContext(request).
There is also a convenience method RequestContextUtils.getWebApplicationContext(request, ctx), which returns the root context if DispatcherServlet's one is not available.
Declare your custom componente as Spring Context aware:
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public MyCustomBean implements ApplicationContextAware {
private ApplicationContext springContext;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
springContext = applicationContext;
}
public ISerializer getSerializer(Object source,boolean useAggressiveSerialization)
{
String serializerBeanName = springContext.getBeanNamesForType(ISerializer.class);
}
}
At the bean initialization, Spring will access the setApplicationContext method of your bean, passing as an argument the context in wich it's being created. There, you can keep it an use it whenever you need.
Hrmmmm.....I have nearly that exact sort of declaration in my Spring/Flex app, using Spring/Flex integration and there is only one application context. Could that be the problem? You have beans declared in the Flex context file that aren't in the MVC context file, and they aren't really getting loaded?
I need to create a Spring bean so that it stores serverName, serverPort, contextPath properties of a HttpServletRequest object so that I can inject this bean to other beans as I need.
In my opinion, those properties do not change with any URI so that it is good to initialize this once (anyway, passing request instance many times is not so expensive at all).
The problem is, how can I inject HttpServletRequest instance to my configuration bean? I prefer xml based injection. Most probably we need to inject it as a <property> but I do not know what will be name or ref for this ServletRequest object.
The aim is to hold those variables in a bean so that they will be accessible from any bean and I won't need to pass request object to many methods as an argument when I need to obtain serverName etc.
Any ideas how to create such a bean and its configuration?
You can do this using a request-scoped bean, and autowiring the current request into your bean:
public class RequestHolder {
private #Autowired HttpServletRequest request;
public String getServerName() {
return request.getServerName();
}
}
And then in the XML:
<bean id="requestHolder" class="com.x.RequestHolder" scope="request">
<aop:scoped-proxy/>
</bean>
You can then wire the requestHolder bean into wheiever business logic bean you choose.
Note the <aop:scoped-proxy/> - this is the easiest way of injecting a request-scoped bean into a singleton - see Spring docs for how this works, and how to configure the aop namespace.