/** * Copyright (C) 2010 Talend Inc. - www.talend.com */ package client; import java.math.BigDecimal; import java.net.URL; import java.util.Collections; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import org.apache.cxf.endpoint.Client; import org.apache.cxf.frontend.ClientProxy; import org.apache.cxf.interceptor.Interceptor; import org.apache.cxf.interceptor.transform.TransformInInterceptor; import org.apache.cxf.interceptor.transform.TransformOutInterceptor; /** * Demonstrates how the forward and backward compatibility between * the old and new SOAP and REST clients is achieved with the help * of the CXF STAX Transform Feature */ public class SOAPClient { private static final String PORT_PROPERTY = "http.port"; private static final int DEFAULT_PORT_VALUE = 8080; private static final String HTTP_PORT; static { Properties props = new Properties(); try { props.load(SOAPClient.class.getResourceAsStream("/client.properties")); } catch (Exception ex) { throw new RuntimeException("client.properties resource is not available"); } HTTP_PORT = props.getProperty(PORT_PROPERTY); } int port; public SOAPClient() { port = getPort(); } /** * Old SOAP client uses old SOAP service */ public void useOldSOAPService() throws Exception { URL wsdlURL = getClass().getResource("/CustomerService.wsdl"); com.example.customerservice.CustomerServiceService service = new com.example.customerservice.CustomerServiceService(wsdlURL); com.example.customerservice.CustomerService customerService = service.getCustomerServicePort(); System.out.println("Using old SOAP CustomerService with old client"); customer.v1.Customer customer = createOldCustomer("Barry Old SOAP"); customerService.updateCustomer(customer); customer = customerService.getCustomerByName("Barry Old SOAP"); printOldCustomerDetails(customer); } /** * New SOAP client uses new SOAP service. */ public void useNewSOAPService(boolean direct) throws Exception { URL wsdlURL = getClass().getResource("/CustomerServiceNew.wsdl"); org.customer.service.CustomerServiceService service = new org.customer.service.CustomerServiceService(wsdlURL); org.customer.service.CustomerService customerService = direct ? service.getCustomerServicePort() : service.getCustomerServiceNewPort(); System.out.println("Using new SOAP CustomerService with new client"); customer.v2.Customer customer = createNewCustomer("Barry New SOAP"); customerService.updateCustomer(customer); customer = customerService.getCustomerByName("Barry New SOAP"); printNewCustomerDetails(customer); } /** * Old SOAP client uses new SOAP service */ public void useNewSOAPServiceWithOldClient() throws Exception { URL wsdlURL = getClass().getResource("/CustomerServiceNew.wsdl"); com.example.customerservice.CustomerServiceService service = new com.example.customerservice.CustomerServiceService(wsdlURL); com.example.customerservice.CustomerService customerService = service.getCustomerServicePort(); // The outgoing new Customer data needs to be transformed for // the old service to understand it and the response from the old service // needs to be transformed for this new client to understand it. Client client = ClientProxy.getClient(customerService); addTransformInterceptors(client.getInInterceptors(), client.getOutInterceptors(), false); System.out.println("Using new SOAP CustomerService with old client"); customer.v1.Customer customer = createOldCustomer("Barry Old to New SOAP"); customerService.updateCustomer(customer); customer = customerService.getCustomerByName("Barry Old to New SOAP"); printOldCustomerDetails(customer); } public void useOldSOAPServiceWithNewClient() throws Exception { URL wsdlURL = getClass().getResource("/CustomerService.wsdl"); org.customer.service.CustomerServiceService service = new org.customer.service.CustomerServiceService(wsdlURL); org.customer.service.CustomerService customerService = service.getCustomerServicePort(); // The outgoing old Customer data needs to be transformed for // the new service to understand it and the response from the new service // needs to be transformed for this old client to understand it. Client client = ClientProxy.getClient(customerService); addTransformInterceptors(client.getInInterceptors(), client.getOutInterceptors(), true); System.out.println("Using old SOAP CustomerService with new client"); customer.v2.Customer customer = createNewCustomer("Barry New to Old SOAP"); customerService.updateCustomer(customer); customer = customerService.getCustomerByName("Barry New to Old SOAP"); printNewCustomerDetails(customer); } /** * Old SOAP client uses new SOAP service with the * redirection to the new endpoint and transformation * on the server side */ public void useNewSOAPServiceWithOldClientAndRedirection() throws Exception { URL wsdlURL = getClass().getResource("/CustomerService.wsdl"); com.example.customerservice.CustomerServiceService service = new com.example.customerservice.CustomerServiceService(wsdlURL); com.example.customerservice.CustomerService customerService = service.getCustomerServiceRedirectPort(); System.out.println("Using new SOAP CustomerService with old client and the redirection"); customer.v1.Customer customer = createOldCustomer("Barry Old to New SOAP With Redirection"); customerService.updateCustomer(customer); customer = customerService.getCustomerByName("Barry Old to New SOAP With Redirection"); printOldCustomerDetails(customer); } /** * Prepares transformation interceptors for a client. * * @param clientConfig the client configuration * @param newClient indicates if it is a new/updated client */ private void addTransformInterceptors(List<Interceptor<?>> inInterceptors, List<Interceptor<?>> outInterceptors, boolean newClient) { // The old service expects the Customer data be qualified with // the 'http://customer/v1' namespace. // The new service expects the Customer data be qualified with // the 'http://customer/v2' namespace. // If it is an old client talking to the new service then: // - the out transformation interceptor is configured for // 'http://customer/v1' qualified data be transformed into // 'http://customer/v2' qualified data. // - the in transformation interceptor is configured for // 'http://customer/v2' qualified response data be transformed into // 'http://customer/v1' qualified data. // If it is a new client talking to the old service then: // - the out transformation interceptor is configured for // 'http://customer/v2' qualified data be transformed into // 'http://customer/v1' qualified data. // - the in transformation interceptor is configured for // 'http://customer/v1' qualified response data be transformed into // 'http://customer/v2' qualified data. // - new Customer type also introduces a briefDescription property // which needs to be dropped for the old service validation to succeed // this configuration can be provided externally Map<String, String> newToOldTransformMap = new HashMap<String, String>(); newToOldTransformMap.put("{http://customer/v2}*", "{http://customer/v1}*"); Map<String, String> oldToNewTransformMap = Collections.singletonMap("{http://customer/v1}*", "{http://customer/v2}*"); TransformOutInterceptor outTransform = new TransformOutInterceptor(); outTransform.setOutTransformElements(newClient ? newToOldTransformMap : oldToNewTransformMap); if (newClient) { newToOldTransformMap.put("{http://customer/v2}briefDescription", ""); //outTransform.setOutDropElements( // Collections.singletonList("{http://customer/v2}briefDescription")); } TransformInInterceptor inTransform = new TransformInInterceptor(); inTransform.setInTransformElements(newClient ? oldToNewTransformMap : newToOldTransformMap); inInterceptors.add(inTransform); outInterceptors.add(outTransform); } /** * namespace: {http://customer}v1 */ private customer.v1.Customer createOldCustomer(String name) { customer.v1.Customer cust = new customer.v1.Customer(); cust.setName(name); cust.getAddress().add("Pine Street 200"); Date bDate = new GregorianCalendar(2009, 01, 01).getTime(); cust.setBirthDate(bDate); cust.setNumOrders(1); cust.setRevenue(10000); cust.setShares(new BigDecimal(1.5)); cust.setType(customer.v1.CustomerType.BUSINESS); return cust; } /** * namespace: {http://customer}v2 * new simple element: briefDescription */ private customer.v2.Customer createNewCustomer(String name) { customer.v2.Customer cust = new customer.v2.Customer(); cust.setName(name); cust.getAddress().add("Pine Street 200"); Date bDate = new GregorianCalendar(2009, 01, 01).getTime(); cust.setBirthDate(bDate); cust.setNumOrders(1); cust.setRevenue(10000); cust.setShares(new BigDecimal(1.5)); cust.setType(customer.v2.CustomerType.BUSINESS); cust.setBriefDescription("Business Customer"); return cust; } private void printOldCustomerDetails(customer.v1.Customer customer) { System.out.print("Name : " + customer.getName()); System.out.print(", orders : " + customer.getNumOrders()); System.out.print(", shares : " + customer.getShares()); System.out.println(); } private void printNewCustomerDetails(customer.v2.Customer customer) { System.out.print("Name : " + customer.getName()); System.out.print(", orders : " + customer.getNumOrders()); System.out.print(", shares : " + customer.getShares()); System.out.println(); } private static int getPort() { try { return Integer.valueOf(HTTP_PORT); } catch (NumberFormatException ex) { // ignore } return DEFAULT_PORT_VALUE; } public static void main(String args[]) throws Exception { SOAPClient client = new SOAPClient(); // Scenario 1: // - Old clients have become aware that the new endpoints have been deployed. // They continue talking to the old endpoints which have not been stopped yet // otherwise they are configured such that they can talk to new endpoints // - New clients talk to the new endpoints but in some cases where new endpoints // have not been introduced yet, they are configured such that they can talk to // the old endpoints System.out.println("Scenario 1: Old and new clients are configured" + " to invoke on the new and old endpoints respectively"); // JAX-WS: Scenario 1 System.out.println(); System.out.println("JAX-WS:"); System.out.println(); // Old Client uses Old Service client.useOldSOAPService(); System.out.println(); // New Client uses Old Service client.useOldSOAPServiceWithNewClient(); System.out.println(); // New Client uses New Service client.useNewSOAPService(true); System.out.println(); // Old Client uses New Service client.useNewSOAPServiceWithOldClient(); System.out.println(); // Scenario 2: // Old clients are unaware of the fact that the old endpoint has been removed // and the new endpoint has been introduced. // Old Client thinks Old Service is still being used but // on the server the request will be redirected to the New Service endpoint // Similarly, the new clients can be redirected to the old endpoints on the server // if no new endpoints have been introduced yet in a given destination System.out.println("Scenario 2: Old clients invoke on the new endpoints" + " without being aware of it"); // JAX-WS: Scenario 2 System.out.println(); System.out.println("JAX-WS:"); System.out.println(); // Old Client uses New Service without being aware of it client.useNewSOAPServiceWithOldClientAndRedirection(); System.out.println(); // New Client uses New Service, the same new endpoint handles // new and redirected old client requests client.useNewSOAPService(true); System.out.println(); System.exit(0); } }