OfOf course! Let's create a comprehensive guide to creating a web service in Java. We'll cover the two most popular approaches:
- JAX-RS (Java API for RESTful Web Services): The modern, standard, and most common way to create RESTful web services. We'll use the reference implementation, Jersey.
- JAX-WS (Java API for XML Web Services): The older, standard way to create SOAP web services. We'll use the reference implementation, Metro.
Part 1: Creating a RESTful Web Service with JAX-RS (Jersey)
REST (Representational State Transfer) is an architectural style that uses standard HTTP methods (GET, POST, PUT, DELETE) to perform operations on resources. It's lightweight, easy to use, and the de-facto standard for modern web APIs and microservices.
Why JAX-RS?
- Standard: It's a Java EE (now Jakarta EE) standard, so your code is portable across different application servers.
- Simplicity: Annotating a plain Java class is all you need to create a web service.
- Flexibility: Easy to integrate with JSON libraries (like Jackson) and dependency injection frameworks (like CDI).
Step-by-Step Guide: Creating a RESTful Service with Jersey
We'll use Maven to manage our project dependencies, which is the standard in the Java world.
Step 1: Set up a Maven Project
Create a new Maven project. In your pom.xml, add the necessary dependencies for Jersey and a JSON provider (to handle JSON data automatically).
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>java-webservice</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<jersey.version>3.1.1</jersey.version>
</properties>
<dependencies>
<!-- Jersey Server -->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-http</artifactId>
<version>${jersey.version}</version>
</dependency>
<!-- Jackson for JSON support -->
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-binding</artifactId>
<version>${jersey.version}</version>
</dependency>
<!-- For unit testing -->
<dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-core</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<mainClass>com.example.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
Step 2: Create the Resource Class
This is the core of your web service. It's a simple Java class with JAX-RS annotations.
package com.example;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
// The @Path annotation defines the base URL for this resource.
@Path("/users")
public class UserResource {
// A simple in-memory "database"
private ConcurrentMap<Integer, User> userStore = new ConcurrentHashMap<>();
private int currentId = 1;
public UserResource() {
// Seed with some data
userStore.put(currentId++, new User(1, "Alice"));
userStore.put(currentId++, new User(2, "Bob"));
}
// @GET: Handles HTTP GET requests.
// @Produces: Specifies the media type this method can return (e.g., JSON).
@GET
@Produces(MediaType.APPLICATION_JSON)
public List<User> getAllUsers() {
return new ArrayList<>(userStore.values());
}
// @GET with a path parameter: e.g., /users/1
@GET
@Path("/{userId}")
@Produces(MediaType.APPLICATION_JSON)
public Response getUserById(@PathParam("userId") int userId) {
User user = userStore.get(userId);
if (user == null) {
// Return a 404 Not Found response if the user doesn't exist.
return Response.status(Response.Status.NOT_FOUND).build();
}
// Return a 200 OK response with the user object.
return Response.ok(user).build();
}
// @POST: Handles HTTP POST requests to create a new resource.
// @Consumes: Specifies the media type this method can accept (e.g., JSON).
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createUser(User user) {
// In a real app, you would validate the input.
user.setId(currentId++);
userStore.put(user.getId(), user);
// Return a 201 Created response with the location of the new resource.
return Response.status(Response.Status.CREATED)
.entity(user)
.build();
}
// A simple data model (POJO)
public static class User {
private int id;
private String name;
public User() {}
public User(int id, String name) {
this.id = id;
this.name = name;
}
// Getters and Setters are crucial for JSON serialization/deserialization
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; }
}
}
Step 3: Create the Application Class and Main Method
This class configures and starts the Jersey web server.
package com.example;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.grizzly.http.server.HttpServer;
public class Main {
public static void main(String[] args) {
// Create a resource config that scans for JAX-RS resources and providers.
ResourceConfig config = new ResourceConfig(UserResource.class);
// Create and start the Grizzly HTTP server.
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(
URI.create("http://localhost:8080/"), config);
System.out.println("Server started. Available resources:");
System.out.println(" - GET http://localhost:8080/api/users");
System.out.println(" - GET http://localhost:8080/api/users/1");
System.out.println(" - POST http://localhost:8080/api/users");
System.out.println("Press Enter to stop the server...");
try {
System.in.read();
} catch (Exception e) {
e.printStackTrace();
} finally {
server.shutdownNow();
}
}
}
Note: You'll need to add the import for GrizzlyHttpServerFactory and URI.
Step 4: Run and Test
-
Run the application:
mvn compile exec:java
You should see the server startup message.
-
Test the endpoints:
-
Get all users: Open your browser or use a tool like
curlor Postman.curl http://localhost:8080/api/users
Output:
[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}] -
Get a single user:
curl http://localhost:8080/api/users/1
Output:
{"id":1,"name":"Alice"} -
Create a new user:
curl -X POST \ -H "Content-Type: application/json" \ -d '{"name":"Charlie"}' \ http://localhost:8080/api/usersOutput:
{"id":3,"name":"Charlie"}
-
Part 2: Creating a SOAP Web Service with JAX-WS
SOAP (Simple Object Access Protocol) is a protocol for exchanging structured information in web services. It's more complex, relies heavily on XML, and often uses WSDL (Web Services Description Language) to define the service contract.
Why JAX-WS?
- Standard: It's the Java standard for building SOAP services.
- WSDL First: It allows you to generate a service contract (WSDL) from your Java code, or vice-versa.
- Tooling: Excellent IDE and build tool support for generating client and server code.
Step-by-Step Guide: Creating a SOAP Service with JAX-WS
Step 1: Set up a Maven Project
Create a new Maven project. For a simple, standalone server, we don't need many extra dependencies beyond the JAX-WS API, which is included in the JDK.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>java-soap-service</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<!-- No extra dependencies needed for a simple JAX-WS endpoint -->
</project>
Step 2: Create the Service Endpoint Interface (SEI)
This is a Java interface that defines the methods your web service will expose. The @WebService and @WebMethod annotations mark it as a SOAP service.
package com.example;
import jakarta.jws.WebMethod;
import jakarta.jws.WebParam;
import jakarta.jws.WebService;
// @WebService marks this class as a Web Service.
@WebService
public interface HelloWorld {
// @WebMethod marks this method as a web service operation.
// @WebParam specifies the name of the parameter in the SOAP message.
@WebMethod
String sayHello(@WebParam(name = "name") String name);
}
Step 3: Create the Service Implementation
This is the concrete class that implements the HelloWorld interface.
package com.example;
import jakarta.jws.WebService;
// @WebService specifies the interface this class implements.
@WebService(endpointInterface = "com.example.HelloWorld")
public class HelloWorldImpl implements HelloWorld {
@Override
public String sayHello(String name) {
if (name == null || name.trim().isEmpty()) {
return "Hello, Stranger!";
}
return "Hello, " + name + "!";
}
}
Step 4: Create the Publisher Class
This class is responsible for publishing your service to a specific URL so clients can access it.
package com.example;
import jakarta.xml.ws.Endpoint;
public class HelloWorldPublisher {
public static void main(String[] args) {
// The URL where the service will be published.
String url = "http://localhost:9999/ws/hello";
// Publish the implementation.
Endpoint.publish(url, new HelloWorldImpl());
System.out.println("SOAP Service published successfully!");
System.out.println("WSDL is available at: " + url + "?wsdl");
System.out.println("Press Enter to stop the server...");
try {
System.in.read();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Step 5: Run and Test
-
Run the publisher:
mvn compile exec:java
You should see the message indicating the service is published.
-
View the WSDL: Open your web browser and navigate to
http://localhost:9999/ws/hello?wsdl. You will see the XML definition of your service. -
Test the service: You can use a tool like SoapUI to create a new project from the WSDL URL and test the
sayHellooperation.- Request:
<sayHello><name>World</name></sayHello> - Response:
<ns2:sayHelloResponse><return>Hello, World!</return></ns2:sayHelloResponse>
- Request:
Comparison: REST (JAX-RS) vs. SOAP (JAX-WS)
| Feature | REST (JAX-RS) | SOAP (JAX-WS) |
|---|---|---|
| Style | Architectural style (resource-oriented) | Protocol (message-oriented) |
| Data Format | Primarily JSON (also XML, HTML, plain text) | Strictly XML (with SOAP envelope) |
| Standard | JAX-RS (Jakarta EE) | JAX-WS (Jakarta EE) |
| WSDL | No (OpenAPI/Swagger is used for REST APIs) | Yes, mandatory for describing the service contract |
| Performance | Generally faster and lighter due to JSON/less overhead | Slower and heavier due to XML/SOAP envelope |
| Use Cases | Public APIs, Mobile apps, Microservices, Web Apps | Enterprise systems (e.g., banking, ERP), B2B integrations |
| Security | Relies on HTTPS, OAuth, API Keys | Built-in, standardized security (WS-Security) |
| Tooling | Good (Jersey, RESTEasy, Swagger) | Excellent (WSDL-first generation, SoapUI) |
| Example Framework | Jersey, RESTEasy | Metro, Apache CXF |
Which One Should You Choose?
-
Choose REST (JAX-RS) if:
- You are building a public-facing API.
- Performance and low overhead are important.
- You need to support a wide range of clients (web, mobile, etc.).
- Your team prefers simplicity and flexibility.
-
Choose SOAP (JAX-WS) if:
- You are integrating with large, established enterprise systems.
- You require a strict, formal contract (WSDL) between the client and server.
- You need advanced, standardized security features like WS-Security.
- Your project is mandated to use SOAP.
For most new projects, REST (JAX-RS) is the recommended choice due to its simplicity, performance, and widespread adoption.
