This blog takes the idea of the ConverterFactory together with a contrived wine service scenario and adds to Spring's documentation to demonstrate how it can be used. In this contrived scenario, I have a WineService that has two setters, each requiring an Enum argument:
public class WineService {
private WineCountry wineCountry;
private WineColour wineColour;
@Required
public final void setWineCountry(WineCountry wineCountry) {
this.wineCountry = wineCountry;
}
@Required
public final void setWineColour(WineColour wineColour) {
this.wineColour = wineColour;
}
/** Some arbitrary business method that checks wine availability */
public boolean isWineAvailable(WineCountry wineCountry, WineColour wineColour) {
if (this.wineCountry == wineCountry && this.wineColour == wineColour) {
return true;
}
return false;
}
}
In order to convert these Enum arguments from strings specified in the XML config, we’ll need a couple of Converters. In this case, we can centralise the conversion logic from String to Enum as the conversion requirements for each Enum are similar and for this we can use a ConverterFactory.
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum<?>> {
public <T extends Enum<?>> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToEnumConverter<T>(targetType);
}
@SuppressWarnings("rawtypes")
private final class StringToEnumConverter<T extends Enum> implements Converter<String, T> {
private final Class<T> enumType;
public StringToEnumConverter(Class<T> enumType) {
this.enumType = enumType;
}
@SuppressWarnings("unchecked")
public T convert(String source) {
checkArg(source);
return (T) Enum.valueOf(enumType, source.trim());
}
private void checkArg(String source) {
// In the spec, null input is not allowed
if (source == null) {
throw new IllegalArgumentException("null source is in allowed");
}
}
}
}
This code has been shamelessly plagiarised from the Spring documentation although I have added a couple of changes to get rid of the warnings in Eclipse together with a quick null check.
The XML config is also very similar to yesterday’s in that we’re specifying a business object and a ConversionServiceFactoryBean as a conversion service. It only differs in that the converters property of the ConversionServiceFactoryBean takes our ConverterFactory implementation: StringToEnumConverterFactory. That’s because the converters property can be any implementations of the Converter, ConverterFactory, GenericConverter, and ConditionalGenericConverter interfaces.
<?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-3.0.xsd"> <bean id="wineService" class="miscillaneous.conversionservice.converterfactory.WineService"> <property name="wineColour"> <value>RED</value> </property> <property name="wineCountry"> <value>FRANCE</value> </property> </bean> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <!-- Converters can be a list of converter or a ConverterFactory --> <bean class="miscillaneous.conversionservice.converterfactory.StringToEnumConverterFactory"/> </property> </bean> </beans>
The code to test this is:
public class ConverterFactoryMain {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"ConverterFactoryService.xml");
WineService wineService = ctx.getBean(WineService.class);
checkWine(wineService, WineCountry.FRANCE, WineColour.RED);
checkWine(wineService, WineCountry.GERMANY, WineColour.WHITE);
}
private static void checkWine(WineService wineService, WineCountry wineCountry,
WineColour wineColour) {
System.out.println(wineCountry.toString()
+ " "
+ wineColour.toString()
+ " is "
+ (wineService.isWineAvailable(wineCountry, wineColour) == true ? "available"
: "unavailable"));
}
}
No comments:
Post a comment