I'm parsing through some inherited code of a Java app that is deployed as a WAR file in JBOSS, and uses Spring, JMS, and HornetQ. I'm trying to figure out, for lack of a better phrase, what makes the app "go". Most of the Spring examples I've seen include an application with a main() method, which imperatively acts on the beans provided by the Spring context in some way. This application has no main(). As far as I can tell, this is what's happening:
The WAR file's web.xml uses a listener to launch Spring when the application starts in JBOSS, and passes it a config file
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application-context.xml</param-value>
</context-param>
Spring then processes the application-context.xml file, which includes this snippet:
<bean id="jmsContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="destination" />
<property name="messageListener" ref="appListenerProxy" />
Through a couple more references in the application-context.xml, the "appListenerProxy" ultimately refers to a class in the application that implements SessionAwareMessageListener, which responds to messages on a queue.
The part that's tripping me up is that I don't see any kind of code to get the jmsContainer bean and do something with it. Is that a well-defined ID, such that the Spring framework is looking for it? And if so, is that documented somewhere (along with other IDs the framework might be looking for)? http://docs.spring.io/spring/docs/current/spring-framework-reference/html/jms.html seemed to be the closest I found, but it doesn't really specify whether that ID is just in the examples by convention, or if it's a meaningful string.
Thanks
Containers in spring are part of the framework core. The Framework will scan all the containers that implement a certain container interface, and initialize them.
This also relies on the Inversion of Control (IoC) principle.
For more information on IoC Container, check this page:
IoC Container
You don't really need to do anything with DefaultMessageListenerContainer bean, its frame work part.
The DefaultMessageListenerContainer bean establish JMS session with your JMS destination on application startup. When message are received in your destination it invokes the messageListener onMessage method- in your case appListenerProxy bean.
read here.
Related
Is there a decent way to read a spring application context from xml without initializing the beans right away, so they can be mocked or not, before they are actually created?
Yes, I know about lazy-init, but the actual applicationContext.xml is taboo for me.
The situation is that I have to create some JUnit tests on an application which has been created in a way that puts some difficulties in the way:
the configuration file must not be altered, nor the code that is to be tested
there are a lot of beans, some of them rather complex and hard to mock
part of the test is to use as many of the beans un-mocked as possible
some of the beans implement InitializingBean, verifying the environment on initialization and throw errors when Jenkins tries to build.
new FileSystemXmlApplicationContext("config.xml") immediately initializes the beans and throws errors if not in an appropriate environment.
What I have tried:
I have tried Powermock whenNew to mock the offending beans but to do that I would have to know the class which actually creates the beans. As this class belongs to the spring framework, it may change with future versions. When using #PrepareEverythingForTest it results in an StackOverflow exception. The application is real life, not a small piece of code from some tutorial.
I also searched for something like ForceLazyFileSystemXmlApplicationContext but didn't find anything.
Pleas don't start nagging about bad design, I know about that.
You can write your own applicationContext for your testing purpose. There you need to write your own BeanFactory. In that factory you can replace some of beans with mocks.
I just remembered about an option. Evaluate if you can use spring profiles. It will allow to choose a different implementation based on profiles.
Example:
<!-- This is the default myBean -->
<beans>
<bean id="myBean" class="mypackage.MyBean" />
</beans>
<!-- This is the mocked myBean for testing purposes, it will take place when testingProfile is active -->
<beans profile="testingProfile">
<bean id="myBean" class="mypackage.MyBeanMock" />
</beans>
You can indicate which profile to use via properties or environment variables. Example, if you are using maven in your project you could run the tests as:
mvn test -Dspring.profiles.active="testingProfile"
Take a look at:
http://www.baeldung.com/spring-profiles
https://spring.io/blog/2011/02/11/spring-framework-3-1-m1-released/
I want to be able to reference environment variables in web.xml, something along the lines of this:
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:conf/log4j-${SERVER_ENVIRONMENT}.properties</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4j2ConfigListener</listener-class>
</listener>
<param-value>classpath:conf/log4j-${SERVER_ENVIRONMENT}.properties</param-value>
This question is similar to mine, and the most upvoted answer claims that:
If you are using Spring, you can create a bean and then directly use envvars or sysprops in Spring XML config files.
However, I don't understand how to do that. I've added this bean:
<context:property-placeholder />
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="serverEnvironment" value="${SERVER_ENVIRONMENT}"></property>
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
But I don't know if I'm doing it right, nor how to access that variable from web.xml. How should I define that bean, and how can I use its value in web.xml?
If you're willing to forego using XML configuration in favor of Java config it's quite easy to reference environment variables there. Just #Autowire Environment in your config class, then you can do something like
#Autowired
Environment environment;
#Bean
public String getServerEnvironment(){
return this.environment.getProperty("SERVER_ENVIRONMENT");
}
Then just call your bean and you're done. This assumes the property either exists in your environment or you've passed it in with -DSERVER_ENVIRONMENT at runtime.
Or you could just use Spring's #Profile annotation and make your logs environment dependent using that (several tutorials on that also).
Incidentally Spring Boot makes this process even easier.
I'm a newbie with Spring MVC but now i've been moved to a new project which uses Spring Integration to turn in channel some service. Example in the context.xml
<int:channel id="example-channel" />
<int:service-activator input-channel="example-channel" ref="exampleServiceFacade" />
For every servicefacade i have to bind the service to a channel.
I was wandering, what if I could map the classes to be turned into channels as i could map the beans with component-scan?
<context:component-scan base-package="com.package" />
so i ended up with this tutorial which speaks about some annotation:
#IntegrationComponentScan
But i cannot understand how it's related to the xml tag service activator and channel.. So i'm quite confused. Does anyone with more experience have an idea if what i'm trying to do can be done?
I just want to scan con the classes which defines channels in integration without having to declare every class.
Thanks.
Your question is a bit unclear. Any Spring Integration custom XML tag is parsed by infrastructure and registered as beans in the application context. Like you'd do that via raw <bean>.
#ComponentScan, #Configuration, #Bean and so on are marker annotations to say application context which classes treat as beans.
So, using both techniques for application context configuration you don't lose anything and can continue to mark you class with #Service and use its bean name from <service-activator ref="">.
From other side now you can fully build Spring Integration without any XML! Please, read the mentioned doc in its entirety.
I did not know how to turn the title, hope this is understandable. I am working on a web-app that have existed for years and is currently in production. This web-app uses spring-flex to display some views and one of the dependency of spring-flex is spring-mvc. I think a relevant link would be there.
So I have all the dependencies already configured in my pom, and a DispatcherServlet already configured in my web.xml with the exact configuration from the manual (I am not the one who did the integration, I am trying to figuring out how it was done).
<servlet>
<servlet-name>flex_servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>flex_servlet</servlet-name>
<url-pattern>/messagebroker/*</url-pattern>
</servlet-mapping>
From the link I have provided, we can see that by default when a flex message-broker is configured, the configuration looks like this (although it is nowhere to be seen in the project)
<!-- Maps request paths at /* to the BlazeDS MessageBroker -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/*=_messageBroker
</value>
</property>
</bean>
<!-- Dispatches requests mapped to a MessageBroker -->
<bean class="org.springframework.flex.servlet.MessageBrokerHandlerAdapter"/>
Now that I want to use spring-mvc for other tasks, what should I do?
Declare another DispatcherServlet or use the same one?
And If I use the same one, can I just copy-past the above block and complete it to override the default configuration?
And if I don't use the same one, how will each DispatcherServlet know which HandlerMapping to use since they are supposed to discover it themselves?
thanks for your help
EDIT: for future reference, here are relevant documentations:
flex and spring-mvc
SimpleUrlHandlerMapping
Multiple UrlHanlderMappings
With BlazeDS you define a named service, the DispatcherServlet will use this name to send requests to the proper service.
So to answer your questions:
Declare another DispatcherServlet or use the same one? Use the same one
And If I use the same one, can I just copy-past the above block and complete it to override the default configuration? No change needed
And if I don't use the same one, how will each DispatcherServlet know which HandlerMapping to use since they are supposed to discover it themselves? Irrelevant, you use the same one
Based on our conversation below, the answer from this question might help you aswell:
Spring MVC: RESTful web services + BlazeDS integration possible in the same web application?
A colleague of mine and I are playing around with the following spring configuration:
<beans>
<context:property-placeholder location='classpath:/configuration.properties'/>
<bean id="myBean" class="${type}" />
</beans>
We want to be able to provide a environment specific implementation of myBean. On a developers system the value of type would be a lightweight implementation of whatever myBean does. And in a production environment we would use a full-blown version.
When my colleague runs the code, everything works. When I run the code, I get a ClassNotFoundException, because spring tries to instantiate ${type}.class. And it is not like it sometimes works and sometimes does not. On my machine it always fails and on my colleagues machine it always works.
Does anybody knows what the problem is?
Thx in advance,
Yevgeniy
UPDATE
as requested, here is how we load the application context:
ClassPathXmlApplicationContext("application-configuration.xml");
the content of the properties file is pretty simple:
type=foobar.TestServiceImpl
Instead of trying to override the class with a placeholder, I would like to suggest an alternative approach for your problem. You could use the Profile functionality of Spring.
It would be simplier and safer to change the class depending of the environment.
<beans>
<beans profile="dev">
<bean id="myBean" class="dev.impl.MyBean" />
</beans
<beans profile="prod">
<bean id="myBean" class="prod.impl.MyBean" />
</beans
</beans>
You can then activate a given profile in development by adding the following system property to your server -Dspring.profiles.active="dev".
You can define a default profile which will be used by adding the following to your web.xml:
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>prod</param-value>
</context-param>
The following assumes Spring 3.1+.
I can tell you this much for sure. For Spring to fail with a ClassNotFoundException for class
${type}
means that it did not resolve the placeholder.
When you specify
<context:property-placeholder location='classpath:/configuration.properties'/>
Spring uses a PropertyPlaceholderBeanDefinitionParser to register either a PropertySourcesPlaceholderConfigurer or a PropertyPlaceholderConfigurer bean which will do the placeholder resolution.
PropertySourcesPlaceholderConfigurer is a BeanFactoryPostProcessor. This mean that it can modify bean definitions. Without going into much detail, if it cannot resolve a placeholder, the process fails with an IllegalArgumentException that states that the placeholder could not be resolved.
If you're saying that the ${type} wasn't resolved, then no PropertySourcesPlaceholderConfigurer or PropertyPlaceholderConfigurer beans were created. This probably means your context does not have
<context:property-placeholder location='classpath:/configuration.properties'/>
With the information you've shown us, that is what I think is going on. If you can prove otherwise, I'll ask you to provide a small reproducible example. Ideally, you would show the contents of your compiled project.
As far as I remember property placeholder fails mutually. This means that if it cannot locate file configuration.properties on your machine the property is just not initialized.
To approve this assumption try to "break" the application on your colleague's machine: change the location to something wrong, e.g. classpath:/configuration12345.properties. I believe that the problem will appear on his machine too.
Now, check what's wrong in your environment and why this file cannot be found there.
BUT: do you have something against profiles? Spring provides a cool feature that is attended exactly for your use-case: spring profiles.