Public sector organisation
Upgrade the application without changing user behaviour
How the public-sector case study moved from Java 6 and Spring 2 towards Java 8, Spring 5, generated SOAP classes at build time, and a better security model while still behaving like the same WebLogic application.
Phase 2 was where the platform began to change technically in a serious way, but still without changing its outward behaviour.
That distinction matters in legacy modernisation.
The fastest way to lose trust is to bundle framework upgrades, deployment changes, runtime changes, and user-facing change into one giant release. We did not want that. We wanted the system to feel boring to the business while it was becoming far less brittle underneath.
Moving from Java 6 to Java 8
The base runtime had to move first.
Running a customer and order platform on Java 6 was not just a question of age. It affected library choice, testing capability, security posture, and the pool of engineers who could work effectively in the codebase without treating it like an archaeological dig.
So the first technical lift in this phase was to move to Java 8.
That meant tackling predictable compatibility work:
- cleaning up old compiler assumptions
- dealing with outdated third-party libraries
- tightening parts of the build that had previously relied on loose conventions
- proving that the EAR still deployed cleanly into WebLogic after the upgrade
The goal was not to exploit every Java 8 feature immediately. The goal was to move the platform onto a runtime that made the next steps practical.
That immediately widened the set of dependencies, tooling, and engineers we could reasonably bring to the programme. It also reduced the operational discomfort of keeping an ageing runtime in service longer than it should have been.
Incrementally upgrading Spring from 2 to 5
The Spring stack also needed a careful progression.
We did not jump from Spring 2 to Spring 5 in one blind step. We moved through compatible stages, resolving deprecations, configuration issues, and wiring problems as we went. Hibernate and several related libraries effectively came along for the ride because once Spring moves, other dependencies tend to reveal their own age very quickly.
This phase was less glamorous than containerisation, but it was essential.
By the end of it we had a codebase that was still recognisably the same application, still deployable to WebLogic, but no longer anchored to framework versions that made future change disproportionally expensive.
From an architecture perspective, this was the point where the application stopped being trapped by its own foundation. Framework decisions were no longer dictating the maximum pace of change.
Removing checked-in generated code from source control
One of the ugliest parts of the original build was the way SOAP-generated classes had been handled.
Instead of generating them cleanly as part of the build, the repository had effectively absorbed generated output as if it were hand-maintained source. That is exactly the kind of pattern that makes legacy repositories feel heavier over time.
So we reversed it.
We reintroduced JAXB and JAX-WS generation at build time and treated WSDL-derived classes as generated artefacts rather than permanent source assets. Gradle was a major help here because it gave us a structured place to define generation tasks, inputs, outputs, and dependency ordering.
Practically, that meant generated sources belonged under build output directories and were recreated from WSDL inputs during the build, rather than being versioned as if they were hand-crafted Java code.
That improved several things at once:
- generated classes were aligned with the service contract instead of drifting from it
- the repository became smaller and easier to review
- developers no longer had to guess whether generated code had been edited manually
- SOAP handling moved closer to a standard, repeatable approach
Fixing identity integration properly
At this point we also addressed a long-standing integration weakness around Oracle Identity Manager.
The original login and logout flow had the usual signs of a legacy integration that worked, but not in a way anybody wanted to defend. Once the Spring baseline had moved far enough, we were able to handle the identity side more cleanly and support SAML properly using stronger Spring security capabilities.
That gave the application:
- more reliable sign-in handling
- correct logout behaviour
- a clearer authentication model
- less custom glue code in a security-sensitive area
That mattered not only technically but operationally. Identity integrations often become one of the least-loved parts of a legacy estate, and one of the most difficult to change safely. The Spring upgrade unlocked a route to improve that without inventing a parallel security model from scratch.
This is a good example of why framework upgrades are not just technical vanity. Better platform choices often unlock safer behaviour in areas the business does care about, even when the visible screens have not changed.
Still WebLogic. Still the same application to users.
A critical point in Phase 2 was that we had not changed the application’s role in production.
It was still:
- an EAR deployed to WebLogic
- using classic servlet-era structures
- serving the same customer and order workflows
- behaving normally from the user’s point of view
That was deliberate.
The value of this phase was not that we had made the system look modern. It was that we had made the system more movable.
By the end of Phase 2 we had a platform that:
- was on Java 8 instead of Java 6
- had moved from Spring 2 towards Spring 5
- no longer depended on checked-in generated SOAP code
- handled SAML identity integration more credibly
- was still stable enough to continue supporting the live business
For the client, the key outcome was confidence. The service still behaved the same to end users, but the estate underneath it was substantially closer to modern frameworks, safer support, and lower long-term maintenance overhead.
That combination gave the team a much stronger base for the next step: designing for two runtime destinations at once.