BLOG POSTS
Java Web Services Tutorial – Step-by-Step Guide

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.

Leave a reply

Your email address will not be published. Required fields are marked