Search code examples
xmlspringpropertiescxfcxf-client

CXF / @ImportResource vs. PropertySourcesPlaceholderConfigurer


I have a problem to make a @ImportResource work in connection with a PropertySourcesPlaceholderConfigurer. The closest I could find is this question, but I fail to understand how I can make it work for me.

Here's my configuration class:

@Configuration
@ComponentScan(basePackages = "com.example")
@EnableWebMvc
@EnableScheduling
@EnableAsync
@ImportResource("classpath:/config/webservices.xml")
public class AppConfig {

  @Bean
  public static PropertySourcesPlaceholderConfigurer properties(Environment environment) throws IOException {
    String env = StringUtils.defaultIfBlank(environment.getProperty("env"), "local");
    PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
    Resource[] allResources = generateListOfPropertiesFiles(env);
    configurer.setLocations(allResources);
    configurer.setIgnoreResourceNotFound(true);
    return configurer;
  }
:
}

Due to our configuration file setup I need this rather complicated approach.

The mentioned /config/webservices.xml looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
  <import resource="classpath:META-INF/cxf/cxf.xml" />
  <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

  <jaxws:client id="sessionControl"
    serviceClass="com.service.schemas.work1_2.SessionControl" address="${endpoint.sessionControl}">
    <jaxws:features>
      <bean class="org.apache.cxf.feature.LoggingFeature" />
    </jaxws:features>
  </jaxws:client>

</beans>

endpoint.sessionControl is defined in one of the .properties-files.

As far as I understand it the XML is parsed and evaluated before the PropertySourcesPlaceholderConfigurer exists and, therefore, no piece of code replaces the placeholder. But, how do I make the internals of Spring understand that it should parse the XML afterwards when all properties are read? Is it correct to use @ImportResource? What do I need to provide to Spring? Or is there a rather simple trick to make it work?

I'm rather a noob in this part of Spring with bean life cycles and bean factories, I'm always amazed how it's all working nicely (well, usually)...

--EDIT--
I've been trying to set the address in a @PostConstruct, but I can't make it work. It's always falling back to the "${...}":

  @Inject
  private SessionControl sessionControl;

  @Value("${endpoint.sessionControl}")
  private String endpointSessionControl;

  @PostConstruct
  public void postConstruct() {
    ((Client) sessionControl).getConduit().getTarget().getAddress().setValue(endpointSessionControl);
    ((Client) sessionControl).getEndpoint().getEndpointInfo().setAddress(endpointSessionControl);
  }

Solution

  • Ha, made it! :)

    What I had to do is follow the CXF-documentation for Spring. At the bottom is a section "Create a Client (More Manual Way)". In other words my /config/webservices.xml is now Java @Beans:

      @Value("${endpoint.sessionControl}")
      private String endpointSessionControl;
    
      @Bean
      public SessionControl sessionControl() {
        JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
        jaxWsProxyFactoryBean.setServiceClass(SessionControl.class);
        jaxWsProxyFactoryBean.setAddress(endpointSessionControl);
        jaxWsProxyFactoryBean.setFeatures(Arrays.asList(new LoggingFeature()));
        return (SessionControl) jaxWsProxyFactoryBean.create();
      }
    

    I hope it's okay to instantiate JaxWsProxyFactoryBean this way.