The Shibboleth IdP V4 software will leave support on September 1, 2024.

DoINeedAFactoryBean

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. 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

The Rational bean
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.

The Containing Bean
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 initially write parsing code like this

Broken parsing

This will not work if the deployer wants to use property replacement

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

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

The Containing Bean

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:

A Factory Bean