Create a Guava TypeToken in Spring xml config? - spring

I'd like to be able to inject Guava TypeToken objects by specifying them as a bean in a Spring xml configuration. Is there a good way to do this? Has anyone written any cade/library to make this easier?
TypeToken seems to work by using reflection to introspect its generic types and is thus constructed using an anonymous class like:
new TypeToken<List<String>>() {}
Spring's xml config syntax doesn't seem to accept generics at all, presumably because it's built at runtime and doesn't "need" them (since generics are compile time checks and technically erased at runtime).
So the only way I know to instantiate a TypeToken bean is to do it in java:
TokenConfig.java:
#Configuration
public class TokenConfig {
#Bean
public TypeToken<List<String>> listOfStringsToken() {
return new TypeToken<List<String>>() {};
}
}
system-test-config.xml:
<beans>
<context:annotation-config/>
<bean class="com.acme.TokenConfig"/>
<bean class="com.acme.Consumer">
<property name="typeToken" ref="listOfStringsToken"/>
</bean>
</beans>
Is there a way to do this with just an xml config?

Maybe you can use spring FactoryBeans: look for factory methods at http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html

To answer my own question:
It IS possible to create a non-generic TypeToken using the static constructor TypeToken.of(Class), but this wont work for deeper generic types.
Here's the Spring xml config:
<bean class="com.google.common.reflect.TypeToken" factory-method="of">
<constructor-arg type="java.lang.Class" value="java.lang.Integer" />
</bean>
Which is equivelent to:
TypeToken.of(Integer.class)
and
new TypeToken<Integer>() {}
I also found a way to use the TypeToken.of(Type) constructor with a ParameterizedType constructed using Google Guice's Types utility. Guava has one too, but it's not public. :'(
I'm not sure if this is quite as robust as using TypeToken/TypeCapture, but it seems to work. Unfortunately it's pretty ugly and long... (maybe someone can simplify it?)
<bean class="com.google.common.reflect.TypeToken" factory-method="of">
<constructor-arg index="0">
<bean class="com.google.inject.util.Types" factory-method="newParameterizedType">
<constructor-arg index="0">
<value type="java.lang.Class">java.util.List</value>
</constructor-arg>
<constructor-arg index="1">
<array><value type="java.lang.Class">java.lang.String</value></array>
</constructor-arg>
</bean>
</constructor-arg>
</bean>
Which is equivelent to:
new TypeToken<List<String>() {}

Related

jee:remote-slsb use custom factory

i have this definition for calling an EJB
<util:properties id="ejbJndiConfig" location="file:/path/to/ejb-jndi-config.properties" />
<jee:remote-slsb id="myEjbService"
jndi-name="myEjbName"
business-interface="foo.bar.MyBusinessInterface"
cache-home="false"
lookup-home-on-startup="false"
refresh-home-on-connect-failure="true"
environment-ref="ejbJndiConfig"
expose-access-context="true">
</jee:remote-slsb>
All is working great and an instance of:
org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean is created to call the EJB method. But what about if i want to change this default behaviour and change the used class?
With old style spring we can do something like this:
<bean id="service" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="remoteInvocationFactory" ref="invocationFactory"/>
....
</bean>
<bean id="invocationFactory" class="src.rmi.CustomRemoteInvocationFactory"/>
is possible with new definition style?

Use Spring Bean in Non-Spring Bean container class

Suppose I have a couple different datasources defined as spring beans:
<bean id="dataSource1" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/db1?user=root&password=password" />
</bean>
<bean id="dataSource2" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/db2?user=root&password=password" />
</bean>
Now I'd like to use one of these datasource beans but from a non-spring bean container. I could call appContext.getBean("dataSource1") but I've read that's bad practice since it creates an explicit dependency on the spring framework in your code. Is there are better way to accomplish this in a way that is not implementation specific? Perhaps something like DatasourceFactory.getInstance("datasource1")?
Is this a weird request? I realize this would be very easy if the container class was a Spring bean since I could just use standard dependency injection in that case. But this is something I've been curious about. It seems that using dependency injection creates an endless loop, where if you want to use a spring bean in a class then that class must also be a bean, and then if another class wants to use that bean then it must also be a bean, and so on, and so on. I don't see an elegant way to break the dependency injection chain.
Again, maybe it's not necessary to break the chain, maybe the answer is that you do make all your classes spring beans, but I was just curious.
Can service locator pattern be applied here? If so can someone provide an example? Thanks.
The method you describe is the classic way to do it.
public class UnmanagedBean {
public UnmanagedBean(DataSource dataSource) {
... // do something
}
}
...
ApplicationContext context = ...;
DataSource dataSource2 = context.getBean("dataSource2");
UnmanagedBean bean = new UnmanagedBean(dataSource2);
You can add a level of abstraction with a BeanProvider class that does this for you, but you are limited to getting the beans directly from the ApplicationContext.

I still don't get how to avoid the use of getBean()

I'm new to Spring and I've read many guides on how to inject beans. Curiously, in every example I see, they use getBean in a class main method (not what I need). Also I've read many forums and questions related to how not to use getBean but I still can't figure out the best approach for my app.
I'm refactoring a web app that is highly coupled and without design patterns. Every business class has a corresponding DAO class, every DAO class extends a super DAO which handles the connection and other stuff. The problem here is that every DAO needs, in the constructor, some database config parameters that are being passed from the business class. What I'm trying to do is to put these parameters in a DBConfig bean and inject them into every DAO allowing me to create the DAO object from every business class simply, for example: dao = new myDAO().
How can I inject the DBConfig bean into every DAO "automatically"? Should I use getBean in the super DAO?
Your config could look like this:
<bean id="daoConfig1" class="com.foo.dao.DAOConfig">
<property name="dbUrl" value="jdbc://urlForDao1" />
...
</bean>
<bean id="dao1" class="com.foo.dao.DAO1">
<constructor-arg ref="daoConfig1" />
</bean>
<bean id="business1" class="com.foo.service.Business1">
<property name="dao" ref="dao1" />
</bean>
<bean id="daoConfig2" class="com.foo.dao.DAOConfig">
<property name="dbUrl" value="jdbc://urlForDao2" />
...
</bean>
<bean id="dao2" class="com.foo.dao.DAO2">
<constructor-arg ref="daoConfig2" />
</bean>
<bean id="business2" class="com.foo.service.Business2">
<property name="dao" ref="dao2" />
</bean>
Or share a single daoConfig instance between all daoX beans, if that's what you want.
You can then use the folowing to handle the business logic:
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"beans.xml"});
Business1 b1 = (Business1) context.getBean("business1");
b1.doStuff();
Or better still, use something like Spring MVC that can wire the business beans into your controllers without needing to call getBean().

How to refer to the returned type

I want to create a spring bean as below.
<bean id="qNameString" class="javax.xml.xpath.XPathConstants.STRING"/>
Here I want the reference to return type which is a QName but I understand the way I referred is wrong. Can someone please help on this.
That won't work, because class="javax.xml.xpath.XPathConstants.STRING" makes no sense, since what you're referring to isn't a class.
You can refer to static fields using <util:constant>, as documented here:
<property name="...">
<util:constant static-field="javax.xml.xpath.XPathConstants.STRING"/>
</property>
Spring can create a QName for you like this:
<bean id="qName" class="java.xml.namespace.QName">
<constructor index="0" value="localpart"/>
<constructor index="1" value="namespaceURI"/>
</bean>
Replace localpart and namespaceURI with the local name and namespace.
To reference a constant in a class, like javax.xml.xpath.XPathConstants.STRING
<bean id="qNameString" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
<property name="targetField" value="javax.xml.xpath.XPathConstants.STRING"/>
</bean>
A shorter version is available with the util schema:
<util:constant static-field="java.xml.xpath.XPathConstants.STRING"/>
Apart from being shorter, the id of the bean will be java.xml.xpath.XPathConstants.STRING rather than qNameString.
See FieldRetrievingFactoryBean and The util schema

How to do spring request parameter conversion

In a Spring 3 based web (portlet) application I have a controller with a method like this:
#RenderMapping
public ModelAndView handleRenderRequest(...,#RequestParam MyClass myObject)
{
...
}
Now I wonder: How do I tell spring how to convert the request parameter to MyClass. I found information about property editors and about the Converter interface and there seem to be some implications that Converter is the successor of the property editor, but nobody seems to like being explicit about it.
I implemented the converter interface for String to MyClass conversion. But how do I tell Spring about it? I am using annotation based configuration wherever possible, so I checked whether spring will detect the Converter from my classpath automatically, but it does not.
So thought that the part Configuring a ConversionService from the manual wants to tell me that I've got to add the following to my applicationContext.xml which I did:
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="some.package.MyConverter"/>
</list>
</property>
</bean>
Bit still:
org.springframework.beans.ConversionNotSupportedException: Failed to
convert value [...]
So what am I missing? And is there a way, to just configure a package and let spring scan this package for converters and register them automatically? And say that in one certain method I want to use a different converter than in all other methods. For example I want an integer that has a Luhn-Checksum to be checked and the checksum removed, how can I do that? Something like #RequestParam(converter=some.package.MyConverter.class) would be great.
EDIT
Ok, I just caught in the documentation:
Use the Formatter SPI when you're working in a client environment,
such as a web application, and need to parse and print localized field
values
So I guess that means I should use the Formatter SPI, yet a third possibility next to property editors and converters (I think I could really to with a comparison table or the like). I did implement the Parser interface as well and tried to register my converter using:
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="some.package.SortOrderEnumConverterSpring"/>
</set>
</property>
</bean>
As you can see I used "set" instead of "list" for specifying the converters. I set a debugging breakpoint in the FormattingConversionServiceFactoryBean.setConverters method which did not fire upon using list, but it did fire on using set.
Additionally I added
<mvc:annotation-driven conversion-service="conversionService"/>
And the namespace for the mvc-prefix to my applicationContext. But still I get the conversion not supported exception.
I also tried going back to the converter approach and changed in my applicationContext.xml file the parameter list for converters from list to set, but that did not change anything either.
EDIT2
As digitaljoel pointed out it is possible to set different converters per controller using an initBinder method. I applied this to my controller:
#Autowired
private ConversionService conversionService;
#InitBinder
public void initBinder(WebDataBinder binder)
{
binder.setConversionService(conversionService);
}
And in my applicationContext.xml:
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="some.package.with.MyConverter"/>
</set>
</property>
</bean>
And all suddenly the conversion works just fine :-). But I am not quite happy about having to apply this to each and every of my controllers. There must be a way to just set it in my applicationContext for everyone, is there not? Good to know that I can override default if I need to (after all I asked for that), but I still want to set defaults.
And what about the Formatter stuff. Shouldn't I be using that instead of Converter?
Spring Portlet MVC 3.0 does not support
<mvc:annotation-driven conversion-service="conversionService"/>
Visit https://jira.springsource.org/browse/SPR-6817 for more info about this.
However you can add this to your common applicationContext
<bean
class="org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="webBindingInitializer">
<bean
class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="conversionService">
<list>
<ref bean="conversionService" />
</list>
</property>
</bean>
</property>
</bean>
This way you do not need add #InitBinder to every single controller
and of course
<bean id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<list>
<!-- converter implementations here -->
</list>
</property>
</bean>
You are correct that Converter (and ConverterFactory) are the successors to property editors. Your problem may be that you are not accepting the appropriate type as a parameter to your converter, but that's hard to say without seeing the converter code. If you are expecting Long or Integer you may actually be getting a String from Spring and need to perform that key conversion yourself first.
As for configuration, I believe you need to list all of your converters in the bean configuration in your xml. If you annotate your converter implementation with #Component you might be able to reference it by the bean name instead of the fully qualified path, but I have only tried that for a ConverterFactory, not a Converter.
Finally, on specific converters, it looks like you may be able to configure the conversion service at the controller level (see Javi's answer on Setting up a mixed configuration for annotation-based Spring MVC controllers ) and then you could just place that method (and others that require that controller) into a controller that uses a secondary conversion service which you ought to be able to inject by name with the #Resource annotation.
Implement a WebArgumentResolver:
public class MyArgumentResolver implements WebArgumentResolver
{
#Override
public Object resolveArgument(MethodParameter methodParameter,
NativeWebRequest webRequest) throws Exception
{
Class<?> paramType = methodParameter.getParameterType();
if (paramType == MyClass.class)
{
String parameterName = methodParameter.getParameterName();
String stringParameter = webRequest.getParameter(parameterName);
return convert(stringParameter);
}
return UNRESOLVED;
}
}
And register it in your applicationContext.xml:
<bean class="org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="customArgumentResolver">
<bean class="com.dshs.eakte.util.MyArgumentResolver" />
</property>
</bean>
This works and even has the advantage of allowing parameter conversion that is based on multiple method parameters.
To achieve something similar to what you're doing, I found this blog entry useful.
i think you need to use something like
public ModelAndView handleRenderRequest(...,#ModelAttribute("myObject") MyClass myObject)

Resources