To Support Apache CXF in Quarkus application the Quarkiverse cxf dependency to be added as part of pom.xml (Quarkiverse is a github organization for any contributors to develop Quarkus extensions). Since current version of Quarkus (2.2.9 final ) doesn't support Apache CXF. So we are going for Quarkiverse CXF dependency.
<dependency>
<groupId>io.quarkiverse.cxf</groupId>
<artifactId>quarkus-cxf</artifactId>
<version>${quarkiverse.version}</version>
</dependency>
Lets create a simple SOAP service which will accept the enrollment number of a student and respond back the entire student information
Now lets start with creating schema classes
StudentInfo.java
package org.brktech.quarkus.cxfpoc.schema;
import java.io.Serializable;
import java.util.Objects;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@XmlType(name = "Student")
@XmlRootElement
public class StudentInfo implements Serializable {
private static final long serialVersionUID = 3483155172700548490L;
private String firstName;
private String middleName;
private String lastName;
private String age;
private String stdClass;
private String classSection;
private String rollNumber;
public String getFirstName() {
return firstName;
}
@XmlElement
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getMiddleName() {
return middleName;
}
@XmlElement
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
public String getLastName() {
return lastName;
}
@XmlElement
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getAge() {
return age;
}
@XmlElement
public void setAge(String age) {
this.age = age;
}
public String getStdClass() {
return stdClass;
}
@XmlElement
public void setStdClass(String stdClass) {
this.stdClass = stdClass;
}
public String getClassSection() {
return classSection;
}
@XmlElement
public void setClassSection(String classSection) {
this.classSection = classSection;
}
public String getRollNumber() {
return rollNumber;
}
@XmlElement
public void setRollNumber(String rollNumber) {
this.rollNumber = rollNumber;
}
@Override
public String toString() {
return "StudentInfo [firstName=" + firstName + ", middleName=" + middleName + ", lastName=" + lastName
+ ", age=" + age + ", stdClass=" + stdClass + ", classSection=" + classSection + ", rollNumber="
+ rollNumber + "]";
}
@Override
public int hashCode() {
return Objects.hash(age, classSection, firstName, lastName, middleName, rollNumber, stdClass);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
StudentInfo other = (StudentInfo) obj;
return Objects.equals(age, other.age) && Objects.equals(classSection, other.classSection)
&& Objects.equals(firstName, other.firstName) && Objects.equals(lastName, other.lastName)
&& Objects.equals(middleName, other.middleName) && Objects.equals(rollNumber, other.rollNumber)
&& Objects.equals(stdClass, other.stdClass);
}
}
GetStudentByRollNumberRequest.java
package org.brktech.quarkus.cxfpoc.schema;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@XmlType(name = "GetStudentInfoByRollNumberRequest")
@XmlRootElement
public class GetStudentByRollNumberRequest {
private String rollNumbber;
public String getRollNumbber() {
return rollNumbber;
}
@XmlElement
public void setRollNumbber(String rollNumbber) {
this.rollNumbber = rollNumbber;
}
@Override
public String toString() {
return "GetStudentByRollNumberRequest [rollNumbber=" + rollNumbber + "]";
}
}
StudentInformationResponse.java
package org.brktech.quarkus.cxfpoc.schema;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@XmlType(name = "StudentInformationResponse")
@XmlRootElement
public class StudentInformationResponse implements Serializable {
private static final long serialVersionUID = -3016596112656167156L;
private List<StudentInfo> studentInformation;
public List<StudentInfo> getStudentInformation() {
if (studentInformation == null) {
studentInformation = new ArrayList<>();
}
return studentInformation;
}
@XmlElement
public void setStudentInformation(List<StudentInfo> studentInformation) {
this.studentInformation = studentInformation;
}
@Override
public String toString() {
return "StudentInformationResponse [studentInformation=" + studentInformation + "]";
}
}
Lets Create a Service Interface as below
StudentInfoService.java
package org.brktech.quarkus.cxfpoc.service;
import javax.jws.WebMethod;
import javax.jws.WebService;
import org.brktech.quarkus.cxfpoc.schema.GetStudentByRollNumberRequest;
import org.brktech.quarkus.cxfpoc.schema.StudentInformationResponse;
@WebService
public interface StudentInfoService {
@WebMethod
public StudentInformationResponse getStudentinformationByRollNuber(
GetStudentByRollNumberRequest getStudentByRollNumberRequest);
}
Now lets create a StudentInfo data loader class which holds the sample StudentInfo as a map.
StudentInfoLoader.java
package org.brktech;
import java.util.HashMap;
import java.util.Map;
import org.brktech.quarkus.cxfpoc.schema.StudentInfo;
public class StudentInfoLoader {
public static Map<String, StudentInfo> getAllStudentInfoAsMap() {
StudentInfo info1 = new StudentInfo();
info1.setFirstName("Anshuman");
info1.setMiddleName("");
info1.setLastName("Mohapatra");
info1.setStdClass("5");
info1.setClassSection("A");
info1.setAge("9");
info1.setRollNumber("tech100");
StudentInfo info2 = new StudentInfo();
info2.setFirstName("Anil");
info2.setMiddleName("");
info2.setLastName("Vunnava");
info2.setStdClass("5");
info2.setClassSection("A");
info2.setAge("9");
info2.setRollNumber("tech101");
StudentInfo info3 = new StudentInfo();
info3.setFirstName("Bharath");
info3.setMiddleName("");
info3.setLastName("Ravi");
info3.setStdClass("5");
info3.setClassSection("A");
info3.setAge("9");
info3.setRollNumber("tech103");
Map<String, StudentInfo> studentinfoMap = new HashMap<>();
studentinfoMap.put(info1.getRollNumber(), info1);
studentinfoMap.put(info2.getRollNumber(), info2);
studentinfoMap.put(info3.getRollNumber(), info3);
return studentinfoMap;
}
}
Lets create a Service Implementation class below
StudentInfoServiceImpl.java
package org.brktech.quarkus.cxfpoc.service.impl;
import java.util.Map;
import java.util.Map.Entry;
import org.brktech.StudentInfoLoader;
import org.brktech.quarkus.cxfpoc.schema.GetStudentByRollNumberRequest;
import org.brktech.quarkus.cxfpoc.schema.StudentInfo;
import org.brktech.quarkus.cxfpoc.schema.StudentInformationResponse;
import org.brktech.quarkus.cxfpoc.service.StudentInfoService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class StudentInfoServiceImpl implements StudentInfoService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private final Map<String, StudentInfo> studentInfoAsMap;
StudentInfoServiceImpl() {
studentInfoAsMap = StudentInfoLoader.getAllStudentInfoAsMap();
logger.info("Student Info Map Loaded");
logger.info("************************************************************");
for (Entry<String, StudentInfo> stdInfo : studentInfoAsMap.entrySet()) {
logger.info("Roll No. :: " + stdInfo.getKey());
logger.info("Student Info. :: " + stdInfo.getValue());
}
logger.info("************************************************************");
}
@Override
public StudentInformationResponse getStudentinformationByRollNuber(
GetStudentByRollNumberRequest getStudentByRollNumberRequest) {
StudentInformationResponse informationResponse = new StudentInformationResponse();
if (getStudentByRollNumberRequest != null) {
StudentInfo info = studentInfoAsMap.get(getStudentByRollNumberRequest.getRollNumbber());
if (info != null) {
logger.info(
"Student Info record found with Roll No : " + getStudentByRollNumberRequest.getRollNumbber());
logger.info("Student info record :: " + info);
informationResponse.getStudentInformation().add(info);
}
}
return informationResponse;
}
}
to expose the service properties to be added as part of application.properties file
application.properties
#override the default http port 8080
quarkus.http.port=8282
#path where cxf services are listed
quarkus.cxf.path=/cxf
#/studentinfoservice is where wsdl is exposed example
$http://localhost:8282/cxf/studentinfoservice?wsdl
quarkus.cxf.endpoint."/studentinfoservice".implementor=org.brktech.quarkus.cxfpoc.service.StudentInfoServiceImpl
Standalone deployment
Lets build the source through maven with the below command
mvn clean package
We can see the source is build successfully as below
now go to the target/quarkus-app directory (projectfolder/target/quarkus-app) and execute the below command
java -jar quarkus-run.jar
Now we can see application is started
we can now see the list of services exposed through http://localhost:8282/cxf through any browser
we can access the studentinfoservice through the below URL http://localhost:8282/cxf/studentinfoservice?wsdl
Docker Deployment
Quarkus application by default provides us the capability on deploying the source to docker container on successful source build, all we need to is to pass an additional optional parameter "-Dquarkus.container-image.build=true" while building the source, on successful build quarkus will create an docker image with our application and deploy the image in the docker container (docker to be installed before performing this activity)
mvn clean package -Dquarkus.container-image.build=true
we can see now a docker container image is created
We can also check the docker images list in our local docker container through "docker images" command
docker images
now we can run the container image by executing the below command
docker run --rm -p 8282:8282 -p 5005:5005 bharaniravikanth/quarkus-cxf-soap-webservice:0.0.1-SNAPSHOT
We can also push our docker container image to docker registry on successful source build, we need to pass optional parameter "-Dquarkus.container-image.push=true" through maven package command.
mvn clean package -Dquarkus.container-image.push=true
Kubernetes Deployment
Prerequisites:
1. Docker
2. Kubernetes cluster
lets create a YAML file with deployment and service information
Deployments
apiVersion: apps/v1
kind: Deployment
metadata:
name: quarkus-cxf-demo
namespace: default
spec:
replicas: 1
selector:
matchLabels:
brktech: web
template:
metadata:
labels:
brktech: web
spec:
containers:
- name: quarkus-cxf-demo-site
image: bharaniravikanth/quarkus-cxf-soap-webservice:0.0.1-SNAPSHOT
on deployment Kubernetes will create only one pod (replicas value is 1)
Service
apiVersion: v1
kind: Service
metadata:
name: quarkus-cxf-demo-service
namespace: default
spec:
selector:
brktech: web
ports:
- name: http
nodePort: 30475
port: 8787
protocol: TCP
targetPort: 8282
clusterIP: 10.96.0.12
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 192.168.0.104
As per the service configuration Kubernetes will expose the our application through the port 8787 which internally redirects to the port 8282 (Target port), since the service type is LoadBalancer, when we scale the pods, Kubernetes handles the load balancing automatically between the pods .
the final yaml file looks as below
apiVersion: apps/v1
kind: Deployment
metadata:
name: quarkus-cxf-demo
namespace: default
spec:
replicas: 1
selector:
matchLabels:
brktech: web
template:
metadata:
labels:
brktech: web
spec:
containers:
- name: quarkus-cxf-demo-site
image: bharaniravikanth/quarkus-cxf-soap-webservice:0.0.1-SNAPSHOT
---
apiVersion: v1
kind: Service
metadata:
name: quarkus-cxf-demo-service
namespace: default
spec:
selector:
brktech: web
ports:
- name: http
nodePort: 30475
port: 8787
protocol: TCP
targetPort: 8282
clusterIP: 10.96.0.12
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 192.168.0.104
to deploy in Kubernetes through yaml file we need to execute the command below
kubectl apply -f fileName.yaml
I have created a yaml file with quarkus_cxf_poc.yaml, so our kubectl apply command will be as follows
On successful execution we get response as deployment is created, service is created
Once the yaml is applied we can check the deployments, services and pods through command line or Kubernetes dashboard
Check deployments the command below
kubectl get deployments
Check deployments through Kubernetes dashboard
Check Services through command line
kubectl get deployments
Check Services through dashboard
Since the replicas are set to 1 the number of pods (are the light weight containers which responsible for running our application) created are one
Once the Kubernetes deployment is complete and our services are up we can access our cxf application through through the port 8787 (which was part of yaml file)
And Wsdl will be exposed through the url http://localhost:8787/cxf/studentinfoservice?wsdl
To test our application I am using SOAP UI
Lets create a soap project with our wsdl http://localhost:8787/cxf/studentinfoservice?wsdl
Once the project is created the Structure looks as below
click on the request and pass the request parameter and hit the request through the url http://localhost:8787/cxf/studentinfoservice
we can see the response back under response section of the request
to check the logs of the application in kubernetes, we can check through command line or dashboard
Checking logs through command line
kubectl logs <pod-name>
Checking logs through dashboard
Login to dashboard and click on Pods and select the pod menu (vertical dots) and click on logs, Dashboard will go inside the pod and displays the logs of the application running through the pod.
You can find the entire project through my github repository
https://github.com/bharaniravikanth/camel-quarkus-brk-tech-repo/tree/main/camel-quarkus-integration/quarkus-cxf-soap-webservice
1 comments:
Write commentsEmoticonEmoticon