DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
View Events Video Library
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Integrating PostgreSQL Databases with ANF: Join this workshop to learn how to create a PostgreSQL server using Instaclustr’s managed service

Mobile Database Essentials: Assess data needs, storage requirements, and more when leveraging databases for cloud and edge applications.

Monitoring and Observability for LLMs: Datadog and Google Cloud discuss how to achieve optimal AI model performance.

Automated Testing: The latest on architecture, TDD, and the benefits of AI and low-code tools.

Related

  • Visually Designing Views for Java Web Apps
  • Migrating Spring Java Applications to Azure App Service (Part 1: DataSources and Credentials)
  • Aggregating REST APIs Calls Using Apache Camel
  • Use Ketch to Deploy Apps on Kubernetes Without YAML

Trending

  • Microservices With Apache Camel and Quarkus (Part 5)
  • Getting Started With Prometheus Workshop: Instrumenting Applications
  • Deploy a Session Recording Solution Using Ansible and Audit Your Bastion Host
  • Java Parallel GC Tuning
  1. DZone
  2. Coding
  3. Languages
  4. Legacy Java Applications: Strangulation inside Tomcat

Legacy Java Applications: Strangulation inside Tomcat

Paul Hammant user avatar by
Paul Hammant
·
Jul. 18, 13 · Interview
Like (0)
Save
Tweet
Share
7.54K Views

Join the DZone community and get the full member experience.

Join For Free

legacy java servlet apps

since its launch in 1997, ‘java servlets’ was a mainstream choice for enterprise application development. sun quickly added jsp, tag libraries and jsf. the open source community made many web frameworks to work. struts 1.0 was the first web framework to hit big in 2000, and maybe the earliest java web-framework you will still encounter in the enterprise today. if it is still around in your deployed applications, it is definitely legacy as the last release was in 2008, and it was formally end of life in april of this year. someone is always going to say rewrite, and sometimes rewrites pay off.

contributions to problems for large enterprise systems:

classic singletons versus dependency injection

while it was known in smaller circles that solutions leaning on singletons (the design pattern not the spring etc idiom) would slowly become unmaintainable, the vast majority of professional developers in 2000 did not at that time know it. dependency injection hit it big in 2004 with martin’s blog entry, and in 2006 spring framework’s xml way of decomposing dependency injection web applications became the main way. in itself, that had problems, but let us mark 2006 as the turning point for the majority.

since then a debate continues as to whether a dependency injection container (i co-created one too – picocontainer) is needed, or whether the semantics of injectable classes/components are enough on their own. also since then graduates arrive in the programming field who have not heard of dependency injection (or its encompassing principal – inversion of control), resetting the clock incrementally. there’s also the rise of dynamic languages (python, ruby) and their non-container reality (though the class idioms of di still apply according to alex martelli and jim weirich ).

other causes

the “static state” problems of singletons aside, java servlet applications in modern age can also scarred by:

  • too many servlet filters – each new team puts one in front of all others
  • over use of threadlocal to perform complex operations
  • a service locator in the mix, but with no plans to move further towards it, or away from it
  • second/third component frameworks in the mix. say the team started with struts1, then put wicket in too (for different pages)

what is strangulation again?

remember, strangulation means safe incremental migration from bad to good, while simultaneously adding new functionality, and putting a part old & part new application live iteratively.

it is different to a plain rejuvenation of a legacy codebase, in that you’ve determined that the end goals of rejuvenation are not attainable, or the starting point is too far removed.

see my previous blog entries: legacy app rejuvenation , a singleton escape plan as well as yesterday’s legacy application strangulation : case studies , and martin’s definitive article: stranglerapplication

your new problem

after you’ve decided to strangle rather than rewrite, your new problem is that you don’t really want to add any more code to the existing war-file application because it’s a house of cards, and could collapse with any subtle coding mistake. it’s brittle and perhaps there are few unit tests protecting it.

web app strangulation generally

as implemented hundreds of times in the industry, web-app strangulation involves having a proxy run in front of the old and the new application, and via mappings route incoming http requests to either the old or new application. over a period of time you’d add more code (and functionality) to the new app, and take more away from the old one, with corresponding changes in the routing proxy. that proxy could be the big-iron programmable load balancer like f5. it could also be an open source piece like haproxy. those two are the high-scale ways of doing it. closer to the application code, you could be just programming routing in an apache layer.

the pure-java solution

this is not necessarily better, but if you are living by other deployment stack and scaling considerations, coding this safely inside the servlet container (tomcat, jetty, etc) is possible. that’s what i’m going to outline.

a second war file.

war files are the drop-in self-contained deployment archive for servlet containers. by convention one called root.war will deploy without a context, and ones named in any other way lend their file name to the context in question. at least by default that’s true for most web-app containers.

for this solution, we’re going to have a new war file application that’s going to occupy the old one’s place in the directory structure for the site in question. if the old app were found at example.com/* , after implementation the new app will be instead. what was the old app will be moved away from that root context. that’s example.com/oldapp/* in this example.

the initial version of this delegate all http requests to the equivalent url on the old application. that’s as simple as prepending oldapp/ to the path and a executing a few lines of servlet magic (see below for how). one by one, end-points are rewritten in the new war file, and obsoleted in the old one. it could be that code migrates too (as it’s rejuvenated) but whether that happens or not, you should have a high level of tests according to the test pyramid to guarantee quality. of those, the functional tests can step into the old application and back again if it makes sense.

the old app also needs a new servlet-filter front-running it to prevent the end-points being visible from the end-usertcp/ip addresses. you may still want the end-points available internally for debugging purposes.

one war-file invoking endpoints in another

if you have two web applications ‘a’ and ‘b’ (a.war & b.war), servlets or filters in ‘a’ can invoke an end-point in ‘b’ like so:

httpservletresponsebuffer tmpresponse = new httpservletresponsebuffer();
tmpresponse.copyafewthingsfromtheupstreamresponse(response);
httpservletrequest servletrequest = (httpservletrequest) request;
servletcontext bcontext = servletrequest.getsession().getservletcontext().getcontext("/b");
requestdispatcher endpoint = context.getrequestdispatcher("/astaticordynamicresourceorendpoint");
myhttpservletresponse newresponse = new myhttpservletresponse((httpservletresponse) response);
endpoint.include(request, tmpresponse); // or .forward(..)
// unpack payload from tmpresponse, and do things to your stored response.

the complexity is that you can only get back a stream of characters/bytes from ‘b’. you’ll have to process those streams as if they’d been acquired by tcp/ip. that includes parsing if you’re talking about json or xml. that is the deliberate design of the servlet container, in that servlet apps (different war files) are in different class-loaders so that they can’t interfere with each other programmatically – the same classic singleton in two war files is actually two totally separate instances. here’s a view of the classloaders (red lines show parent and grandparent classloaders):

they can still interfere with each other in terms of cpu availability as the jvm is not able to protect one web-app from another’s cpu or socket busyness. that’s sun’s implementation shortcoming, that oracle can maybe remedy in the future.

there’s some extra magic required in to allow ‘a’ to invoke ‘b’, that’s not allowed by default. at least for tomcat, context.xml needs to have an attribute set crosscontext="true" . here is a perl one-liner for that, as that’s outside the war file:

perl -p -i.bak -e 's/<context>/<context crosscontext=\"true\">/g' /path/to/tomcat_install/conf/context.xml

threading

as at least tomcat and jetty have implemented it, no new threads are used when ‘a’ invokes something in ‘b’. this is important as you otherwise worry about a drop in capacity for the part strangled “application” as you put it to production. if the containers were to silently use new thread during include(..) or forward(..) , you would see capacity approximately halve assuming you were more or less fully utilized for the machine (or virtual machine).

shared aspects.

there are lots of things that come down from the browser with a http request. the url is an obvious one, as is the payload of the request (posts have post-fields). there’s also cookies, and some may be pertinent to your new app, with others for the old app. you could choose to separate those if it matters, or let both applications see them.

there’s also a jsessionid for java servlet applications, used to identify who a request is on behalf of. sharing that for two apps (old and new) serving the same end-user request is perfectly ok, and like the threading section above means a more streamlined strangulation.

further reading.

sitemesh has been using context.getrequestdispatcher(..).forward(..) for 15 years. a legendary framework from legendary developers (joe walnes, mike cannon-brookes). here’s the critical class . you will want to copy sections of this codebase (particularly the tricks they are doing to ‘request’ and ‘response’) if you are going in the direction i outline.

i’ve made an example of an ‘a’ and ‘b’ web-app scenario. it is on github: github.com/paul-hammant/servletdispatchertest and there are instructions there as to how to deploy it yourself for a tryout. there are servlets/filters and static resources. shown is accessing those things directly, for via in implicit getcontext('/b').getrequestdispatcher(..).include(..) route. i’ve cheated and used mime types of “text/plain” to make the code smaller, but it would be the same for html, xml, gifs, json, etc. it also goes out of its way to show that one-thread is used for all of a request.

thanks to

joe walnes (of sitemesh fame) and mark thomas (tomcat mail-list)


application Java (programming language) app Dependency injection Apache Tomcat Web apps Open source unit test Spring Framework

Published at DZone with permission of Paul Hammant, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Visually Designing Views for Java Web Apps
  • Migrating Spring Java Applications to Azure App Service (Part 1: DataSources and Credentials)
  • Aggregating REST APIs Calls Using Apache Camel
  • Use Ketch to Deploy Apps on Kubernetes Without YAML

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: