Aubergine
Aubergine

Reputation: 6042

Spring xml to java config: how to convert nested xml bean definition

 <bean
    name="ticketValidationFilter"
    class="org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter"
    p:service="https://my.local.service.com/cas-client">
    <property name="ticketValidator">
    <bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
    <constructor-arg index="0" value="https://localhost:8443/cas" />
    </bean>
    </property>
  </bean>

Can anyone please help to convert this xml definition to java config? Specifically, I am confused about how to declare ticketValidator.

    @Bean
    FilterRegistrationBean ticketValidationFilter(){

        Filter ticketValidationFilter = new
                org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter();

        FilterRegistrationBean frb = new FilterRegistrationBean();
        frb.addUrlPatterns("/*");
        frb.setFilter(ticketValidationFilter);

        // ticket Validator somewhere here

        Map<String, String> initParamMap = new HashMap<String, String>();
        initParamMap.put("service", "http://localhost:8889/cas-client");
        frb.setInitParameters(initParamMap);

        return frb;
    }

-----UPDATE FOR FILTERS CONFIG-------

if you use nested beans for filter configuration(Eg. like in my case), whatever properties you set on your filters - they will be null if you use just setters(don't know why, if anyone eager to explain please do).

To solve this issue: create filter(@Component) that extends GenericFilterBean and set your properties there. Then @Autowire it to your config and use setters on filter registration as usual.**

If you have simple configuration property(eg. String) then you can use Map with init params and set it on the FilterRegistration bean.

Upvotes: 1

Views: 1802

Answers (2)

sawprogramming
sawprogramming

Reputation: 867

Per the Javadoc comment for the org.springframework.context.ApplicationContextAware interface:

Implementing this interface makes sense for example when an object requires access to a set of collaborating beans.

@soulcheck's answer is appropriate for most use cases, but when you need to register the inner bean with Spring for any reason, like to define init and destroy methods, you need to:

  1. Implement org.springframework.context.ApplicationContextAware.
  2. Store the org.springframework.context.ApplicationContext for use inside your @Bean method.
  3. Use the org.springframework.context.ApplicationContext to get access to the org.springframework.beans.factory.support.BeanDefinitionRegistry to manually create and register bean definition.
  4. Use the org.springframework.context.ApplicationContext again to retrieve the bean created in the previous step.

Here's an example skeleton of a class that shows those steps in action:

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfiguration implements ApplicationContextAware {
    // members ///////////////////////////////////////////////////////////////////////////
    private ApplicationContext applicationContext;

    // getters and setters ///////////////////////////////////////////////////////////////
    @Override
    public void setApplicationContext(final ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    // methods ///////////////////////////////////////////////////////////////////////////
    @Bean
    public Foo foo() {
        ((BeanDefinitionRegistry)applicationContext.getAutowireCapableBeanFactory()).registerBeanDefinition(
            "bar",
            BeanDefinitionBuilder.genericBeanDefinition(Bar.class)
                .setInitMethodName("start")
                .setDestroyMethodName("stop")
                .addConstructorArgValue("Hello world!")
                .addPropertyValue("hello", "world")
            .getBeanDefinition()
        );

        final Foo foo = new Foo(applicationContext.getBean("bar", Bar.class));

        return foo;
    }
}

Upvotes: 0

soulcheck
soulcheck

Reputation: 36777

Since it's a bean that's used only in this particular external bean, you simply convert it to an instance creation and set the apropriate property.

You can't use Filter interface as that doesn't have the setter for ticket validator.

Use AbstractTicketValidationFilter instead:

@Bean
FilterRegistrationBean ticketValidationFilter(){

    AbstractTicketValidationFilter ticketValidationFilter = new
            org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter();

    FilterRegistrationBean frb = new FilterRegistrationBean();
    frb.addUrlPatterns("/*");
    frb.setFilter(ticketValidationFilter);

    //here be ticket validaror

    TicketValidator validator = new org.jasig.cas.client.validation.Cas20ServiceTicketValidator("https://localhost:8443/cas");

    ticketValidationFilter.setTicketValidator(validator);

    //end of ticket validator config

    Map<String, String> initParamMap = new HashMap<String, String>();
    initParamMap.put("service", "http://localhost:8889/cas-client");
    frb.setInitParameters(initParamMap);

    return frb;
}

The whole beauty of java config is that some complex xml configurations can be converted to very simple java.

Upvotes: 2

Related Questions