views:

214

answers:

6

I have this friend....

I have this friend who works on a java ee application (j2ee) application started in the early 2000's. Currently they add a feature here and there, but have a large codebase. Over the years the team has shrunk by 70%.

[Yes, the "i have this friend is". It's me, attempting to humorously inject teenage high-school counselor shame into the mix]

Java, Vintage 2002

The application uses EJB 2.1, struts 1.x, DAO's etc with straight jdbc calls (mixture of stored procedures and prepared statements). No ORM. For caching they use a mixture of OpenSymphony OSCache and a home-grown cache layer.

Over the last few years, they have spent effort to modernize the UI using ajax techniques and libraries. This largely involves javascript libaries (jquery, yui, etc).

Client Side

On the client side, the lack of upgrade path from struts1 to struts2 discouraged them from migrating to struts2. Other web frameworks became popular (wicket, spring , jsf). Struts2 was not the "clear winner". Migrating all the existing UI from Struts1 to Struts2/wicket/etc did not seem to present much marginal benefit at a very high cost. They did not want to have a patchwork of technologies-du-jour (subsystem X in Struts2, subsystem Y in Wicket, etc.) so developer write new features using Struts 1.

Server Side

On the server side, they looked into moving to ejb 3, but never had a big impetus. The developers are all comfortable with ejb-jar.xml, EJBHome, EJBRemote, that "ejb 2.1 as is" represented the path of least resistance.

One big complaint about the ejb environment: programmers still pretend "ejb server runs in separate jvm than servlet engine". No app server (jboss/weblogic) has ever enforced this separation. The team has never deployed the ejb server on a separate box then the app server.

The ear file contains multiple copies of the same jar file; one for the 'web layer' (foo.war/WEB-INF/lib) and one for the server side (foo.ear/). The app server only loads one jar. The duplications makes for ambiguity.

Caching

As for caching, they use several cache implementations: OpenSymphony cache and a homegrown cache. Jgroups provides clustering support

Now What?

The question: The team currently has spare cycles to to invest in modernizing the application? Where would the smart investor spend them?

The main criteria:

1) productivity gains. Specifically reducing the time to develope new subsystems features and reduced maintenance. 2) performance/scalability.

They do not care about fashion or techno-du-jour street cred.

What do you all recommend?

On the persistence side Switch everything (or new development only) to JPA/JPA2?
Straight hibernate? Wait for Java EE 6?

On the client/web-framework side: Migrate (some or all) to struts2? wicket? jsf/jsf2?

As for caching: terracotta? ehcache? coherence? stick with what they have? how best to take advantage of the huge heap sizes that the 64-bit jvms offer?

Thanks in advance.

A: 

If I were you, I'll start with measurements. Perhaps take a few typical operations, preferably the ones that are annoying users at high load or something, chop their path into some phases and measure how much time is consumed in each step. Once you find a step that is taking long, you can narrow down until you have a likely culprit. If it's DB access maybe caching would help etc. You might find some plain old optimization, but that's okay, too, right?

If you are running on older version of app. server, then I'd recommend upgrading it. It has generally better performance/scalability, and in case of JBoss it makes even more sense because of the better support (can find information/human resource easier).

As for EJB2.1 to EJB3 migration, I tested EJB3 dependency injection vs. EJB2.1 JNDI look up, and it was like much, much faster. Maybe getting rid of EJBHome stuff made things faster, too, though I don't know for sure. Once you migrate fully to EJB3, you'll have JPA and it is very easy to get ehcache to work, and I can tell you that ehcache is very efficient. (well I don't know how it compares to the one you are using now..)

Enno Shioji
A: 

I understand your friend's pain :)

However newer, shinier frameworks won't necessarily make your end user's life better, after all that's what you're paid for. Some issues have been around for a while, caching data to speed things up, auto wiring request parameters to a method call, managing transactions, seaming related components together ... Over the years people have developed 'good enough' implementations.

My feeling is that the framework switch is only worth the cost if it brings a lot of improvements. For instance, if switching to something else enables to AJAXify your application, then it might be worth it. But you will have to let the end users feel the improvement.

My advice would be to start decoupling your business model from anything else (DAO, UI, transaction management...), then it will be easier to introduce a new framework (ORM ?). Even if it doesn't work in the end, you would probably have improved your codebase's quality by decoupling.

Guillaume
+1  A: 

Honestly, you could probably just redo the entire thing in grails and do it like 20x faster than how long it took to do it in 2002 ;)

For projects that I absolutely have to use a real Java stack for, I stick with Spring 3.0 and Hibernate 3.5. You can even use Roo to get the project started quickly, and just turn it off if you don't want it anymore. Use an idea like IntelliJ to make coding quick.

Unfortunately, I gotta say this is not productive anymore. While it's great for raw Java... raw Java just sucks unless you absolutely need everything Java offers.

Also, Ajax is really bad with Spring. If you want to use database mapping in your MVC, you have to code against custom Jackson deserializers... so you gotta parse raw JSON. This is terrible, and the Spring people are already aware of the problem. Good luck seeing a high-level fix any time soon.

I used to laugh at the Ruby on Rails people... but I gotta hand it to them, they got it right. For small to medium projects, it really works. And if you want the scalability of the Java stack, Grails is a great solution.

If you really, really, really don't want to go in that direction, then just using a modernized Spring 3.0 with Hibernate 3.5 is the way the go.

egervari
I've researched grails (worked through one of the books) and found it very impressive: it looked a great candidate for a new application, but integrating with an existing app looked more difficult.Can you clarify about "ajax being bad with spring"? i.e. Spring-in-general or a specific component/aspect of Spring. I'm not familiar with Spring details nor its problems with json.thanks
+2  A: 

I was a in a very similar position to yourself a few years ago, with a complex, monolithic application consisting of a mix of EJB 1.x and a home-grown MVC framework. Migrating the whole thing at once was never an option, I needed a way of doing it a bit at a time, without breaking anything, and without requiring approval for a mega-budget project.

The tool that let me do this was Spring (v1.2 as it was at the time). When you strip away all the buzzwords from Spring, what you're left with is a simple framework for integrating disparate application components, making it easier to swap these components out for alternatives, modernising as you go.

For example, Spring gives you integration with Struts 1, making it easier to introduce Struts 1 components into the "Spring way". Your Struts apps should operate as before, but now they've got the leverage to get themselves modernised from the bottom up.

Next, Spring's data access abstractions will allow you to plug in your existing JDBC DAOs, and start introducing the DA abstractions to make them easier to modernise. If you choose to stick with JDBC, that's fine, Spring provides extensive JDBC support to make it feel less stone-age. If you want to tinker with JPA or Hibernate, then those will integrate with the Spring-managed application just as easily as JDBC will.

For EJB, Spring can wrap the EJB access layer into something a bit less prehistoric, making them easier to swallow. Once you've isolated the client layer from the specifics of EJB data access, you could, if you choose, replace the EJBs (one at a time) with simpler Spring-managed components (with any of remoting, transactions, security, or none of them), or you can retain EJB (using EJB3, perhaps) if you choose to.

In summary, let Spring take over the role of application "backbone", while starting off using the same legacy components you already have. You then have increased freedom to modernise, at the pace and risk you dictate.

It won't be easy, but with some patience and perseverance you can get to where you want to go without too much disruption.

skaffman
Thanks for the response. The 'gradual transition to spring' provides some helpful insight. As a non-spring user, I forget that Spring provides more than just a 'web framework'. I'm new to stackoverflow and not sure why I can't specify more than one answer for a question, but it was a tossup between yours and the other answer. Thanks for weighing in.
+1  A: 

Okay here is the answer that doesn't demand a rewrite in a different language or learning spring and writing 2000 lines of XML. Advise your friend to try deploying the application in glassfish v3, the EJB 2.1 will work (and play nice with any new EE 6 code).

Going forward they can now develop new code in EJB 3.1 and they can refactor old EJB 2.1 code at their leisure.

They could continue to use Struts or slowly migrate new code to JSF / Wicket / ???

Same goes for JPA, move to JPA 2 at their own pace although their domain model may require this occur in one big bang or many smaller bangs.

Caching? EclipseLink (JPA 2 RI) has some pretty decent caching on board. Read more

Most of all this pudding has proof. I just deployed a legacy EJB 3 + Struts 1.3 app in glassfish v3 at work not only was it dead simple (the bit of effort it took was I moved it out of a jdeveloper project and into a netbeans project) but its a great footing to begin refactoring to EE 6 which we intend to do as bugs or features are requested.

Basically this comes down to "If it ain't broke don't fix it." why change working (albeit very old) code? Let it live on, write new features in EE 6 and if a feature touches on old code, consider refactoring it to EE 6 while you're there.

Most of all, the effort involved in this, would be deploying into glassfish v3...

Justin
+3  A: 

It's really hard to justify re-engineering something that "works" as is. You spend a lot of work getting back to where you started.

That said.

The transition from EJB 2.1 Session Beans to EJB 3 is pretty trivial. For us, when we made the transition, most of our EJBs were deployed separately rather than in a combined EAR. But you don't have that problem. Even with EJB 3, you may very likely still have an ejb-jar.xml file (or files).

But, there's still benefit, I think, and the cost is very low. You can incrementally do it, bean by bean vs "all at once", which is nice, simply by moving the bulk of the information in the current ejb-jar.xml files in to the annotations within the application. If nothing else, it brings visibility (like transaction requirements, etc.) in to the code, and not "hidden away" in the ejb-jar.xml files.

There's no reason to deploy the "app tier" on to a separate jvm/server. Is the web tier calling Remote session beans? vs Local? You may or may not see a speed up by switching to local calls (many "co-located" deployments can be made similar to a local invocation on some servers if configured properly, dunno if you are doing that already).

The biggest risk of switching to local is that with a remote call, your arguments are "safe" from being changed, since they're serialized over the network. With local semantics, if you change the value of an argument, on purpose or not, (i.e. say, changing the value of a property in a bean), that change will be reflected in the caller. That may or may not be a problem. If they're already using the local call semantics, even for a "remote" bean, then they already have encountered this issue.

As for JPA vs SQL, I'd leave it as is. It's not worth redoing the entire data tier to swtich to JPA, and if you really wanted the benefits of JPA runtime (vs development time), notably caching etc., then you'd have to convert the ENTIRE data layer (or at least large chunks of inter-related parts) all at once. Really risky and error prone.

For the "duplicate jars" issue, that's an artifact of packaging and build, not deployment. To fix the ambiguity issue, you need to work on your development environment to use a shared jar repository, and be cognisant of the fact that if you upgrade the jar for one, you'll upgrade it for all. People decry that that is an unreasonable demand, forcing the entire application to upgrade if a jar changes. For enormous, disparate apps, sure. But for apps in a single JVM, no, it's not. As much as we'd like every little bit to be an isolated world in the teeming soup we call a Java classloader environment, it's simply not true. And the more we can keep that simplified, the better off we in terms of complexity and maintenance. For common jars, you MIGHT consider bundling those jars in to the app server and out of the application. I'm not fond of that approach, but it has it's uses if you can make it work for you. It certainly reduces the deployment size.

Client side, it's not that hard to convert from Struts 1 to Struts 2, as they both very similar at the high level (notably, they're both action frameworks). The key here is that both frameworks can live side by side with each other allowing, again, incremental change. You can slowly migrate old code over, or you can solely implement new code in the new framework. This is different from trying to mix and match an action framework and a component framework. That's literally a "dogs and cats, living together" situation. If I were to go that route, I'd simply deploy the component stuff in their own WAR and move on. The state management of component frameworks makes interoperating with them on the back end really troublesome. If you choose to implement via a new WAR, make sure you spend a little time doing some kind of "Single Sign On" so folks are "logged in" to each module as appropriate. As long as the apps don't share any session state, this is as far as the integration really needs to go. And once you've chosen to add a new subsystem via a new WAR, you can use any tech you want for the client side.

Caching is a different issue. The different caches solve different problems. It's one thing to cache and memoize some little bits within the system (like JSP renderings), or to use a distributed cache to transfer sessions across instance during failover or load balancing. It's quite another to have a cache based domain layer where the persistence and caching are very, very tightly integrated. That's far more complex. Just keeping it all straight in your head is painful.

The former you can pretty much sprinkle willy nilly across the application as you encounter a needs, and those kinds of caches can be pretty much stand alone rather than part of a coordinated, overarching caching framework.

The latter, is a different. There you need to pretty much redo your entire data model, even for parts that you're not caching at all, as you want to ensure that you have consistent access to the data and it's cache views.

This is effectively what JPA does, with its two levels of caching, and why I mentioned earlier it's not something you can casually slip in to an application, save for mostly stand alone chunks of your system. When you have distinct modules hitting the same backend resources, cache coherence and consistency becomes a real issue, and that's why you want those integrated on both systems.

Mind, it can be done. The trick is simply integrating the data access level, and then you can start caching at that level. But if you have folks making direct SQL calls, those have to go.

Finally, I think the term to use is evolution, not revolution. Migrating to EJB 3 or 3.1 I don't think has to be painful, as it pretty much Just Works with EJB 2.1, which is a boon. You CAN have a "mixed" environment. The most painful integration would have been if you used Entity beans, but you didn't, so that's good. And for all of the EJB naysayers, this backward compatibility that spans across, what, almost 10 years of EJB, is what lets you actually keep a bulk of your code yet still move forward.

Will Hartung
Thanks for the helpful response. Your and the "wade into the spring" post complemented each other and addressed our realms-of-concern.As for 'remote' vs. 'local' I was a bit unclear. In practice all calls are local --because Jboss treats ejb calls in the same vm as local. I do not foresee ever putting ejb's on separate vm's as the web tier. We'll look at our 'duplicate jar' issue and deployment.AFAIK and IIRC the earlier versions were vague on .war jars and .ear jars (Jboss had its 'flatclassloader' model which treated every jar in the ear/war as equals. But that's another topic)
Yea, Glassfish also has the "one classloader per EAR" model, with the "one per WAR" model being an option. This was a real issue when I tried to bundle two WARs in the same EAR. The EJB spec does not define the behavior one way or the other, so both models are "compliant", but I consider this a hole as the applications built under the two different environments aren't really interchangable, even if both are coded "to spec".
Will Hartung
+1 since this answer has only received 1 other upvote at this time.
Matt Ball