Showing posts with label config. Show all posts
Showing posts with label config. Show all posts

Wednesday, December 3, 2008

Spring, Filters and Configuration easier than you think.

So I needed to add a filter for some cool stuff in my application.

First off I needed to get my filter to play nicely with Spring, so I needed to do the following in my web.xml

<filter>
<filter-name>myCoolNewFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>myCoolNewFilterSpringBean</param-value>
</init-param>
</filter>

So then I go ahead and open up my applicationContext.xml file and wire up the bean like normal

<bean name="myCoolNewFilterSpringBean" class="com.mycompany.filter.MyCoolNewFilter" />

Notice that the bean name in the app context matches the param-value in the filter config: myCoolNewFilterSpringBean

Then instead of trying to do URL patterns for the config of the filter I just tried the following:

<filter-mapping>
<filter-name>myCoolNewFilterSpringBean</filter-name>
<servlet-name>dispatcher</servlet-name>
</filter-mapping>

Which maps this filter to all filter all calls being directed to my dispatcher servlet! No nasty or convoluted URL patterns... Just easily readable servlet names!

Friday, July 25, 2008

Externalization

So I am working on a project at work using Java 6, Spring ( DI and MVC ), ehcache, SOAP(Axis), ibatis, and some other cool stuff. But that is the core of it. Anyways. Early on some decisions were made to make so decisions based on tracking parameters passed in by the client of the web application. So lets say request parameter foo would have known values of a,s,d,f...N and so on. But these were always going to be known. Well, of course this decision by the TEAM has been forgotten and I needed a way to add flexibility to the application configuration so that when new "foo's" are added, the configuration could be updated to handle it.

My initial reaction was:

"Well, I could have the values/config stored in the database and then just cache them."



But that solution would require days because I would have to create the tables, get them ok'd by the db ops team, have them put in the different environments( test, integration, stage, and then finally production ). Then on top of that I would have to create some sort of tool to manage the values in the table so that they could be updated as needed. Then I would have to secure the the admin page for the config management. And so on and so forth.

Then it occurred to a co-worker and I that we could just externalize the Spring Bean definition and cache it for a set amount of time ( and of course relying on the last good configuration if the new one fails ). The external config for the Spring Bean could then live anywhere ( local file system, or over http on a server somewhere ).

This of course was by far the most flexible and convenient way of dealing with the situation. So here is how it works.

Step 1: Create Factory.


public class ServiceFactoryFactoryContextLoader
implements ApplicationContextAware, IServiceFactoryFactoryContextLoader
{
private ApplicationContext parentContext;
private IServiceFactory serviceFactory;
private String location;

public IServiceFactory getServiceFactory() {
String [] configs = new String[]{location};
ApplicationContext applicationCtx = new ClassPathXmlApplicationContext(configs,true, parentContext );
try {
serviceFactory = (IServiceFactory)applicationCtx.getBean("serviceFactory");
} catch (Exception e) {
Logger.getLogger(this.getClass()).error("Uh... Houston we have a problem... Remote Config is not good", e);
}
return serviceFactory;
}

@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException
{
this.parentContext = applicationContext;
}

public void setLocation(String location) {
this.location = location;
}
}


Step 2: Enter the JNDI config for location of your externalized resource in the applicationContext.xml:

<jee:jndi-lookup id="serviceFactoryConfig" startup="true"
name="conf/serviceFactoryConfig"
ref="true"
cache="true"></jee:jndi-lookup>



Step 3: Wire factory up in applicationContext.xml

Set up the ServiceFactoryFactoryContextLoader with the cache proxy of course so that we can cache the retrieval and creation of the object. The property 'location' gets injected with the JNDI config value for the location of the externalized resource. And the cache:proxy allows me t cache any method beginning the 'get'. I am using ehcache for my caching but you can use others if you want/need to.

<bean id="serviceFactoryTarget"
class="com.foo.bar.factory.ServiceFactoryFactoryContextLoader">
<property name="location" ref="serviceFactoryConfig" />
</bean>
<cache:proxy id="serviceFactory" refId="serviceFactoryTarget">
<cache:caching methodName="get*" cacheName="serviceFactoryTarget" />
</cache:proxy>



Step 4: Pull the Bean definition out of the applicationContext.xml and into its own config with the Spring beans tag and xmlns declarations.


<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean id="serviceFactory" class="com.foo.bar.service.ServiceFactory">
<property name="lookups">
<map>
<entry key="default">
<bean class="com.foo.bar.service.ServiceLookupKey">
<property name="var1" value="foo" />
<property name="var2" value="bar" />
<property name="resourceExt" value="default" />
</bean>
</entry>
<entry key="bar">
<bean class="com.foo.bar.service.ServiceLookupKey">
<property name="var1" value="baz" />
<property name="var2" value="xyz" />
<property name="resourceExt" value="bar" />
</bean>
</entry>
</map>
</property>
</bean>
</beans>



And of course you need to define the JNDI values however you want.

So now that the application is configured we can launch the application and once the ServiceFactoryFactoryContextLoader.getServiceFactory() is called the app will make a call out to the externalized location to get the config, load it with the parent context ( which in this case is the applicationContext ) and then you can make the call to get the bean in the externalized config from Step 4 ( "serviceFactory" ). And there it is... the externalized config is now set so that I can host the config where ever I want it; on the local file system somewhere or over http on another server.