Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

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.

...

Consider two beans.  One represents rational numbers

The Rational bean
Code Block
languagejava
titleThe 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
Code Block
languagejavatitleThe Containing Bean
public class Parent extends AbstractIdentifiableInitializableComponent {
    private Rational rational;
    
    public void setRational(Rational what) {
        rational = what;
    }
}

...

Code Block
languagexml
<plugin:Parent numerator="22" denominator="7" name="pi"/>

You might intially initially write parsing code like this

Broken parsing
Code Block
title
languagejavaBroken parsing
	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);

...

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
Code Block
title
languagejavaThe Containing Bean
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);
    }
}

...

In our example, the FactoryBean  might look like this:

A Factory Bean
Code Block
languagejavatitleA Factory Bean
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;
	}
}