views:

283

answers:

4

I have a couple of design/architectural questions that always come up in our shop. I said "our", as opposed to "me" personally. Some of the decisions were made and made when J2EE was first introduced so there are some bad design choices and some good.

  1. In a web environment, how do you work with filters. When should you use J2EE filters and when shouldn't you? Is it possible to have many filters, especially if you have too much logic in them. For example, there is a lot of logic in our authentication process. If you are this user, go to this site and if not go to another one. It is difficult to debug because one URL path could end up rendering different target pages.

  2. Property resource bundle files for replacement values in JSP files: It seems that the consensus in the Java community is to use bundle files that contain labels and titles for a jsp parsing. I can see the benefit if you are doing development with many different languages and switching the label values based on locale. But what if you aren't working with multiple languages? Should every piece of static text in a JSP file or other template file really have to be put into a property file. Once again, we run into issues with debugging where text may not show up due to misspelling with property value keys or corrupt property files. Also, we have a process where graphic designers will send us html templates and then we convert them to jsp. It seems it more confusing to then remove the static text, add a key, add the key/value in a property file, etc.

E.g. A labels.properties file may contain the Username: label. That gets replaced by some key and rendered to the user.

  1. Unit Testing for all J2EE development - we don't encourage unit testing. Some people do but I have never worked at shop that uses extensive unit testing. Once place did and then when crunch time hit, we stopped doing unit testing and then after a while the unit tests were useless and wouldn't ever compile. Most of the development I have done has been with servers, web application development, database connectivity. I see where unit testing can be cumbersome because you need an environment to unit test against. I think unit test manifestos encourage developers not to actually connect to external sources. But it seems like a major portion of the testing should be connecting to a database and running all of the code, not just a particular unit. So that is my question, for all types of development (like you see in CRUD oriented J2EE development) should we write unit tests in all cases? And if we don't write unit tests, what other developer testing mechanisms could we use?

Edited: Here are some good resources on some of these topics.

http://www.ibm.com/developerworks/java/library/j-diag1105.html

A: 

Filters are useful to help move logic such as is the user authenticated, to properly handle this, since you don't want this logic in every page.

Since you don't have a central controller it sounds like your filters are serving this function, which is fine, but, as you mentioned, it does make debugging harder.

This is where unit tests can come in handy, as you can test different situations, with each filter individually, then with all the filters in a chain, outside of your container, to ensure it works properly.

Unit testing does require discipline, but, if the rule is that nothing goes to QA without a unit test then it may help, and there are many tools to help generate tests so you just have to write the test. Before you debug, write or update the unit test, and show that the unit test is failing, so the problem is duplicated.

This will ensure that that error won't return, and that you fixed it, and you have updated a unit test.

For resource bundles. If you are certain you will never support another language, then as you refactor you can remove the need for the bundles, but, I think it is easier to make spelling/grammar corrections if the text is actually in one place.

James Black
"then as you refactor you can remove the need for the bundles, but, I think it is easier to make spelling/grammar corrections if the text is actually in one place."I agree, but at the same time, if you are looking for these types of errors. Let's say that QA notices a spelling error, the first thing you check is the html template file (jsp). If you have multiple bundle files, then you have to research which file is getting loaded. It just seems like more work.When I meant spelling errors, I also meant with misspelling the "key". Meaning the key won't be found from the property file.
Berlin Brown
You may need to clean up your bundles then, reduce how many you need, or make certain which one is loaded is obvious, perhaps a bundle/page.
James Black
A: 
  1. Filters in general are expected to perform smaller units of functionality and filter-chaining would be used to apply the filters as needed. In your case, maybe a refactoring can help to move out some of the logic to additional filters and the redirecting logic can be somewhat centralized through a controller to be easier to debug and understand.
  2. Resource bundles are necessary to maintain flexibility, but if you know absolutely that the site is going to be used in a single locale, then you might skip it. Maybe you can move some of the work in maintaining the bundles to the designers i.e let them have access to the resource bundles, so that you get the HTML with the keys in place.
  3. Unit testing is much easier to implement at the beginning of a project as opposed to building it into a existing product. For existing software, you may still implement unit tests for the new features. However, it requires a certain amount of insistence from team leads and the team needs to buy into the necessity of having unit tests. Code review for unit tests helps and a decision on what parts of the code need to be absolutely covered can help developers. Tools/plugins like Coverlipse can indicate the unit testing coverage, but they tend to look at every possible code path, some of which may be trivial.
    At one of my earlier projects, unit tests were just compulsory and unit tests would be automatically kicked off after each check-in. However, this was not Test-driven development, as the tests were mostly written after the small chunks of code were written. TDD can result in developers writing code to just work with the unit tests and as a result, developers can lose the big picture of the component they are developing.
Thimmayya
A: 

In a web environment, how do you work with filters. When should you use J2EE filters and when shouldn't you?

Filters are meant to steer/modify/intercept the actual requests/responses/sessions. For example: setting the request encoding, determining the logged-in user, wrapping/replacing the request or response, determining which servlet it should forward the request to, and so on.

To control the actual user input (parameters) and output (the results and the destination) and to execute actual business logic, you should use a servlet.

Property resource bundle files for replacement values in JSP files.

If you don't do i18n, just don't use them. But if you ever grow and the customer/users want i18n, then you'll be happy that you're already prepared. And not only that, it also simplifies the use of a CMS to edit the content by just using the java.util.Properties API.

Unit Testing for all J2EE development

JUnit can take care about it. You can also consider to "officially" do user tests only. Create several use cases and test it.

BalusC
+4  A: 
  1. Redirection is a simpler way to handle different pages depending on role. The filter could be used simply for authentication, to get the User object and any associated Roles into the session.

    As James Black said, if you had a central controller you could obviate the need to put this logic in the filters. To do this you'd map the central controller to all urls (or all non-static urls). Then the filter passes a User and Roles to the central controller which decides where to send the user. If the user tries to access a URL he doesn't have permission for, this controller can decide what to do about it.

    Most major MVC web frameworks follow this pattern, so just check them out for a better understanding of this.

  2. I agree with James here, too - you don't have to move everything there but it can make things simpler in the future. Personally, I think you often have to trade this one off in order to work efficiently with designers. I've often put the infrastructure and logic in to make it work but then littered my templates with static text while working with designers. Finally, went back and pulled all the static text out into the external files. Sure enough, found some spelling mistakes that way!

  3. Testing - this is the big one. In my experience, a highly disciplined test-first approach can eliminate 90% of the stress in developing these apps. But unit tests are not quite enough.

    I use three kinds of tests, as indicated by the Agile community:

    • acceptance/functional tests - customer defines these with each requirement and we don't ship til they all pass (look at FitNesse, Selenium, Mercury)
    • integration tests - ensure that the logic is correct and that issues don't come up across tiers or with realistic data (look at Cactus, DBUnit, Canoo WebTest)
    • unit tests - both defines the usage and expectations of a class and provides assurance that breaking changes will be caught quickly (look at JUnit, TestNG)

So you see that unit testing is really for the benefit of the developers... if there are five of us working on the project, not writing unit tests leads one of two things:

  • an explosion of necessary communication as developers try and figure out how to use (or how somebody broke) each other's classes
  • no communication and increased risk due to "silos" - areas where only one developer touches the code and in which the company is entirely reliant on that developer

Even if it's just me, it's too easy to forget why I put that little piece of special case logic in the class six months ago. Then I break my own code and have to figure out how... it's a big waste of time and does nothing to reduce my stress level! Also, if you force yourself to think through (and type) the test for each significant function in your class, and figure out how to isolate any external resources so you can pass in a mock version, your design improves immeasurably. So I tend to work test-first regardless.

Arguably the most useful, but least often done, is automated acceptance testing. This is what ensures that the developers have understood what the customer was asking for. Sometimes this is left to QA, and I think that's fine, but the ideal situation is one in which these are an integral part of the development process.

The way this works is: for each requirement the test plan is turned into a script which is added to the test suite. Then you watch it fail. Then you write code to make it pass. Thus, if a coder is working on changes and is ready to check in, they have to do a clean build and run all the acceptance tests. If any fail, fix before you can check in.

"Continuous integration" is simply the process of automating this step - when anyone checks code in, a separate server checks out the code and runs all the tests. If any are broken it spams the last developer to check in until they are fixed.

I once consulted with a team that had a single tester. This guy was working through the test plans manually, all day long. When a change took place, however minor, he would have to start over. I built them a spreadsheet indicating that there were over 16 million possible paths through just a single screen, and they ponied up the $10k for Mercury Test Director in a hurry! Now he makes spreadsheets and automates the test plans that use them, so they have pretty thorough regression testing without ever-increasing QA time demands.

Once you've begun automating tests at every layer of your app (especially if you work test-first) a remarkable thing happens. Worry disappears!

So, no, it's not necessary. But if you find yourself worrying about technical debt, about the big deployment this weekend, or about whether you're going to break things while trying to quickly change to meet the suddenly-urgent customer requirements, you may want to more deeply investigate test-first development.

Benjamin Cox
Good answers.And on testing. I think it depends on the software development culture at your particular shop. I really feel that unit testing is very useful. But how are you going to get 200 something developers to use it?For example, it is difficult to code J2EE without an IDE or a compiler, editor, the right tools. But, is it required to write unit tests before moving forward? Some would say, yes you can write the code.
Berlin Brown
@Berlin Brown - Require that any bug be duplicated in a unit test first, otherwise, how can they know that that bug was fixed. So, you start with a test at the top level, duplicate it, then work down, until you find the method that is the cause, but along the way you have created some tests.
James Black
"Require that any bug be duplicated in a unit test first, otherwise, how can they know that that bug was fixed."This is where I mention, complex J2EE environments. Normally, it isn't the Java code or the code that is the problem. But the database username/password was revoked. Or this query failed for some reason. Or the jms server was sending an invalid msg. How do you contend with multi-tiered environments in a unit test?
Berlin Brown
@Berlin - you can't. That's what functional and acceptance testing are for. There are certain things in a production environment that you can't automatically test, since by necessity automatic testing sets up a known state. What you can do for some of those things, though, is have sanity check routines at server launch time. Refuse to launch or "deploy the new code" (OSGI?) if these fail. That doesn't prevent bugs, just prevents them from finding their way to production. If you do enough integration testing, though, this tends not to happen often since it is clear what needs to be set up.
Benjamin Cox
However, if the issue is something you could reproduce in a functional test (for example, queries that go sideways), you should follow the pattern James described for functional testing, too. I typically try and catch things in acceptance tests first, functional tests next, and unit tests last. That's because unit tests are the most granular, so can catch things that the others can't. Also, your unit tests might all pass but your system still not work, as you pointed out in the complex J2EE case.
Benjamin Cox