Difference Between web-app's context-param and servlet's init-param? - java

I'm using Spring MVC. In a Controller class, I want to use the #Value annotation to inject a value that comes from a properties file:
#Value("${upload.dir}")
private String uploadDir;
So I need to put a property-placeholder somewhere.
The web.xml is typical:
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/mvc-dispatcher-servlet.xml</param-value>
</init-param>
...
</servlet>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/business-context.xml</param-value>
</context-param>
First, I put the placeholder in business-context.xml. It doesn't work: "Could not autowire field".
Then I put it in mvc-dispatcher-servlet.xml, it works.
So I'm confused about these two contexts, are they the same one or different? Because the beans I defined in business-content.xml can be autowired, but the #Value doesn't work.
I don't want to put the placeholder in both xml files 'cause I have a long 'location' property. Also the business-context.xml will be used by some jobs, so it cannot be omitted.
Any way to make placeholder defined in business-context.xml become visible in mvc-dispatcher-servlet.xml as well?

A BeanFactoryPostProcessor which is what the property-placeholder is will only operate (and be visible) to the application context it is defined in. This is by design. So no you cannot make a property-placeholder from a parent visible to a child context (well with some nasty hacks you could).
As a work around you could do the following in your business-context.xml
<util:properties id="applicationProperties" location="path-to-your-very-long-location" />
<context:property-placeholder properties-ref="applicationProperties" />
and this in your mvc-dispatcher-servlet.xml.
<context:property-placeholder properties-ref="applicationProperties" />
Define the same <context:property-placeholder ../> in both xml context and simply reference the already loaded properties. Added advantage the properties are only loaded once.

Related

Get Spring profile name with spring EL

Consider a web based application with spring 4. The spring bean profiles is defined in web.xml like:
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>prod,edb,cas</param-value>
</context-param>
Now consider a bean is defined in spring-applicaiton-context.xml as
<util:properties id="myPolicy"
location=
"classpath:/configs/${ACCESS-ACTIVE-PROFILE-SECOND-ITEM}/my-policy.properties" />
Is it possible that I can access the list of active profiles and select the second one (in my example edb). In this way I can make my resource load dynamically when active profile changes.
This may help! I could get the active profile when web application starts with below code:
public void contextInitialized(ServletContextEvent event){
ApplicationContext applicationContext = WebApplicationContextUtils
.getWebApplicationContext(event.getServletContext());
String activeProfiles[] = applicationContext.getEnvironment().getActiveProfiles();
system.out.print(activeProfiles[1])
}
The syntax would be "#{environment.activeProfiles[1]}" - however, it's too early in the context life cycle; the activeProfiles is not set up before the SpEL is evaluated in this case.
What's wrong with
<beans profile="foo">
<util:properties id="myPolicy"
location="classpath:/configs/foo/my-policy.properties" />
</beans>
<beans profile="bar">
<util:properties id="myPolicy"
location="classpath:/configs/bar/my-policy.properties" />
</beans>
?
Actually, I just found that
"#{environment.getActiveProfiles()[1]}"
works - explicitly calling the getter causes the property to be loaded.

Create bean in spring context

<bean id="configuration" class="com.mypackage.util.Configuration" factory-method="getInstance">
<property name="path" value="${path.props.app.dev}"></property>
</bean>
Then I have the following in my class
Configuration.getInstance();
Whereas the spring application context is loaded in another class Factory like this
private Factory() {
context = new ClassPathXmlApplicationContext("META-INF/spring.xml");
}
The problem is that before Factory class is accessed the context does not load and the configuration object gives null for path whereas when Factory is accessed and after that path property is accessed it gives the correct path.
Please tell me how to do it correctly? That is how can i get my member variable path with correct data without accessing Factory class.
Assuming that you are using Spring WebMVC. There are 2 ways:
Putting you bean configurations to dispatcher config XML (mvc-dispatcher-servlet.xml)
Remain your spring.xml and specify it in web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>spring.xml</param-value>
</context-param>
In both cases, you will no longer need a class like Factory. Besides, because of that Spring creates beans in singleton scope by default, you do not need to implement a getInstance() method for your com.mypackage.util.Configuration class.

Bean in DispatcherServlet accessing sibling bean

We have a web application with both a root application context (applicationContext.xml) and a dispatcher servlet (dispatcher-servlet.xml) defined in our web.xml like so:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext*.xml</param-value>
</context-param>
...
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
Due to the access restrictions, we obviously cannot access any of the DispatcherServlet beans from the root parent context.
But, what we'd like to be able to do is access the sibling beans. For example, in our dispatcher-servlet.xml we have:
<bean id="firstController" class="org.springframework.web.servlet.mvc.multiaction.MultiActionController">
...
</bean>
<bean id="secondController" class="org.springframework.web.servlet.mvc.multiaction.MultiActionController">
...
</bean>
How can we access the secondController from inside firstController, without requiring that it be passed in as a constructor-arg, or set as a property?
We're using WebApplicationContextUtils.getWebApplicationContext(...) to access siblings in the root applicationContext.xml, but we'd like to do the same thing in the child context. (From within one of the children.)
you can get an ApplicationContext object using
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("dispatcher-servlet.xml")
This is assuming dispatcher-servlet.xml is in class path. context object has to be cached.
Then use context.getBean() to access beans from any where.
Other option seems to be , use overloaded method where we can pass the context attribute name. I have not used it.
WebApplicationContextUtils.getWebApplicationContext(ServletContext sc, String attrName)

RESTEasy will not map my Spring bean with custom Spring ContextLoader

RESTEasy 2.0.1GA
Java 1.6
Spring 3.0.3
I have tried everything I can, and cannot make head or tail of what's going on. I have a Spring MVC application, however I'd like to have some RESTEasy endpoints available outside the Spring MVC app, but in the same container, ultimately being able to wire in the same beans.
As a first step, I'm simply trying to stand-up RESTEasy inside the container, serving requests from a Spring-configured bean. I have tried the boilerplate from the instructions and have also tried manual setup, to no avail.
Bean
#Resource
#Path("/")
public class NeighborComparison {
private String foo;
#GET #Path(value="customer") #Produces("text/plain")
public String getNeighborComparison() {
return "foo";
}
}
web.xml
<context-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/api</param-value>
</context-param>
<listener>
<listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>
<!-- NOT configuring SpringContextLoaderListener because I declare my own, so if I do, everything
blows up, plus all it actually does is sanity check configuration -->
<listener>
<listener-class>com.example.MyCustomContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>Resteasy</servlet-name>
<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Resteasy</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
applicationContext.xml
<bean id="resteasy.providerFactory" class="org.jboss.resteasy.spi.ResteasyProviderFactory"
factory-method="getInstance">
</bean>
<bean id="resteasy.dispatcher" class="org.jboss.resteasy.core.SynchronousDispatcher">
<constructor-arg ref="resteasy.providerFactory"/>
</bean>
<bean id="resteasy.spring.bean.processor" class="org.jboss.resteasy.plugins.spring.SpringBeanProcessor">
<description>
Add Resources and #Providers to the appropriate places
in Resteasy's infrastructure
</description>
<constructor-arg ref="resteasy.dispatcher"/>
</bean>
<bean id="neighborComparison" class="opower.api.customer.neighbor_comparison.NeighborComparison">
</bean>
According to the documentation, all I have to do is “manually register the RESTeasy BeanFactoryPostProcessor by allocating an instance of org.jboss.resteasy.plugins.spring.SpringBeanProcessor”. I believe this spring configuration does that.
Jetty starts and the app context spins up with no issues. Application works normally, however when I
> curl -H"Accept: text/plain" localhost:8080/ei/api/customer
("ei" is the application context). The log shows (this and only this):
2011-03-29 16:44:24,153 DEBUG [qtp-575315405-0] [EI] [] [asy.core.SynchronousDispatcher] PathInfo: /customer
2011-03-29 16:44:24,156 DEBUG [qtp-575315405-0] [EI] [] [asy.core.SynchronousDispatcher] Failed executing GET /customer
org.jboss.resteasy.spi.NotFoundException: Could not find resource for relative : /customer of full path: http://localhost:8080/ei/api/customer
Even if I could convince RESTEasy to show me the mappings, it seems that it's just not discovering my bean.
If I map it explicitly via the resteasy.resources context param, it works, though obviously doesn't have access to auto-wired Spring beans.
Anything else I can try? I have debug log on the entire RESTEasy codebase and I don't get any messages. I've also confirmed that Spring is, in fact, creating my bean, so it's just that RESTEasy isn't finding it.
Your resource class needs to be annotated with #Path annotation for RESTeasy to pick up on it during bootstrap:
#Path("/customer")
#Resource
public class NeighborComparison {
#GET #Path("/{customerId}") #Produces("text/plain")
public String getNeighborComparison(#PathParam("customerId") long customerId) {
return "foo";
}
}
Note the #Path("/{customerId}} annotation without which your #PathParam parameter would not have been mapped correctly, resulting in a pretty detailed exception (and an accompanying 500 response on the client side). Assuming the service is picked up by RESTeasy of course.
In addition if you don't use RESTeasy's SpringContextLoader, you have to make sure your SpringBeanProcessor instance is registered with the ApplicationContext. RESTeasy delegates to it by registering an ApplicationListener in SpringContextLoader:
ApplicationListener listener = new ApplicationListener() {
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
ContextRefreshedEvent cre = (ContextRefreshedEvent) event;
ConfigurableListableBeanFactory autowireCapableBeanFactory = (ConfigurableListableBeanFactory) cre
.getApplicationContext().getAutowireCapableBeanFactory();
new SpringBeanProcessor(dispatcher, registry, providerFactory)
.postProcessBeanFactory(autowireCapableBeanFactory);
}
}
};
configurableWebApplicationContext.addApplicationListener(listener);
If using a custom context loader and not the RESTEasy-provided one, this code has to appear somewhere in your context loader so that everything gets wired up. A bit convoluted, yeah. It is SpringBeanProcessor that goes through all Spring beans and registers with RESTeasy those that have a #Path annotation somewhere in their hierarchy (type and their corresponding interfaces).

Where do you define spring bean configuration files

I am separating my spring bean configuration files as follows:
myapp-service.xml
myapp-servlet.xml
However I am getting the error;
Error creating bean with name 'beanName' defined in ServletContext resource [/WEB-INF/myapp-servlet.xml]: Cannot resolve reference to bean 'beanService' while setting bean property 'beanService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'beanService' is defined
All I need to do (I think) is figure out how to tell Spring to read the myapp-service.xml file where the path to beanService is defined.
Which file/location is that done in?
Thanks
It's defined in your web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
Alternatively in myapp-servlet.xml you could put:
<import resource="myapp-service.xml"/>
if you like to include more applicationContext files and are indeed developing a web application:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext-1.xml,
/WEB-INF/applicationContext-2.xml
</param-value>
</context-param>
also wildcarding works, applicationContext* will have the same effect here.
if you are bootstrapping spring context by hand e.g from code:
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] { "applicationContext-1.xml", "applicationContext-2.xml" });

Categories