Buscar este blog

sábado, 2 de abril de 2016

Camel - CXF - Spring Boot - Web Service proxy Camel Route

This is a simple Spring Boot application, based on Apache Camel which act as a web service proxy. It is useful when you want to make accessible a web service allocated in a non accessible network. You can deploy it in an intermediate machine accessible from both sides.

This is a project based on this post in Java Code Geeks, but making it bootiful ;).

The project is very simple, you only need one Camel Route:
@Configuration
public class CamelConfig {

 @Bean
 public RouteBuilder routeBuilder() {
  return new RouteBuilder() {
   @Override
   public void configure() throws Exception {
    from("cxf:bean:servicePublisher?dataFormat=POJO")
    .log(LoggingLevel.INFO,"Doing some stuff")
    .to("cxf:bean:serviceConsumer?dataFormat=POJO");
   }
  };
 }
}

The route uses two CXF Endpoints, by publishing and by consuming the same web service contract.
This is the CXF config:
@Configuration
@ImportResource("classpath:/spring/conduit-config.xml")
public class CxfConfig {

 @Value("${service.publish.localEndpoint}")
 private String localEndPoint;

 @Value("${service.client.remoteEndpoint}")
 private String remoteEndPoint;


 @Bean
 public LoggingInInterceptor loggingInInterceptor() {
  final LoggingInInterceptor loggingInInterceptor = new LoggingInInterceptor();
  loggingInInterceptor.setPrettyLogging(true);
  return loggingInInterceptor;
 }


 @Bean
 public LoggingOutInterceptor loggingOutInterceptor() {
  final LoggingOutInterceptor loggingOutInterceptor = new LoggingOutInterceptor();
  loggingOutInterceptor.setPrettyLogging(true);
  return loggingOutInterceptor;
 }


 @Bean
 public GZIPInInterceptor gzipInInterceptor() {
  return new GZIPInInterceptor();
 }


 @Bean
 public CxfEndpoint servicePublisher(final CamelContext camelContext) {
   final CxfComponent cxfComponent = new CxfComponent(camelContext);

   final CxfEndpoint serviceEndpoint = new CxfEndpoint("", cxfComponent);
   serviceEndpoint.setServiceClass(ProductPortImpl.class);
   serviceEndpoint.setAddress(localEndPoint);
   serviceEndpoint.setWsdlURL("/wsdl/service/productService.wsdl");

   serviceEndpoint.setServiceNameString("{http://ws.javacodegeeks.com/product-service}ProductService");
   serviceEndpoint.setEndpointNameString("ProductPort");

   serviceEndpoint.setDataFormat(DataFormat.POJO);

   serviceEndpoint.setLoggingFeatureEnabled(true);
   serviceEndpoint.getInInterceptors().add(gzipInInterceptor());
   serviceEndpoint.getInInterceptors().add(loggingInInterceptor());
   serviceEndpoint.getOutInterceptors().add(loggingOutInterceptor());

   return serviceEndpoint;
 }


 @Bean
 public CxfEndpoint serviceConsumer(final CamelContext camelContext) {
   final CxfComponent cxfComponent = new CxfComponent(camelContext);

   final CxfEndpoint serviceEndpoint = new CxfEndpoint("", cxfComponent);
   serviceEndpoint.setServiceClass(Product.class);
   serviceEndpoint.setAddress(remoteEndPoint);
   serviceEndpoint.setWsdlURL("/wsdl/service/productService.wsdl");

   serviceEndpoint.setServiceNameString("{http://ws.javacodegeeks.com/product-service}ProductService");
   serviceEndpoint.setEndpointNameString("ProductPort");

   serviceEndpoint.setDataFormat(DataFormat.POJO);

   serviceEndpoint.setLoggingFeatureEnabled(true);
   serviceEndpoint.getInInterceptors().add(gzipInInterceptor());
   serviceEndpoint.getInInterceptors().add(loggingInInterceptor());
   serviceEndpoint.getOutInterceptors().add(loggingOutInterceptor());

   return serviceEndpoint;
 }
}

The end points configuration is very similar, but there is two differences:
  • The publisher service class references the service impl class. The consumer service class references the service interface class.
  • The publisher  service address references the full URL in which this project will publish the service. The consumer service address references the full URL in which the true service is published (by a third app)

You also need to keep in mind the SSL, so you need a http conduit for the client end point. I tried to configure it with java-based configuration, but I couldn't, so I finally  opted for XML-based configuration.
This is the conduit-config.xml file:
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xmlns:cxf="http://camel.apache.org/schema/cxf"
 xmlns:sec="http://cxf.apache.org/configuration/security" 
 xmlns:http="http://cxf.apache.org/transports/http/configuration"
 xmlns:util="http://www.springframework.org/schema/util"
 xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
              http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/camel-cxf.xsd
              http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
              http://cxf.apache.org/configuration/security http://cxf.apache.org/schemas/configuration/security.xsd
              http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd">

 
 <http:conduit name="{http://ws.javacodegeeks.com/product-service}ProductPort.http-conduit">
     <http:client Accept="text/xml" AcceptEncoding="gzip,deflate,sdch"  AllowChunking="true" AutoRedirect="true" CacheControl="No-Cache" ContentType="text/xml"/>
     
  <http:tlsClientParameters disableCNCheck="true">
   <sec:trustManagers>
    <sec:keyStore file="${service.client.truststore.path}" type="jks" password="${service.client.truststore.pass}" />
   </sec:trustManagers>
  </http:tlsClientParameters>
 </http:conduit>
  
</beans>

Some configuration values are environment-dependant, so I used a external properties file. This file also references a truststore used by the conduit config.
This is the PropertiesConfig file:
@Configuration
@PropertySources({
 @PropertySource("classpath:conf.properties"),
 @PropertySource("file:${propertyFile}")
})
public class PropertiesConfig {
 @Bean
 public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
  return new PropertySourcesPlaceholderConfigurer();
 }
}

When you launch the project, you need to pass it the propertyFile key, for example as a command line argument.

The full source code is in my githubhttps://github.com/evazquezma/JEE6/tree/master/proxy-service

No hay comentarios:

Publicar un comentario