
Java Web Services Tutorial – Step-by-Step Guide
Java Web Services provide a standardized way for applications to communicate over networks using platform-independent protocols like HTTP and XML. Whether you’re building REST APIs or SOAP-based services, understanding web services is crucial for modern enterprise development and microservices architectures. This guide walks you through creating, deploying, and consuming Java web services using both JAX-RS for REST and JAX-WS for SOAP, with practical examples you can implement immediately.
Understanding Java Web Services Architecture
Java web services operate on two main paradigms: REST (Representational State Transfer) and SOAP (Simple Object Access Protocol). REST services use HTTP methods and JSON/XML for lightweight communication, while SOAP relies on XML messaging with built-in error handling and security features.
The Java ecosystem provides several frameworks for web service development:
- JAX-RS (Java API for RESTful Web Services) – Standard for REST services
- JAX-WS (Java API for XML Web Services) – Standard for SOAP services
- Spring Boot – Popular framework with built-in web service support
- Apache CXF – Comprehensive web services framework
- Jersey – Reference implementation of JAX-RS
Feature | REST (JAX-RS) | SOAP (JAX-WS) |
---|---|---|
Protocol | HTTP/HTTPS | HTTP, HTTPS, SMTP, JMS |
Message Format | JSON, XML, Plain Text | XML only |
Performance | Faster, lightweight | Slower due to XML overhead |
Security | HTTPS, OAuth, JWT | WS-Security, built-in standards |
Caching | HTTP caching supported | Limited caching options |
Setting Up Your Development Environment
Before building web services, ensure your development environment includes the necessary dependencies. For Maven-based projects, add these dependencies to your pom.xml
:
<dependencies>
<!-- For REST services -->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>3.1.3</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>3.1.3</version>
</dependency>
<!-- For SOAP services -->
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-rt</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
For deployment, you’ll need a servlet container like Tomcat, Jetty, or a full application server. When hosting on production servers, consider using VPS services for scalable deployment options.
Building REST Services with JAX-RS
Creating REST services with JAX-RS involves defining resource classes with appropriate annotations. Here’s a complete example of a user management service:
@Path("/users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class UserResource {
private static List<User> users = new ArrayList<>();
@GET
public Response getAllUsers() {
return Response.ok(users).build();
}
@GET
@Path("/{id}")
public Response getUserById(@PathParam("id") int id) {
User user = users.stream()
.filter(u -> u.getId() == id)
.findFirst()
.orElse(null);
if (user != null) {
return Response.ok(user).build();
} else {
return Response.status(Response.Status.NOT_FOUND)
.entity("User not found").build();
}
}
@POST
public Response createUser(User user) {
user.setId(users.size() + 1);
users.add(user);
return Response.status(Response.Status.CREATED)
.entity(user).build();
}
@PUT
@Path("/{id}")
public Response updateUser(@PathParam("id") int id, User updatedUser) {
for (int i = 0; i < users.size(); i++) {
if (users.get(i).getId() == id) {
updatedUser.setId(id);
users.set(i, updatedUser);
return Response.ok(updatedUser).build();
}
}
return Response.status(Response.Status.NOT_FOUND).build();
}
@DELETE
@Path("/{id}")
public Response deleteUser(@PathParam("id") int id) {
users.removeIf(user -> user.getId() == id);
return Response.noContent().build();
}
}
The corresponding User model class:
public class User {
private int id;
private String name;
private String email;
private String department;
// Constructors
public User() {}
public User(String name, String email, String department) {
this.name = name;
this.email = email;
this.department = department;
}
// Getters and setters
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getDepartment() { return department; }
public void setDepartment(String department) { this.department = department; }
}
Configure the REST application by creating an Application class:
@ApplicationPath("/api")
public class RestApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();
classes.add(UserResource.class);
return classes;
}
}
Implementing SOAP Services with JAX-WS
SOAP services require more boilerplate but provide stronger contracts and built-in error handling. Here’s a complete calculator service implementation:
@WebService(serviceName = "CalculatorService")
public class CalculatorService {
@WebMethod
public double add(@WebParam(name = "a") double a, @WebParam(name = "b") double b) {
return a + b;
}
@WebMethod
public double subtract(@WebParam(name = "a") double a, @WebParam(name = "b") double b) {
return a - b;
}
@WebMethod
public double multiply(@WebParam(name = "a") double a, @WebParam(name = "b") double b) {
return a * b;
}
@WebMethod
public double divide(@WebParam(name = "a") double a, @WebParam(name = "b") double b)
throws DivisionByZeroException {
if (b == 0) {
throw new DivisionByZeroException("Division by zero is not allowed");
}
return a / b;
}
@WebMethod
public CalculationResult performComplexCalculation(
@WebParam(name = "operation") String operation,
@WebParam(name = "operands") double[] operands) {
CalculationResult result = new CalculationResult();
result.setOperation(operation);
try {
switch (operation.toLowerCase()) {
case "sum":
double sum = Arrays.stream(operands).sum();
result.setResult(sum);
result.setSuccess(true);
break;
case "average":
double average = Arrays.stream(operands).average().orElse(0.0);
result.setResult(average);
result.setSuccess(true);
break;
default:
result.setSuccess(false);
result.setErrorMessage("Unsupported operation: " + operation);
}
} catch (Exception e) {
result.setSuccess(false);
result.setErrorMessage(e.getMessage());
}
return result;
}
}
Create custom exception classes for proper SOAP fault handling:
@WebFault(name = "DivisionByZeroFault")
public class DivisionByZeroException extends Exception {
private String faultInfo;
public DivisionByZeroException(String message) {
super(message);
this.faultInfo = message;
}
public String getFaultInfo() {
return faultInfo;
}
}
Define complex types for structured responses:
public class CalculationResult {
private String operation;
private double result;
private boolean success;
private String errorMessage;
// Constructors, getters, and setters
public CalculationResult() {}
public String getOperation() { return operation; }
public void setOperation(String operation) { this.operation = operation; }
public double getResult() { return result; }
public void setResult(double result) { this.result = result; }
public boolean isSuccess() { return success; }
public void setSuccess(boolean success) { this.success = success; }
public String getErrorMessage() { return errorMessage; }
public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; }
}
Deployment and Configuration
Configure your web application using web.xml
to handle both REST and SOAP services:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<display-name>Java Web Services Tutorial</display-name>
<!-- Jersey REST configuration -->
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jakarta.ws.rs.Application</param-name>
<param-value>com.example.RestApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
<!-- SOAP service endpoints -->
<listener>
<listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
</listener>
<servlet>
<servlet-name>calculator</servlet-name>
<servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>calculator</servlet-name>
<url-pattern>/calculator</url-pattern>
</servlet-mapping>
</web-app>
Create sun-jaxws.xml
for SOAP service configuration:
<?xml version="1.0" encoding="UTF-8"?>
<endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0">
<endpoint name="CalculatorService"
implementation="com.example.CalculatorService"
url-pattern="/calculator"/>
</endpoints>
Testing Your Web Services
Testing REST services can be done using curl commands or REST clients:
# Create a new user
curl -X POST http://localhost:8080/your-app/api/users \
-H "Content-Type: application/json" \
-d '{"name":"John Doe","email":"john@example.com","department":"Engineering"}'
# Get all users
curl -X GET http://localhost:8080/your-app/api/users
# Get specific user
curl -X GET http://localhost:8080/your-app/api/users/1
# Update user
curl -X PUT http://localhost:8080/your-app/api/users/1 \
-H "Content-Type: application/json" \
-d '{"name":"John Smith","email":"johnsmith@example.com","department":"DevOps"}'
# Delete user
curl -X DELETE http://localhost:8080/your-app/api/users/1
For SOAP services, create a simple Java client:
import javax.xml.namespace.QName;
import javax.xml.soap.*;
import javax.xml.ws.Service;
import java.net.URL;
public class SOAPClient {
public static void main(String[] args) throws Exception {
// Access the WSDL
URL wsdlUrl = new URL("http://localhost:8080/your-app/calculator?wsdl");
QName serviceName = new QName("http://example.com/", "CalculatorServiceService");
Service service = Service.create(wsdlUrl, serviceName);
CalculatorService calculator = service.getPort(CalculatorService.class);
// Test basic operations
System.out.println("5 + 3 = " + calculator.add(5, 3));
System.out.println("10 - 4 = " + calculator.subtract(10, 4));
System.out.println("6 * 7 = " + calculator.multiply(6, 7));
try {
System.out.println("8 / 2 = " + calculator.divide(8, 2));
System.out.println("8 / 0 = " + calculator.divide(8, 0));
} catch (DivisionByZeroException e) {
System.out.println("Error: " + e.getMessage());
}
// Test complex operation
double[] numbers = {1, 2, 3, 4, 5};
CalculationResult result = calculator.performComplexCalculation("sum", numbers);
if (result.isSuccess()) {
System.out.println("Sum result: " + result.getResult());
} else {
System.out.println("Error: " + result.getErrorMessage());
}
}
}
Real-World Use Cases and Applications
Java web services excel in various enterprise scenarios:
- Microservices Architecture – REST services enable loose coupling between components
- Legacy System Integration – SOAP services provide robust integration with older enterprise systems
- Mobile Backend Services – REST APIs serve mobile applications with JSON responses
- B2B Data Exchange – SOAP services offer standardized data exchange with partners
- Internal API Development – REST services facilitate internal system communication
Performance considerations become critical in production environments. REST services typically handle 2000-5000 requests per second on standard hardware, while SOAP services handle approximately 1000-2000 requests per second due to XML processing overhead. For high-traffic applications, consider dedicated servers to ensure consistent performance.
Security Implementation and Best Practices
Implement proper security measures for production web services:
@Path("/secure")
@RolesAllowed({"USER", "ADMIN"})
public class SecureResource {
@Context
private SecurityContext securityContext;
@GET
@Path("/user-data")
public Response getUserData() {
String username = securityContext.getUserPrincipal().getName();
// Validate user permissions
if (!securityContext.isUserInRole("USER")) {
return Response.status(Response.Status.FORBIDDEN).build();
}
// Return user-specific data
return Response.ok("Data for user: " + username).build();
}
@POST
@Path("/admin-operation")
@RolesAllowed("ADMIN")
public Response performAdminOperation(AdminRequest request) {
// Admin-only functionality
return Response.ok("Admin operation completed").build();
}
}
Implement input validation and sanitization:
@POST
@Path("/users")
public Response createUser(@Valid User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
List<String> errors = bindingResult.getAllErrors()
.stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.toList());
return Response.status(Response.Status.BAD_REQUEST)
.entity(errors).build();
}
// Sanitize input
user.setName(sanitizeInput(user.getName()));
user.setEmail(validateEmail(user.getEmail()));
// Process valid user
return processUser(user);
}
Common Issues and Troubleshooting
Several issues frequently occur during web service development:
- ClassNotFoundException – Usually indicates missing JAX-RS or JAX-WS dependencies
- 404 Not Found – Check URL patterns in web.xml and @Path annotations
- 415 Unsupported Media Type – Verify Content-Type headers match @Consumes annotations
- 500 Internal Server Error – Review server logs for detailed stack traces
- CORS Issues – Implement proper CORS filters for browser-based clients
Add CORS support for REST services:
@Provider
public class CORSFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext request,
ContainerResponseContext response) throws IOException {
response.getHeaders().add("Access-Control-Allow-Origin", "*");
response.getHeaders().add("Access-Control-Allow-Headers",
"Origin, Content-Type, Accept, Authorization");
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
}
}
For SOAP service debugging, enable detailed logging:
# Add to your logging configuration
com.sun.xml.ws.level=FINE
com.sun.xml.ws.transport.http.level=FINE
Monitor performance using JMX beans and implement proper logging:
@Path("/users")
public class UserResource {
private static final Logger logger = LoggerFactory.getLogger(UserResource.class);
@GET
public Response getAllUsers() {
long startTime = System.currentTimeMillis();
try {
List<User> users = userService.getAllUsers();
logger.info("Retrieved {} users in {}ms",
users.size(), System.currentTimeMillis() - startTime);
return Response.ok(users).build();
} catch (Exception e) {
logger.error("Error retrieving users", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
}
}
Java web services remain essential for enterprise integration and modern application development. By following these implementation patterns and best practices, you can build robust, scalable web services that handle real-world production requirements effectively. Remember to consider your specific use case requirements when choosing between REST and SOAP approaches, and always implement proper security measures for production deployments.

This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.
This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.