The Shibboleth IdP V3 software has reached its End of Life and is no longer supported. This documentation is available for historical purposes only. See the IDP4 wiki space for current documentation on the supported version.
DoINeedAFactoryBean
In V2 the preferred paradigm was to write a Factory Bean for every object for which you had a parser. Â In V3, although it does not case any problems to have a FactoryBean, it is not usually required.
It is important to understand when you must implement a Factory Bean.
A Factory Bean is required if the plugin you have implemented takes a parameter (via a bean setter or a constructor) which is not a String or an Object which Spring can convert into from a String. Â The IdP String infrastructure has a reasonably wide set of functions to convert from Strings to other types:
- Booleans
- Integers
- Durations (
@Duration long)
- Predicates
If your code requires more than this then you need a Factory Bean. Â To put another way, if the object passed to builder.addConstructorArgValue()
or builder.addParameterValue()
 is not a String, then you should have used a Factory Bean.
Why?
The problem revolves around property replacement. Â In V3, we use Spring for property replacement and this happens between the parser being called and the bean being created. Â If the parameter which the parsers has gathered has been converted in the parser, then the property replacement cannot happen.
This is best described by a simple example
A Worked Example
Consider two beans. Â One represents rational numbers
public class Rational { private int denominator; private int numerator; public void setDenominator(int what) { denominator = what; } public void setNumerator(int what) { numerator = what; } public float approxValue() { return numerator/denominator; } }
and the other contains an instance of it.
public class Parent extends AbstractIdentifiableInitializableComponent { private Rational rational; public void setRational(Rational what) { rational = what; } }
And assume you want to program configure from an XML segment like this
<plugin:Parent numerator="22" denominator="7" name="pi"/>
You might intially write parsing code like this
final String id = StringSupport.trimOrNull(child.getAttributeNS(null, "name")); builder.addPropertyValue("id", id); final String num = StringSupport.trimOrNull(child.getAttributeNS(null, "numerator")); final String denom = StringSupport.trimOrNull(child.getAttributeNS(null, "denominator")); final Rational rational = new Rational(); rational.setDenominator(Integer.decode(denom)); rational.setNumerator(Integer.decode(num)); builder.addPropertyValue("rational", rational);
This will not work if the deployer wants to use property replacement
<plugin:Parent numerator="%{idp.numerator}" denominator="7" name="pi"/>
In this case the parsing will fail because the property replacement has not happened at the time that the parsing occurs and %{idp.numerator}
is not a valid integer. Â In order that property replacement should work, you need to defer the conversion from the parsing to the bean creation.
The Solutions
There are several techniques available, which you chose depends on your precise circumstance
Use a Secondary BeanDefinitionBuilder
If the object being built inside the parser is a simple bean which can configured with Strings and auto converted types, then it can be created by a secondary builder. Â In the example above
final BeanDefinitionBuilder rationalDefn = BeanDefinitionBuilder.genericBeanDefinition(Rational.class); rationalDefn.addPropertyValue("denominator", denom); rationalDefn.addPropertyValue("numerator", num); builder.addPropertyValue("rational", rationalDefn.getBeanDefinition());
A special case of this is the org.springframework.beans.factory.support.ManagedList
. Â If a ManagedList is passed to addPropertyValue
or addConstructorValue,
then Spring treats this as a special list and applies any conversions to the contents prior to bean creation. Â So, for instance if the element of a ManagedList is "%{idp.property}" then this is replaced prior to any bean it is injected into being instantiated, whereas if this was an ArrayList it would not be replaced.
Push the Conversion to the Bean
The idea here is to hold the parameters inside the bean and then create the subsidiary bean in the doInitialize()
method. Â Again using the same example the Parent bean would become
public class Parent extends AbstractIdentifiableInitializableComponent { private Rational rational; private int denominator; private int numerator; public void setDenominator(int what) { denominator = what; } public void setNumerator(int what) { numerator = what; } Â @Override public void doInitialize() throws ComponentInitializationException { super.doInitialize(); final Rational rational = new Rational(); rational.setNumerator(numerator); rational.setDenominator(denominator); setRational(rational); } }
Add a Builder
The above two solutions are often not possible and in that case you have to build a FactoryBean. Â You then set the FactoryBean as the class in the parser.
In our example, the FactoryBean  might look like this:
public class ParentFactoryBean extends AbstractComponentAwareFactoryBean<Parent> { Â private int denominator; private int numerator; public void setDenominator(int what) { denominator = what; } public void setNumerator(int what) { numerator = what; } @Override public Class<?> getObjectType() { return Parent.class; } @Override protected Parent doCreateInstance() throws Exception { final Rational rational = new Rational(); rational.setNumerator(numerator); rational.setDenominator(denominator); Â Parent parent = new Parent(); parent.setRational(rational); return parent; } }