- CustomersOrderService - Construct, Read
- Steps:
- WSDL:
- Generate Stubs
- Implement the endpoint
- Configure the cf-servlet.xml (spring config file for apache cxf)
- Steps:
- Construct the Project
- Construct the WSDL
- Generate the Stubs
- Construct the endpoint
- Construct the config class
- Run the application
- Construct the Project
-
New > Spring Starter Project
- wsdlfirstws
- Description: WSDL First
- Finish
-
Dependency:
<dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-spring-boot-starter-jaxws</artifactId> <version>3.2.1</version> </dependency> -
application.propertiesserver.context-path=/wsdlfirstws cxf.path=/ # /services is default
- WSDL - xml document
- Root: wsdl definitions element - namespaces to be used
-
targetNamespace- our own namespace. Applicable to all the requests and responses in our application- Usually domain name
-
name="CustomerOrderService"- unique name for our service -
schema- all request and response types<wsdl:types> <xs:schema xmlns="http://www.w3.org/2001/XML_Schema" xmlns:tns="http://trainings.ws.bharath.com/" elementFormDefault="unqualified" targetNamespace="http://trainings.ws.bharath.com/" version="1.0"> <xs:complexType name="order"> <xs:sequence> <xs:element name="id" type="xs:integer" /> <xs:element maxOccurs="unbounded" name="product" type="tns:product" /> </xs:sequence> </xs:complexType> <xs:complexType name="product"> <xs:sequence> <xs:element minOccurs="0" name="id" type="xs:string" /> <xs:element minOccurs="0" name="description" type="xs:string" /> <xs:element minOccurs="0" name="quantity" type="xs:integer" /> </xs:sequence> </xs:complexType> <xs:complexType name="getOrdersRequest"> <xs:sequence> <xs:element minOccurs="0" name="customerId" type="xs:integer" /> </xs:sequence> </xs:complexType> <xs:complexType name="getOrdersResponse"> <xs:sequence> <xs:element minOccurs="0" maxOccurs="unbounded" name="order" type="tns:order" /> </xs:sequence> </xs:complexType> <xs:complexType name="createOrdersRequest"> <xs:sequence> <xs:element name="customerId" type="xs:integer" /> <xs:element name="order" type="tns:order" /> </xs:sequence> </xs:complexType> <xs:complexType name="createOrdersResponse"> <xs:sequence> <xs:element name="result" type="xs:boolean" /> </xs:sequence> </xs:complexType> <xs:element name="getOrdersRequest" type="tns:getOrdersRequest" /> <!-- element definition --> <xs:element name="getOrdersResponse" type="tns:getOrdersResponse" /> <xs:element name="createOrdersRequest" type="tns:createOrdersRequest" /> <xs:element name="createOrdersResponse" type="tns:createOrdersResponse" /> </wsdl:types> <wsdl:message name="getOrdersRequest"> <!-- Analogous to input params to Java methods and outputs --> <wsdl:port element="tns:getOrdersRequest" name="parameters"/> </wsdl:port> </wsdl:message> <wsdl:message name="getOrdersResponse"> <!-- Analogous to input params to Java methods and outputs --> <wsdl:port element="tns:getOrdersResponse" name="parameters"/> </wsdl:port> </wsdl:message> <wsdl:message name="createOrdersRequest"> <!-- Analogous to input params to Java methods and outputs --> <wsdl:port element="tns:createOrdersRequest" name="parameters"/> </wsdl:port> </wsdl:message> <wsdl:message name="createOrdersResponse"> <!-- Analogous to input params to Java methods and outputs --> <wsdl:port element="tns:createOrdersResponse" name="parameters"/> </wsdl:port> </wsdl:message> <wsdl:portType name="CustomerOrdersPortType"> <!-- operations are grouped here - abstract portion --> <wsdl:operation name="getOrders"> <!-- operation --> <wsdl:input message="tns:getOrdersRequest" name="getOrdersRequest"> </wsdl:input> <wsdl:output message="tns:getOrdersResponse" name="getOrdersResponse"> </wsdl:output> </wsdl:operation> <wsdl:operation name="createOrders"> <!-- operation --> <wsdl:input message="tns:createOrdersRequest" name="createOrdersRequest"> </wsdl:input> <wsdl:output message="tns:createOrdersResponse" name="createOrdersResponse"> </wsdl:output> </wsdl:operation> </wsdl:portType> <wsdl:binding name="CustomerOrdersServiceSoapBinding" type="tns:CustomerOrdersPortType"> <!-- links the abstract and physical portions --> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" /> <wsdl:operation name="getOrders"> <soap:operation soapAction="" style="document" /> <wsdl:input name="getOrdersRequest"> <soap:body use="literal" /> <!-- binding recommended - entire messages are validated by soap engine --> </wsdl:input> <wsdl:output name="getOrdersResponse"> <soap:body use="literal" /> </wsdl:output> </wsdl:operation> <wsdl:operation name="createOrders"> <soap:operation soapAction="" style="document" /> <wsdl:input name="createOrdersRequest"> <soap:body use="literal" /> <!-- binding recommended - entire messages are validated by soap engine --> </wsdl:input> <wsdl:output name="createOrdersResponse"> <soap:body use="literal" /> </wsdl:output> </wsdl:operation> <wsdl:service name="CustomerOrdersService"> <!-- Defines how to consume the web service --> <wsdl:port binding="tns:CustomerOrdersServiceSoapBinding" name="CustomerOrdersPort"> <soap:address location="http://localhost:8080/wsdlfirstws/services/customerOrdersService" /> <!-- dynamically changed by cxf with ip address of the server --> </wsdl:port> </wsdl:service> </wsdl:binding>
-
- https://www.udemy.com/course/java-web-services/learn/lecture/20280735#content
- Change the name to
CustomerOrders.wsdl - Paste it in
src/main/resources/wsdl - Eclipse has support for wsdl (not STS)
- Search for
cxf codegen plugin-cxf-codegen-plugin-
Apache CXF - link
<plugin> <groupId>org.apache.cxf</groupId> <artifactId>cxf-codegen-plugin</artifactId> <version>3.2.1</version> <executions> <execution> <id>generate-sources</id> <phase>generate-sources</phase> <configuration> <sourceRoot>${project.build.directory}/generated/cxf</sourceRoot> <!-- generated stubs are copied here - in target directory --> <wsdlOptions> <wsdlOption> <wsdl>${basedir}/src/main/resources/wsdl/CustomerOrders.wsdl</wsdl> <!-- basedir - src/main/resources/wsdl --> <wsdlLocation>classpath:CustomerOrders.wsdl</wsdlLocation> <!-- above or this can be used --> </wsdlOption> </wsdlOptions> </configuration> <goals> <goal>wsdl2java</goal> </goals> </execution> </executions> </plugin>- Paste in
pom.xmlat the end - Right click - maven > update project
- Stubs are generated
- Paste in
-
- Generates both JAXB and JAXWS classes
-
Request and Response classes
-
PortType class - wraps the operations
@WebService ... -
Service class -
-
Order - pojo (JAXB)
-
ObjectFactory - meta data used to construct the objects
-
- Endpoint creation:
- Right click > New > Class
- CustomerOrdersWsImpl
- Add: CustomerOrdersPortType (Interface)
- We don't have to mark the class with any annotations (annotations are present in the interface)
- Right click > New > Class
-
CustomerOrdersWsImpl
Map<BigInteger, List<Order>> customerOrders = new HashMap<>(); int currentId; public CustomerOrderWsImpl() { init(); } public void init() { List<Order> orders = new ArrayList<>(); Order order = new Order(); order.setId(BigInteger.valueOf(1)); // default for stubs Product product = new Product(); product.setId("1"); product.setDescription("IPhone"); product.setQuantity(BigInteger.valueOf(3)); order.getProduct().add(product); orders.add(order); customerOrders.put(BigInteger.valueOf((++currentId), orders); }
@Override
public GetOrdersResponse getOrders(GetOrdersRequest request) {
BigInteger customerId = request.getCustomerId();
List<Order> orders = customerOrders.get(customerId);
GetOrdersResponse orders = new GetOrdersResponse();
response.getOrder().addAll(orders); // response has empty list
return orders;
}
@Override
public CreateOrdersResponse createOrders(CreateOrdersRequest request) {
BigInteger customerId = request.getCustomerId();
Order order = request.getOrder();
List<Order> orders = customerOrders.get(customerId);
orders.add(order);
CreateOrderResponse response = new CreateOrderResponse();
response.setResult(true);
return response;
}
-
src/main/java/com.bharath.ws.soap.config.WebServiceConfig.java
@Bean public Endpoint endpoint() { Endpoint endpoint = new EndpointImpl(bus, new CustomerOrdersService()); ... }
-
Enable logging:
@Features(features = "org.apache.cxf.feature.LoggingFeature") public class CustomerOrdersWsImpl implements CustomerOrdersPortType { ... }
- Run as Spring Boot Application
- http://localhost:8080/wsdlfirstws
- Click on the wsdl link
- Copy the URL from browser
- New SOAP Project:
- Name: customerordersservice
- Wsdl: url
- Test Suit enabled
- getOrders - test case
- Customer ID: 1
- createOrders - test case
- Customer ID: 1
- Order ID: 2
- Product ID: 2
- Description: Mac BOOK RPO
- Delete orders method starting with WSDL file
- Change WSDL
- Modify xml schema if required
- Add Message
- Add Operation under port types
- Add Binding
- Change WSDL
- Order processing WSDL first WS
- WSDL File
- Generate Stubs (wsdl2java codegen plugin) (pom.xml)
- Implement the endpoint (interface generated is used)
- Publish the endpoint
- Steps
- Creation of a Project
- Add apache cxf
- Retrieve WSDL
- Save
- Copy to project
- Generate stubs
- Creation of client
- Run application
- Creation of a Project
-
New Spring starter project
- Name: javasoapclient
- Finish
-
pom.xml
<dependency> <groupId>org.apache.cxf</groupId> <artefactId>cxf-spring-boot-starter-jaxws</artefactId> <version>3.2.1</version> </dependency> -
Copy to src/main/resources/wsdl/CustomerOrders.wsdl
- Open customer orders service wsdl file
- save page as CustomerOrders.wsdl
- Copy to src/main/resources/wsdl/CustomerOrders.wsdl
- save page as CustomerOrders.wsdl
- pom.xml
- Copy codegen plugin
- cxf-codegen-plugin
- Copy codegen plugin
-
com.bharath.ws.soap.CustomerOrderWsClient.javapublic class CustomerOrderWsClient { public static void main(String[] args) throws MalformedURLException { CustomerOrderWsImplService service = new CustomerOrderWsImplService(new URL("http://localhost:8080/wsdlfirstws/customerdersservice?wsdl")); CustomerOrdersPortType customerOrdersWsImplPort = service.getCustomerOrderWsImplPort();// wraps all the operations GetOrdersRequest request = customerOrdersWsImplPort.getOrders(new GetOrdersRequest()); request.setCustomerId(BigInteger.valueOf(1); GetOrdersResponse response = customerOrdersWsImplPort.getOrders(request); List<Order> orders = response.getOrder(); System.out.println("Number of orders for the customer are: " + orders.get(0)); } }- Right click on the class and Run as Java Application
- CreateRequest:
- Pass it to create Orders method
- What is going on behind the scenes:
- Creation of customer order service:
-
Serviceclass constructordelegate = Provider.provider().createServiceDelegate(...);- Uses service provider mechanism
- Searches for
Providerclass in jar files (javax.xml.ws.spi.Provider)ProviderImpl
- Searches for
delegateis responsible for generating client dynamically at runtimedelegateis used to construct a port on the fly (uses proxy pattern may be)
- Uses service provider mechanism
-
- Creation of customer order service:
pom.xml- maven dependencies- Generate stubs - using maven codegen plugin of cxf
- Standalone client
- Service provider mechanism (Java 6) - used to discover who is the provider on the client side
- Which is apache cxf
- Generates ports, serializes and deserializes messages
- Which is apache cxf
- Context
- Bank - Amazon wants to process payments using our gateway
- User calls Amazom
- Web layer
- Business layer
- Data access
- DB
- For eBay, Flipkart, ...
- Instead of re-writing
- Business layer is marked as Java Web Service using Payment Gateway
- Payment Gateway is added to connect to Business layer
- eBay calls Business layer through Payment Gateway to make payments
- PG Web Service
- Instead of re-writing
- This approach is used when the legacy application already exists and we want to expose legacy service as a web-service for other applications
-
Steps:
- Construct teh Project
- Construct the Endpoint
- Mark with JAXB annotations
- Mark with JAX-WS annotations
- Construct the config class
- Run the application
-
Download the java first web services project from Resources
-
Import the project into STS
-
Open
pom.xmlcxf-spring-boot-starter-jaxws
-
Mark DTOs with JAXB - to serialize and deserialize
-
PaymentProcessorRequest@XmlType(name = "PaymentProcessorRequest") // name is optional @XmlAccessorType(XmlAccessType.FIELD) // fields are used for JAXB public class PaymentProcessorRequest { @XmlElement(name = "creditCardInfo", required = true) private CreditCardInfo creditCardInfo; @XmlElement(name = "Amount") private Double amount; } -
CreditCardInfo@XmlType public class CreditCardInfo { ... } -
PaymentProcessorResponse@XmlType public class PaymentProcessorResponse { ... }
-
PaymentProcessor- interface is enough (cxf will take care of it)@WebService(name = "PaymentProcessor") // JaxWS API, name is optional public interface PaymentProcessor { //... @WebMethod is optional for method, @WebResult can be used to mark response and @WebParam(name="") can be used for parameters - optional }
-
Copy WebServiceConfig.java
package com.bharath.ws.soap.config; @Configuration public class WebServiceConfig { @Autowired private Bus bus; @Bean public Endpoint endpoint() { Endpoint endpoint = new EndpointImpl(bus, new PaymentProcessorImpl()); endpoint.publish("/paymentProcessor"); return endpoint; } } -
application.propertiesserver.context-path=/javafirstws cxf.path=/
- Open
http://localhost:8080/javafirstws- cxf scans packages and based on annotations, it will build a wsdl file
-
Copy wsdl url and paste in SoapUI
- Single TestCase with one Request for each Operation
-
Example Request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://soap.ws.bharath.com/"> <soapenv:Header/> <soapenv:Body> <soap:processPayment> <!--Optional:--> <arg0> <!--Optional:--> <creditCardInfo> <!--Optional:--> <address>TEST</address> <!--Optional:--> <cardNumber>123</cardNumber> <!--Optional:--> <expirtyDate>2021-09-24+06:00</expirtyDate> <!--Optional:--> <firstName>A</firstName> <!--Optional:--> <lastName>M</lastName> <!--Optional:--> <secCode>123</secCode> </creditCardInfo> <!--Optional:--> <amount>1000</amount> </arg0> </soap:processPayment> </soapenv:Body> </soapenv:Envelope> -
Apache cxf de-serializes request into PaymentProcessorRequest and passes it on to PaymentProcessorImpl and then serializes response back from PaymentProcessorResponse
- Run tests in SOAP UI and check responses
- Endpoint and Beans
- Annotate
- Publish the Endpoints
- SoapUI Testing
- Security can be configured out of the box
- UT - User Token
- MTOM - to exchange files
- Encryption and Decryption
- Why WS.*?
- Two components need to communicate with each other seamlessly so we need WS standards
- When? Why? and How? to use the standards
- Why?
- Example: Online Shopping Application
- Credit card info is sent to Banks Payment Gateway Web Service
- Bank will ask us to authenticate
- SOAP message should contain it
- WS security is used:
- Username and Password is passed
- WS security is used:
- WS-Security:
- 3 important issues
- Authentication - Each of them know about each other
- User Name Token Profile - username and password
- X 508 Certificates
- SAML - Single Sign On
- If we sign on to one web service, we can use any other web service
- Confidentiality - make sure hacker does not read the messages
- Encryption and Decryption - public key cryptography
- Integrity - messages must not get changed or tampered
- XML Signature - included in the message
- Consumer calculates signature and compares
- XML Signature - included in the message
- Authentication - Each of them know about each other
- MTOM - For exchange files
- Any kind of files - word, text, images
- X-Rays
- Any kind of files - word, text, images
- WS-Addressing - Asynchronous Callbacks
- If WS is taking a lot of time, consumers can give a URL to call back once the processing is done
- Headers can be used
- Redirect WS response to another server instead of consumer
- If WS is taking a lot of time, consumers can give a URL to call back once the processing is done
- WS-Policy- Assert and mandate certain rules to consume our web services
- Say ensure all consumers to use https to call web service
- CXF ensures policies are followed
- WS-SecureConversation - Improve Performance while encrypting and decrypting by negotiating a private key at the beginning
- Instead of for each request and response
- WS-SecurityPolicy - Assert WS-Security requirements
- Combination of WS security and WS policy
- Assures certain WS security standards are followed by the Web Services consumer in an easier way
- For example, consumer must encrypt message before sending
- Assures certain WS security standards are followed by the Web Services consumer in an easier way
- Combination of WS security and WS policy
- 3 important issues
- Example: Online Shopping Application
- CXF implements these standards which can be used without doing much work at our end
- Refer to documentation
- Wrap Up:
- There are other standards as well
- WS-Security - application security
- MTOM - file exchange
- WS-Addressing - asynchronous callback, redirect to different server
- WS-Policy - Rules for consumer
- WS-SecurityPolicy
- WS-SecureConversation
- Steps:
- Add Security Dependency
- Configure the WSS4J Interceptors - WebServices Security for Java
- They handle security - need to configure
- Construct the Password Callback class - provides password so that WSS4J interceptors can intercept incoming message and validate against password
- Run and Test
-
Search for cxf jaxws security dependency
-
Maven repository:
cxf-rt-ws-security<!-- https://mvnrepository.com/artifact/org.apache.cxf/cxf-rt-ws-security --> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-ws-security</artifactId> <version>3.3.4</version> </dependency>
-
Open
WebServicesConfigpublic Endpoint endpoint() { EndpointImpl ...; new HashMap<String, Object> hashMap = new HashMap<>(); WSS4JInInterceptor wssIn = new WSS4JInInterceptor(hashMap); // in interceptor used for incoming requests, out interceptor is used for outgoing responses endpoint.getInInterceptors().add(wssIn); ... }
-
Properties
Map<String, Object> inProps = new HashMap<>(); inProps.put(ConfigurationConstants.ACTION, ConfigurationConstants.USERNAME_TOKEN); inProps.put(CongirutationConstants.PASSWORD_TYPE, WSConstants.PW_TEXT); inProps.put(ConfigurationConstants.PW_CALLBACK_CLASS, UTPasswordCallback.class.getName());-
Generate the class
com.bharath.ws.soap.config.UTPasswordCallback- Implement: CallbackHandler - javax.security.auth.callback
-
-
UTPasswordCallback
private Map<String, String> passwords = new HashMap<>(); public void UTPasswordCallback() { passwords.put("bharath", "bharath"); passwords.put("cxf", "cxf"); } @Override public void handle(...) { }- Passwords can be obtained from database or from LDAP
-
Handle gets callback array - callback has username as parameter
for(Callback callback : callbacks) { WSPasswordCallback passwordCallback = (WSPasswordCallback) callback; String password = passwords.get(passwordCallback.getIdentifier()); // returns username if (password != null) { passwordCallback.setPassword(password); return; } }
- Summary:
- Interceptor
- What it needs to do - action
- password type
- callback
- Callback handler
- returns password to WSS4J
- Interceptor
- Re-start application
- Run the request in SoapUI - security error
-
Download the text file:
<soapenv:Header> <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1"> <wsse:UsernameToken xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> <wsse:Username xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">Bharath</wsse:Username> <wsse:Password xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">Bharath</wsse:Password> </wsse:UsernameToken> </wsse:Security> </soapenv:Header>-
SoapUI:
Paste in Header section -
Example:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://soap.ws.bharath.com/"> <soapenv:Header> <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1"> <wsse:UsernameToken xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> <wsse:Username xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">bharath</wsse:Username> <wsse:Password xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">bharath</wsse:Password> </wsse:UsernameToken> </wsse:Security> </soapenv:Header> <soapenv:Body> <soap:processPayment> <!--Optional:--> <arg0> <!--Optional:--> <creditCardInfo> <!--Optional:--> <address>TEST</address> <!--Optional:--> <cardNumber>123</cardNumber> <!--Optional:--> <expirtyDate>2021-09-24+06:00</expirtyDate> <!--Optional:--> <firstName>A</firstName> <!--Optional:--> <lastName>M</lastName> <!--Optional:--> <secCode>123</secCode> </creditCardInfo> <!--Optional:--> <amount>1000</amount> </arg0> </soap:processPayment> </soapenv:Body> </soapenv:Envelope>
-
- WS-Standards
- Why?
- Open bodies worked on standards such as WS-Security
- Authentication
- Confidentiality
- Integrity
- Open bodies worked on standards such as WS-Security
- Why?
- MTOM
- WS-Addressing - Async communication, redirection
- WS-Policy - SSL, user name token profile, ...
- User Name Token Profile
- Callback
cxf-serlet.xml
- New Spring Starter Project - UTClient
- Finish
pom.xml- Copy cxf dependencies
- spring - 1.5.9
- Generate stubs
-
Copy plugin
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.cxf</groupId> <artifactId>cxf-codegen-plugin</artifactId> <version>3.2.1</version> <executions> <execution> <id>generate-sources</id> <phase>generate-sources</phase> <configuration> <sourceRoot>${project.build.directory}/generated/cxf</sourceRoot> <wsdlOptions> <wsdlOption> <wsdl>${basedir}/src/main/resources/wsdl/CustomerOrders.wsdl</wsdl> <wsdlLocation>classpath:wsdl/paymentProcessor.wsdl</wsdlLocation> </wsdlOption> </wsdlOptions> </configuration> <goals> <goal>wsdl2java</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
-
- Right click on
javafirstws- run it- Open wsdl in browser
http://localhost:8080/javafirstws - Command+S: paymentProcessor.wsdl (select All Files)
- Copy the file to
src/main/resources- New folder:wsdl - Change to
paymentProcessor.wsdleverywhere - Run as Spring Boot Application
- Open wsdl in browser
- Stub classes get generated
-
Right click on
src/main/java- Other - Class -com.bharath.trainings.ws.client.PaymentWSClientPaymentProcessor_Service service = new PaymentProcessor_Service(new URL("http://localhost:8080/javafirstwsut/services/paymentProcessor?wsdl")); PamentProcessor port = service.getPaymentProcessorPort(); PaymentProcessorResponse response = port.processPayment(new PaymentProcessorRequest()); System.out.println(response.isResult());
-
Run as Java Application
-
Error - Security error
-
CXF specific way:
-
PaymentWSClient
Client client = ClientProxy.getClient(port); Endpoint endpoint = client.getEndpoint(); Map<String, Object> props = new HashMap<>(); WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(props); endpoint.getOutInterceptors().add(wssOut); PaymentProcessorResponse ...
props = new HashMap<>();
props.put(WSHandlerConstants.ACTION, WSHandlerConstants.USENAME_TOKEN);
props.put(WSHandlerConstants.USER, "cxf");
props.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
-
New Class - UTPasswordCallback - Implements - CallbackHandler
handle(...) { for (int i = 0; i < callbacks.length; i++) { WSPasswordCallback wpc = (WSPasswordCallback) callbacks[i]; if (wpc.getIdentifier().equals("cxf")) { wpc.setPassword("cxf"); // can be read from a properties file return; } } } -
PaymentWSClientprops.put(WSHandlerConstants.PW_CALLBACK_CLASS, UTPasswordCallback.class.getName());
- Right click - Run as Java Application
- Server side shows full request
-
MTOM - Message Transmission and Optimization Mechanism - Standard from SOAP
-
Standard mechanism to compress messages (best way) - WS-Attachments is a legacy
-
cxf, weblogic, websphere have this
-
Datatype: DataHandler attachinfo; // incoming file is read and set into this data handler
- used to return response as well
binding.setMTOMEnabled(true);- by default it is not enabled
-
Setup:
FileWs | upload | download FileWsImpl
- File > New > Spring Starter Project > mtom
- Description: MTOM
- Copy the section: cxf dependency from pom.xml and paste
- Spring version: 1.5.9
- Right click on project > Maven Update Project
src/main/resources/application.properties-
Paste the following:
server.context-path=/mtom cxf.path=/
-
-
src/main/java- New Interface -FileWs@WebService public interface FileWs { void upload(@WebParam(name = "file") DataHandler attachment); // cxf grabs attachment and converts to DataHandler DataHandler download(); // cxf writes attachment back to client }
-
src/main/java/FileWsImplimplementsFileWspublic class FileWsImpl implements FileWs { @Override public void upload(DataHandler attachment) { InputStream inputStream = null; OutputStream outputStream = null; try { inputStream = attachment.getInputStream(); OutputStream outputStream = new FileOutputStream(new File("/Users/bharaththippireddy/Desktop/files/uploaded/test.jpg")); byte[] b = new byte[100000]; int bytesRead = 0; while ((bytesRead = inputStream.read(b) != -1) { outputStream.write(b, 0, bytesRead); } } catch (IOException e) { e.printStackTrace(); } finally { try { inputStream.close(); outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
-
FileWsImpl
@Override DataHandler download() { return new DataHandler(new FileDataSource(new File("/Users/bharaththippireddy/Desktop/files/uploaded/test.jpg"))); }
-
WebServicesConfig.java@Bean public Endpoint endpoint() { Endpoint endpoint = new EndpointImpl(bus, new FileWsImpl()); endpoint.publish("/fields"); SOAPBinding binding = (SOAPBinding) endpoint.getBinding(); // javax.xml.ws.soap binding.setMTOMEnabled(true); return endpoint; }
-
Run as Spring Boot App
localhost:8080/mtom- Click on wsdl link and copy the link
-
In SoapUI, New SOAP Project:
- Project Name: fileWs
- Initial WSDL: paste the link
-
Open upload request:
- Attachment has content id:
- Click on Attchments at button
- Click on + button
- Select
sky-div.jpg - Don't cache
- Click on Part: Select the content id
- Attachment has content id:
-
Hit play
-
To Download:
- Open download request
- Hit play
- Includes cid:
- Click on Attachments tab at the bottom:
- File is seen
- Click on the file to open to save it
- What? Why and When to use the Jax-WS handlers
- Lower level API to implement handlers
- Cross-cutting concerns: - across ws endpoints or across clients
- Handlers are used for this
- Classes we develop by implementing interfaces
- Handlers are used for this
- Called when WS-Client invokes a request to CXF (in between)
- Called when cxf sends request to WS-Endpoing (in between)
- And also when response flows through
- They are similar to servlet filters but can be applied on client and server side
- Handlers are used for:
- Custom authentication defition (own soap headers)
- We can manipulate here
- Caching - caches responses (same request or not)
- Maintaining different versions of WS-Endpoint
- We may have to decide which web service will serve the request (dispatched to specific endpoints)
- Custom authentication defition (own soap headers)
- We can do on eithe server or client side or both
- SOAP Handlers: A type of handler
- They have access to entire message (soap headers, body, ...)
- Logical Handlers: Another type of handler
- Just the payload information
- Implementation:
SOAPHandler<SOAPMessageContext>interface is implemented- Methods to override:
handleMessagehandleFaultgetHeadersclose
- Methods to override:
handleMessage,getHeaders: Called on the way in and way out (twice)handleFault: Called only when there is a faultclose: On the way out at the end of entire flow (cleanup code can be put here - closing resources)- Implementation:
LogicalHandler<LogicalMessageContext>:- Methods to override:
handleMessagehandleFaultclose- No
getHeaders
- Methods to override:
SiteName-SiteHandleris used to extract site name
- Design the handler chain (we can have multiple handlers) - servlets filter pattern
- Construct the handlers
- Configure the handlers
- Run and Test
-
wsdlFirstws - New Class > com.bharath.trainings.ws.handlers.SiteHandler implements SOAPHandler (javax.xml.ws.handler.soap.SOAPHandler)
public class SiteHandler implements SOAPHandler<SOAPMessageContext> { ... @Override public boolean handleMessage(SOAPMessageContext context) { return false; } ... }
- Steps:
-
Configure for request message only
@Override public boolean handleMessage(SOAPMessageContext context) { Boolean isResponse = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); if (!isResponse) { SOAPMessage soapMsg = context.getMessage(); try { SOAPEnvelope envelope = soapMsg.getSOAPPart().getEnvelope(); SOAPHeader header = envelope.getHeader(); Iterator childElements = header.getChildElements(); } catch (SOAPException e) { e.printStackTrace(); } } else { System.out.println("Response on the way"); } return false; }
-
System.out.println("HandleMessage");
...
while (childElements.hasNext()) {
Node eachNode = (Node) childElements.next(); // javax.xml.soap.Node
String name = eachNode.getLocalName();
if (name != null && name.equals("SiteName")) {
System.out.println("Site Name is =====> " + eachNode.getValue());
}
}
...
return true; // or else this handler will not be called
@Override
public boolean handleFault(SOAPMessageContext context) {
System.out.println("handleFault");
return false;
}
-
WebServiceConfig.xml@Bean public Endpoint endpoint() { ... SOAPBinding binding = (SOAPBinding) endpoint.getBinding(); ArrayList<Handler> handlerChain = new ArrayList<>(); handlerChain.add(new SiteHandler()); binding.setHandlerChain(handlerChain); return endpoint;
- Start application
- Access wsdl file
- Paste in SOAP-UI
- Project Name: SiteHandlerTest
-
Request:
- CustomerID: 1
- id: 1234
- description: iPhone
- quantity: 1000
-
Add headers
<soapenv:Header> <SiteName>Amazon</SiteName> </soapenv:Header>- Console prints the handler message
-
- First method called:
getHeaders- on the way in - Second:
handleMessage- on the way in - Third:
getHeaders- on the way out - Fourth:
handleMessage- on the way out - Third:
close- at the end before ending
- Assignment: implement it
mustUnderstandattribute-
It takes two values:
- 1 - shoud be processed by the web services provider
- Code on should handle soap header
- 0 - optional
- 1 - shoud be processed by the web services provider
-
Example:
<SiteName mustUnderstand="1">Amazon</SiteName>-
Server:
@Override public Set<QName> getHeaders() { System.out.println("getHeaders"); return null; // put SiteName in the Set and return it for cxf to know that we have handled it }- Should be implemented if
mustUnderstand="1"attribute is set
- Should be implemented if
-
-
-
Soap services should handle errors
<SOAP-ENV:Envelope> <SOAP-ENV:Body> <SOAP-ENV:Fault>- Four child elements:
<faulCode>: Indicates what has gone wrongSOAP-ENV:VersionMismatch- if soap envelope namespace is not what the server is expectingSOAP-ENV:MustUnderstand- Used whenmustUnderstandflag is set to 1 in soap header- If provider does not handle the child element of the header, then this error occurs
SOAP-ENV:Client- message was not formed well (wrong info or incorrect message)SOAP-ENV:Server- server fault
<faultString>: Message that explains the error<faultActor>: If message is going through multiple nodes, we want to know at which node the error occurred<detail>: More information about the error- If multiple errors, we can indicate here
- Four child elements:
- Run application: javafirstws
- Test using SOAP UI
-
Example:
public class PaymentProcessorImpl implements PaymentProcessor { public PaymentProcessorResponse processPayment(PaymentProcessorRequest paymentProcessorRequest) { ... if (paymentProcessorRequest.getCreditCardInfo().getCardNumber() == null || paymentProcessorRequest.getCreditCardInfo().getCardNumber().length() == 0) { throw new RuntimeException("Invalid Card Number"); } } }-
Remove the number and run
<faultcode>soap:Server</faultcode> <faultstring>Invalid Card Number</faultstring>- wsdl does not have any fault elements
- We are throwing runtime exception and not checked exception
- wsdl does not have any fault elements
-
-
Making soap fault part of contract
-
Example:
@WebService(name = "PaymentProcessor") public interface PaymentProcessor { public @WebResult(name = "response") PaymentProcessorResponse processPayment(@WebParam(name = "paymentProcessorRequest") PaymentProcessorRequest paymentProcessorRequest) throws Exception; }-
WSDL has fault element
-
Custom exception:
... throws ServiceException-
Class
public class ServiceException extends Exception { public ServiceException(String message) { super(message); } }-
PaymentProcessorImpl... throws ServiceException { ... throws new ServiceException("Invalid Card Number"); }-
Detail:
<detail> <...ServiceException> </detail>
-
-
-
-
- CRUD on products
- Product:
- id
- description
- ...
- Product:
- Spring Data JPA
- Hibernate
- Classes
- ProductRepository
- Product
- ProducWs
- ProductWsImpl
- Steps:
- Download MySQL and MySQL Workbench
- Install (choose Workbench)
- Configure (enter Root password)
- MySQL Workbench - client for MySQL
- Steps:
- Download installer
- Install MySQL & MySQL workbench (choose Workbench option)
- Configure MySQL (enter root password)
- Link: search for mysql install: https://corlewsolutions.com/
- Configuration:
- Open MySQL.prefPane
- Select Configuration tab
- Click Select against Configuration File option
-
Select
/private/etc/my.cnf -
In
/etc/my.cnfadd the following:[mysqld] skip-grant-tables -
Restart mysqld as follows:
ps aux | grep mysqlkill -9 <pid1> <pid2> ...(grab pids of all mysql related processes)
-
MySQLD gets restarted automatically
-
Run the following to connect:
/usr/local/mysql-8.0.16-macos10.14-x86_64/bin/mysql -uroot -ppassword
-
-
Launch MySQL workbench
- MySQL COnnections +
- Username: root
- Connection Name: Local
- Test Connection: Enter password
- Double click on icon
- New > Query Tab
-
SQL
create database mydb- Click on lightning icon
-
SQL:
use mydb; create table product (id int, name varchar(20), description varchar(20), price int); select * from product;
- Go to STS
- New starter project:
- name: productcrud
- group: com.bharath.soap
- Description: Product CRUD Operations
- Dependencies:
-
JPA (Hibernate is JPA provider my default)
-
MySQL
-
Search of cxf spring boot jaxws dependency (Google)
<dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-spring-boot-starter-jaxws</artifactId> <version>3.2.6</version> </dependency>
-
-
Goto STS
-
New Class:
-
com.bharath.ws.soap.entities.Product
@Entity public class Product { @Id private int id; private String name; private String description; private int price; // Getters and setters }
-
-
New Interface:
-
com.bharath.ws.soap.repos.ProductRepository
public interface ProductRepository extends JpaRepository<Product, Integer> { }
-
- New Interface:
-
com.bharath.ws.soap.ProductWs
public interface ProductWs { List<Product> getProducts(); Product getProduct(int id); Product constructProduct(Product product); Product updateProduct(Product product); // delete } -
com.bharath.ws.soap.ProductWsImpl
public class ProductWsImpl implements ProductWs { @Override List<Product> getProducts() { } @Override Product getProduct(int id) { } @Override Product constructProduct(Product product) { } @Override Product updateProduct(Product product) { } // delete }
-
-
Implementation:
@Service public class ProductWsImpl implements ProductWs { @Autowired private ProductRepository productRepos; @Override List<Product> getProducts() { retrurn productRepo.findAll(); } @Override Product getProduct(int id) { return productRepo.findById(id).get(); // Optional product } @Override Product constructProduct(Product product) { return productRepo.save(product); } @Override Product updateProduct(Product product) { return productRepo.save(product); // checks if object exists } // delete // Getters and setters }
-
ProductWS
@WebService public interface ProductWs { @WebMethod List<Product> getProducts(); @WebMethod Product getProduct(int id); @WebMethod Product createProduct(Product product); @WebMethod Product updateProduct(Product product); }
-
Goto hellowebservice: Grab WebServiceConfig.java
@Configuration public class WebServiceConfig { @Autowired private Bus bus; @Autowired private ProductWs productService; @Bean public Endpoint endpoint() { Endpoint endpoint = new EndpointImpl(bus, productService); endpoint.publish("/products"); return endpoint; } }
-
application.properties
server.servlet.context-path=/productcrud cxf.path=/ spring.datasource.url=jdbc:mysql://localhost:3306/mydb spring.datasource.username=root spring.datasource.password=test
- Run as > Spring Boot App
- http://localhost:8080/productcrud/
- Copy the wsdl link
- Copy wsdl link and paste in SoapUI
- Open Request 1
- Enter values
- Submit
- Check database