We currently have a Spring web application and are doing our configuration using XML files. We are starting Spring the DispatcherServlet which creates an XmlWebApplicationContext and loads it from the default location: spring-servlet.xml.
I am specifying several additional configuration files using the context-param contextConfigLocation. This loads up our entire application from the XML files.
So here's what I want to do. The XML file contains the database connection information and our DAOs for accessing these tables. I want to use one of those DAOs to read a value from the database and load an additional set of beans from the XML file.
So if the database value retrieved is orange, I want to load beans from orange.xml. If it's apple, I want to load apple.xml. I want these beans to be part of the same application context so after they're loaded, I can move forward without noticing the difference.
I'm wondering if I should implement my own sub-class of XmlWebApplicationContext and have DispatcherServlet implement that, but I'm not quite sure how to proceed with that.
Not exactly loading from the different files, but you can try to use Spring Environment and Profile abstractions.
<beans profile="apple">
<bean id="someBean">
...first set of bean parameters...
</bean>
</beans>
<beans profile="orange">
<bean id="someBean">
...second set of bean parameters...
</bean>
</beans>
And in java:
context.getEnvironment().setActiveProfiles("orange");
context.refresh();
You could use a BeanFactoryPostProcessor to load the configuration.
For example, if you have a LocationService that give the config locations as String[]:
public class XmlBeanDefinitionReaderPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) beanFactory);
ResourceLoader resourceLoader = new DefaultResourceLoader();
reader.setResourceLoader(new DefaultResourceLoader());
reader.setEntityResolver(new ResourceEntityResolver(resourceLoader));
reader.setEnvironment(new StandardEnvironment());
LocationService locationService = (LocationService) beanFactory.getBean("locationService");
reader.loadBeanDefinitions(locationService.getLocations());
}
}
Is not exactly the same as the reader is unaware of the already loaded beans and could be aliases or bean names colisions.
Note that your LocationService should not use Autorwire, AOP Transactional Proxies, and something that in general implies the use of BeanPostProcessors.
Other option to reuse the same XmlBeanDefinitionReader is overriding postProcessBeanFactory method in XmlWebApplicationContext:
public class CustomWebApplicationContext extends XmlWebApplicationContext {
private XmlBeanDefinitionReader reader;
#Override
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
this.reader = reader;
super.loadBeanDefinitions(reader);
}
#Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
LocationService locationService = (LocationService) beanFactory.getBean("locationService");
this.reader.loadBeanDefinitions(locationService.getLocations());
super.postProcessBeanFactory(beanFactory);
}
}
We ended up extending XmlWebApplicationContext and overriding the loadBeans method. We load the beans, look up the bean that provides our configuration, then switch profiles and run again with the new profiles.
Thanks for all the help.
Related
The TeamCity plugin API allows adding controllers by extending their BaseController, which is a thin wrapper around Spring's AbstractController.
When I extend BaseController I can inject beans into the constructor in usual Spring manner. This is managed by a beans definition file like standard spring.,
To provide a Controller I must extend BaseController, override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response), and add the controller to the beans definition. I register a URL route as part of constructor initialisation.
That's about all the extension points available to me for Controller.
I was hoping to be able to write small framework that would allow me to annotate my classes with #RestController and #RequestMapping, etc.
What I think I need to do is:
Wire up an annotation processor to find my controllers and their methods.
Build some sort of mapper which maps #RequestMapping annotated methods to routes
Wire up some content handlers, eg serialising/unserialising for JSON and XML
Dispatch incoming requests to the appropriate method inside my handleRequest method
Most of the above has already been written in Spring and Jersey, and I am wondering where I start researching that.
What classes provide the above functionality?
I've tried a few things to try and instantiate MVC, but it seems to break when ApplicationContext is not available.
Not a TeamCity user. However I'll give my two cents, hoping for the best.
Being that you can register Spring Bean(s), why not trying out ClassPathBeanDefinitionScanner?
If you have access somehow to a Spring lifecycle hook, an initialization one, or if you are able to register a Configuration class, you can create a Bean of type BeanFactoryPostProcessor. I'll give you a Java example, but you should be able to translate it to XML pretty quickly.
#Bean
public BeanFactoryPostProcessor beanFactoryPostProcessor(final ApplicationContext applicationContext) {
return new BeanFactoryPostProcessor() {
#Override
public void postProcessBeanFactory(final ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof BeanDefinitionRegistry) {
try {
final BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
final ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry);
scanner.setResourceLoader(applicationContext);
scanner.scan("your.base.package");
} catch (final Exception e) {
// Handle
}
}
}
};
}
The ClassPathBeanDefinitionScanner will register all classes with Spring stereotype annotations.
You can also make a class implement the BeanFactoryPostProcessor interface directly.
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");
}
I'm working with Spring 3.0 and jUnit 4.8 and I'm trying to develop some unit test .
In fact, I'm only trying to set a property (a file) of a bean using dependency injection in a test case definined in the XML loaded in the application context used by the jUnit.
I'm using an XML file configuration loaded using the annotation for jUnit 4 aproach. This is the main BaseTest that all test classes used:
#ContextConfiguration("/test-context.xml")
#RunWith(SpringJUnit4ClassRunner.class)
#Ignore
public class BaseTest { ... }
And this is a section of the test-context.xml
<context:component-scan base-package="com.test" />
<bean id="ExtractorTest" class="com.test.service.ExtractorTest">
<property name="file" value="classpath:xls/SimpleFile.xls"></property>
</bean>
So what I'm trying to do in my class with the test (ExtractorTest) is only set the 'file' property with a file loaded within the classpath, nothing else.
Here is a section of the class with the test:
public class ExtractorTest extends BaseTest {
private Resource file;
private InputStream is;
public void setFile(Resource file) {
this.file = file;
}
#Before
public void init() {
this.is = this.getClass().getResourceAsStream("/xls/SimpleFile.xls");
Assert.assertNotNull(is);
}
#Test
public void testLoadExcel() throws IOException {
// file is always null, but the InputStream (is) isn't!
Assert.assertNotNull(file.getFile());
InputStream is = new FileInputStream(file.getFile());
HSSFWorkbook wb = new HSSFWorkbook(new POIFSFileSystem(is));
// todo...
}
}
The problem is that the setter works, because I've added a breakpoint and Spring is setting it's property ok. But when the test method starts it's null, may be because it's another instance that is running, but why? How I can set the 'file' to be loaded using the XML of the application context for testing? I'm not able to assign it using jUnit and I don't understand why and how to do it. I'm trying to avoiding writing in the #Before method, but I don't know it's a good approach definitely...
Thank you.
PD: Sorry about my english ;-)
Your configuration doesn't work because Spring doesn't create the instance of ExtractorTest which JUnit uses; instead the instance is created by JUnit and then passed to Spring for post processing.
The effect you see is because the application context creates a bean with the id ExtractorTest but nobody ever uses that.
Pseudocode:
ApplicationContect appContext = new ...
appContext.defineBean("ExtractorTest", new ExtractorTest()); // Calls setter
ExtractorTest test = new ExtractorTest(); // Doesn't call setter
test.postProcess(appContext); // inject beans from appContext -> does nothing in your case
So the solution is to define a bean file:
<bean id="file" class="..." />
(see the documentation how to build a Resource bean) and then let Spring inject that:
#Autowired
private Resource file;
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?
When using database migrations, I obviously want none of the DAOs to be usable before the migrations are run.
At the moment I'm declaring a lot of DAOs, all having a depends-on=databaseMigrator property. I find this troubling, especially since it's error prone.
Is there a more compact way of doing this?
Notes:
the depends-on attribute is not 'inherited' from parent beans;
I am not using Hibernate or JPA so I can't make the sessionFactory bean depend-on the migrator.
You could try writing a class that implements the BeanFactoryPostProcessor interface to automatically register the dependencies for you:
Warning: This class has not been compiled.
public class DatabaseMigratorDependencyResolver implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
String[] beanNames = beanFactory.getBeanDefinitionNames();
for (String beanName : beanNames) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
// Your job is here:
// Feel free to make use of the methods available from the BeanDefinition class (http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/beans/factory/config/BeanDefinition.html)
boolean isDependentOnDatabaseMigrator = ...;
if (isDependentOnDatabaseMigrator) {
beanFactory.registerDependentBean("databaseMigrator", beanName);
}
}
}
}
You could then include a bean of this class alongside all your other beans.
<bean class="DatabaseMigratorDependencyResolver"/>
Spring will automatically run it before it starts initiating the rest of the beans.
I do this on application start-up. The schema version that the application requires is compiled into the application as part of the build process. It is also stored in the database and updated by the database migration scripts.
On application start-up, the app checks that the schema version in the database is what it expects and if not, aborts immediately with a clear error message.
In a normal Java program, this happens right at the start of the main method.
In a webapp, it's performed by the app's ServletContextListener and is the first thing it does when the servlet context is created.
That's saved my (apps') bacon several times.
I ended up creating a simple ForwardingDataSource class which appears in the context files as:
<bean id="dataSource" class="xxx.ForwardingDataSource" depends-on="databaseMigrator">
<property name="delegate">
<!-- real data source here -->
</property>
</bean>
If find it less elegant than Adam Paynter's solution, but clearer.