POSTED ON 31 JUL 2025

READING TIME: 5 MINUTES

How We Made 150+ JAXB Errors Disappear

author-image
Cezary Czekalski
Software Engineer

WSDL to Java: Surviving SOAP in 2025

Some technologies age like wine. Others, like SOAP, age like milk.

In 2025, REST rules the web, gRPC powers internal services, and GraphQL is fashionable again. And yet - SOAP still lurks in telco stacks, in critical systems that power provisioning, billing, and network orchestration.

When we were asked to consume a SOAP API from a legacy WSDL file, it sounded simple. Generate some Java classes, send a few requests, parse a response. We've done it before.

But as it turned out, this particular WSDL had other ideas.

This is the story of how we got it working - through trial, tools, errors, a few dead ends, and ultimately, a solution that restored sanity (and SOAP support) to our system.

The Task

We were given a WSDL - short for Web Services Description Language. It’s the blueprint of a SOAP API: a structured, XML-based contract that tells you what messages are expected, how they’re shaped, and where to send them.

Our job:
Generate the client classes. Marshal requests into XML. Unmarshal the responses. Wrap it all in a Feign client so it fits into a modern Java microservice architecture.Consume the API in Java

SOAP isn’t our first choice, but it’s still deeply entrenched in telco ecosystems - and often the only way to talk to older systems.

So we started building.

Attempt 1: First Shot with Axis2 – and a Flood of Errors

We kicked things off using Apache Axis2 and its wsdl2java tool. It’s a common choice for generating Java classes from WSDL, and with a few Gradle tweaks, we had it running:

groovy

CopyEdit

tasks.register('generateFromWsdl', JavaExec) {

    mainClass.set('org.apache.axis2.wsdl.WSDL2Code')

    classpath = configurations.axis2Tools

    args = [

        '-uri', 'file.wsdl',

        '-d', 'xmlbeans',

        '-o', "${projectDir}/generated",

        ...

    ]

}

The classes were generated. So far, so good.
Then we plugged it into our Feign SOAP client.

And boom - this:

text

CopyEdit

IllegalAnnotationsException: 150 counts of IllegalAnnotationExceptions

GeneratedSoapClass is an interface, and JAXB can't handle interfaces.

Axis2 had generated interfaces in the model layer. JAXB, which we were relying on for marshaling and unmarshaling, doesn't like interfaces. Not even a little bit.

We tried multiple binding options - ADB, JAXB RI - but no success. After wrestling with it for far too long, we decided to change course.

Attempt 2: Switch to CXF – Different Tool, Different Problem

Our team lead suggested switching to Apache CXF, a more modern framework with better tooling support.

We swapped the codegen task to use wsdl2java from CXF:

groovy

CopyEdit

tasks.register('generateFromWsdl', Exec) {
    workingDir "$buildDir/apache-cxf/bin"
    commandLine './wsdl2java', '-client', ...
}

This time the classes looked cleaner. Structurally more sound. But once again, when we tried to marshal and unmarshal a request:

text

CopyEdit

IllegalAnnotationsException:

Two classes have the same XML type name "generatedType".

Use @XmlType.name and @XmlType.namespace to assign different names.

Even after applying the -xjc-npa flag (which forces namespace annotations into individual classes rather than relying on package-info.java), the problem persisted.

So we stepped back and started looking into how the marshaller itself was being initialized.

Attempt 3: Finding the Real Fix - Custom JAXB with Jakarta and Classloaders

The default JAXB marshaller in our Feign client was choking on the generated classes. Not because they were wrong - but because the context being built for marshalling wasn’t seeing them correctly.

The issue wasn’t just about namespaces or duplicates. It was about how the context was created and which classloader it used.

We scrapped the default marshaller logic and created a custom one, using Jakarta JAXB backed by Glassfish’s runtime. This allowed us to explicitly control the context and ensure everything was correctly scoped.

Here’s the encoder fix:

java

CopyEdit

jakarta.xml.bind.Marshaller marshaller =

    JAXBContext.newInstance(bodyType.getPackageName(), bodyType.getClassLoader())

               .createMarshaller();

And the corresponding decoder:

java

CopyEdit

jakarta.xml.bind.Unmarshaller unmarshaller =

    JAXBContext.newInstance(((Class<?>) type).getPackageName(), ((Class<?>) type).getClassLoader())

               .createUnmarshaller();

With this in place, everything snapped into place.

SOAP messages were being generated. Responses were unmarshaling. No more type errors. No more annotation collisions.

Finally, a working SOAP integration in 2025.

What This Means (and Why We Bothered)

This wasn’t just an exercise in nostalgia. Legacy SOAP APIs still sit at the heart of critical telco platforms - from subscriber management to OSS systems and billing gateways.

When you’re modernising a stack or integrating new capabilities, you’ll often run into these interfaces. They're not pretty. They’re not RESTful. But they’re essential.

What matters is how you deal with them.

At Sonalake, we don’t treat legacy like a liability. We treat it like a puzzle. One that, with the right tools and a bit of persistence, can be solved without compromising quality.

We use this kind of experience to power real integrations  - bridging modern cloud-native services with systems that were designed long before Kubernetes was a thing.

Final Thoughts

SOAP in 2025 isn’t dead. It’s just hiding in plain sight.

It takes patience, experimentation, and collaboration to get these kinds of integrations working right - but when they do, they form the backbone of critical telco operations.

We know how to navigate these waters - because we already have.

If your next project involves not only the newest technologies but also more mature legacy interfaces, outdated protocols, or ancient WSDLs… we should probably talk.

Get in touch with Sonalake today!

TECH LEADERS TRUST SONALAKE

We make software better every day

Get in touch

Copyright © 2025 Sonalake Limited, registered in Ireland No. 445927. All rights reserved.Privacy Policy

Nr Cert. 18611 ISO/IEC 27001