Metro Web Services: High-Performance SOAP
While REST dominates modern API design, SOAP remains firmly entrenched in enterprise systems—particularly in finance, healthcare, telecommunications, and government sectors where contract-first design, strong typing, and standardized security matter. When you need SOAP on the Java platform, Metro is one of the most capable and performant stacks available.
This post explores what Metro is, how to build high-performance SOAP services with it, and the tuning strategies that separate a sluggish endpoint from a production-grade one.
What Is Metro?
Metro is a comprehensive web services stack that combines two major components:
- JAX-WS (Jakarta XML Web Services) — the reference implementation of the Java API for XML-based web services.
- WSIT (Web Services Interoperability Technologies) — support for WS-* standards like WS-Security, WS-ReliableMessaging, WS-AtomicTransaction, and WS-Policy.
Together they provide a full-featured, standards-compliant SOAP platform that interoperates cleanly with .NET (WCF) and other WS-* implementations.
Setting Up a Basic Service
Metro embraces a contract-first or code-first approach. Here's a minimal code-first service endpoint:
import jakarta.jws.WebService;
import jakarta.jws.WebMethod;
@WebService(serviceName = "QuoteService")
public class QuoteService {
@WebMethod
public double getQuote(String symbol, int shares) {
double pricePerShare = lookupPrice(symbol);
return pricePerShare * shares;
}
private double lookupPrice(String symbol) {
// lookup logic
return 142.75;
}
}
Publishing it for a quick test is trivial:
import jakarta.xml.ws.Endpoint;
public class Publisher {
public static void main(String[] args) {
Endpoint.publish(
"http://localhost:8080/quotes",
new QuoteService()
);
}
}
Metro auto-generates the WSDL, available at http://localhost:8080/quotes?wsdl.
The Performance Challenge
SOAP's reputation for being "slow" is largely a matter of configuration. The primary bottlenecks are:
- XML parsing and serialization overhead
- JAXB (un)marshalling of large object graphs
- Verbose message payloads consuming bandwidth
- Repeated schema validation
- Security processing (signing, encryption)
Metro provides mechanisms to address each of these.
Optimization 1: Fast Infoset
The single biggest win for high-throughput scenarios is Fast Infoset (FI)—a binary encoding of XML that dramatically reduces message size and parse time. When both client and server support it, Metro negotiates FI automatically via content negotiation.
Enable it on the client via the FastInfosetFeature:
import com.sun.xml.ws.developer.FastInfosetFeature;
QuoteService port = service.getPort(
QuoteService.class,
new FastInfosetFeature()
);
Or through a system property for pessimistic negotiation:
System.setProperty(
"com.sun.xml.ws.client.ContentNegotiation",
"optimistic"
);
In benchmarks with document-heavy payloads, Fast Infoset commonly reduces payload size by 40–60% and parse time by a similar margin.
Optimization 2: MTOM for Binary Data
If your service transfers binary content (images, PDFs, documents), base64-encoding it inline bloats messages by ~33%. MTOM (Message Transmission Optimization Mechanism) streams binary attachments alongside the SOAP envelope instead.
import jakarta.xml.ws.soap.MTOM;
@MTOM(threshold = 1024)
@WebService(serviceName = "DocumentService")
public class DocumentService {
@WebMethod
public byte[] fetchDocument(String id) {
return loadBytes(id);
}
}
The threshold attribute means only payloads above 1 KB use MTOM optimization—small values stay inline to avoid attachment overhead.
Optimization 3: HTTP Persistent Connections and Streaming
Metro's stream-based architecture avoids buffering entire messages in memory where possible. On the client, reuse the Service and port instances—they're expensive to create but safe to reuse across threads when configured properly.
Enable HTTP connection pooling and set timeouts to prevent hung threads:
import jakarta.xml.ws.BindingProvider;
import java.util.Map;
Map<String, Object> ctx = ((BindingProvider) port).getRequestContext();
ctx.put("com.sun.xml.ws.connect.timeout", 5000);
ctx.put("com.sun.xml.ws.request.timeout", 10000);
Optimization 4: JAXB Tuning
JAXB marshalling can dominate CPU time for complex types. Key strategies:
-
Reuse
JAXBContext— it's thread-safe and costly to build. Metro caches this internally, but custom marshalling code should too. - Avoid unnecessary schema validation in production; validate at the boundary during testing instead.
- Prefer flat, well-designed schemas over deeply nested polymorphic hierarchies.
// Build once, reuse everywhere
private static final JAXBContext CONTEXT =
JAXBContext.newInstance(Quote.class);
Optimization 5: Secure Without Sacrificing Speed
WS-Security is inherently
Top comments (0)