Wednesday, February 27, 2008

Acegi and Struts 2

The easy part is done. Acegi's jar files are in place and they reliably block access to the "protected" part of the application, and redirect users to the login page. Unfortunately I haven't yet figured out how to connect my login action to Acegi authentication.

I believe I will have to have my login action point at my own custom implementation of UserDetailsService, which will in turn have a link to my already-existing UserDao. It will have to put the UserDetails in question into the security context... I think I got it. Now just to, y'know, write the code.
First real step was to tell web.xml that I was going to have multiple applicationContext files (applicationContext.xml and securityContext.xml) so that I don't poop all over my application context with security stuff. Yeesh, there's a lot of configuration there.

Acegi Security

Today I am checking out Acegi Security for our app. I'm slapping it against the application now, early on, rather than trying to shoehorn it in later, after everything works in an insecure fashion.

I would like to know how the hell it's supposed to be pronounced.

It certainly looks cool. Pretty pluggable. Should fit reasonably well within our current application design; I'm just going to write a UserDetailsService implementation that uses our existing Hibernate configuration.

More as I actually do it.

Friday, February 22, 2008

Incredibly Basic Design Problem

I'm working on a web application. All noteworthy data in here has been scrubbed appropriately.

This application has users. There are both external and internal users. Each user may have access to a certain subset of the application's functionality, although generally speaking internal users' functionality access is different from external users' functionality access. For instance, we might have an external user who is a customer, and another external user who is a supplier. We might have an internal user who is a sales rep, and another who provides support to the logistics team.

The question I immediately ask myself is this: Am I looking at an inheritance hierarchy, or am I looking at an opportunity for composition?

In other words, do I have:

public abstract class User {...}
public abstract class InternalUser extends User {...}
public class LogisticsUser extends InternalUser {...}

?

Or should I rely instead on composition?

public class User {
Set availableRoles = new HashSet();
}
User fred = new User();
fred.addRole(LOGISTICS);

I might consider answering this question with a look at the database tables that I'm relying on, but (1) I'm more concerned with the conceptual problem here than the implementation details and (2) in this particular case the tables are no help at all anyway.

So here is the core question: Can a user ever fill multiple roles? Java doesn't support multiple concrete inheritance, so a given user either IS a LogisticsUser or he IS a CafeteriaUser or what-have-you. If users can fill multiple roles, then I should be assigning them roles rather than slotting them in a particular concrete class.

And in this particular case, yes, some users do need to fill multiple roles (for instance, we might have a logistics support person who also has administrative authority over some of the website). So I'll be making a simple User hierarchy with a more complex collection of Roles available.

Wednesday, February 20, 2008

Today I Am A Real Boy

Today a client coworker gave me some Java code of his to work on and finish up, because we're going to need it for forward development.

So I looked at what stuff could be refactored, wrote a couple of unit tests, refactored some bits out, ran the tests, and...

Well the code logged a few major exceptions and was unusable. But the test passed.

We were conversing about this code in general terms a few minutes later, and I pointed at the screen and hollered "I want that test to fail! Help me make it fail!"

Saturday, February 16, 2008

Dippy

To go with "ATM machine" we now have "plain POJO," per the Hibernate docs.

Thursday, February 14, 2008

Glad to see this works

I wasn't sure this would work. One quick unit test verifies that it does. Very good to know:

Query query = getEntityManager().createQuery("from User order by ?");
query.setParameter(1, "lastName");


I don't know why it wouldn't work, really, but I had only used replaceable parameters in the WHERE clause of queries before.

Thought I was GONE didn't you

I've played a bit over the last few days with a simple Struts 2 CRUD app, mostly following the examples given here.

Right now I have a data table that lists the users of the app and provides an exciting 'edit' link for each one. The edit link takes you to a pretty standard 'edit' type page for the given user.

Here's what I'd like to do next (several things, all fairly independent of the others).
  • Turn the data table into a submit-driven paging sorting data grid.
  • Turn the data table into an AJAX-driven paging sorting data grid.
  • Implement a smart(ish) filter for the data grid.
  • Change the two-page layout (table, data entry grid) into a one-page AJAX-driven layout (click on a user to open the edit grid for that user next to the table).
I think I'm going to start with the first one; I'm going to have to do it eventually anyway.

Thursday, February 7, 2008

Another thing that drives me berserk about ColdFusion and CFEclipse

It is damned near impossible without using the "find" feature and extraordinarily tedious tag-counting (and tag-collapsing) to figure out whether or not a given variable is in scope at a particular part of the page. I can't control-click to go to the spot where the variable is declared; I get no indication as to whether the variable exists at a given spot; I don't even get a warning telling me that the variable may not have been declared conditionally at a given spot.

And then I get exceptions thrown when my predecessor's code references a variable that was conditionally declared earlier and the declaration got skipped this time. Madness.

Wednesday, February 6, 2008

Wrong Ways, first in a series

After a few days spent doing ColdFusion support I'm doing some more Struts/Hibernate development today.

An observation: There's a right way to set up developers' environments, and there are dozens of wrong ways. I feel like I have a pretty good grasp of some of the wrong ways, and now I'd like to share them with you. Critically important note: These observations do not pertain to my current client or my employer.

Wrong Way #1: Stint on hardware. A consultant costs, let's say, $100/hr. A fully-loaded full-time employee might be $70/hr. According to newegg, 4 GB of RAM from a no-name vendor costs $100. If you want to go with a really reliable, trusted vendor, you might spend $200. If you are furnishing a consultant with hardware, a really great Wrong Way to do things would be to save $200 on memory. Give him a machine with 1 GB or less. Sure, it takes 15 minutes for his machine to boot every day, and lockups, crashes, and slow response time cost at least an hour a day. But you saved the cost of that memory!

(but seriously) This is especially and offensively common when different cost centers or different managers are paying for developers' time versus developers' hardware. The MIS guys saved $150 off their budget but cost the company an additional $6000 in developer productivity over three months. Woo! Way to contribute! Go team!

Wrong Way #2: Lock down that Internet. Developers can't be trusted with free reign to go to any website they want. Network administrators know what kind of tools, open-source libraries, and reference materials a developer could want. Let the network guys and their management make that determination. Blogs? Forget it! Those things are just time sinks, they're like reading the newspaper at work. And there's no reason to differentiate between a customer service rep and a software developer in terms of Internet access. If you can't trust a CSR with access to blogspot.com, there's no way a developer needs it. Lock 'em all down.

Wrong Way #3: Shared environments. Software licensing is expensive. Rather than spring for a development license for each developer, why not set up a shared dev server, with a shared database and everything? Don't give developers local development environments; just make their desktops into glorified terminals. Also, this way your developers won't have to worry about solving problems when they're away from an Internet connection or when the VPN is down. That will encourage them to spend more time in the office.

Oh, I bet there are plenty more of these I could do...

Monday, February 4, 2008

Excellent series on developers' database management and putting your db in version control over at http://odetocode.com/:

Part 1
Part 2
Part 3
Part 4
Part 5

Friday, February 1, 2008

Well that was funny

I only had to change the URLs in hibernate.cfg.xml and in applicationContext.xml. That's good and easy.

Things didn't work for a sec, because in the 'real' database that I'm now using, Users aren't stored in a 'user' table, they are stored in, let's say, a table called 'bogus_user'. With Hibernate 2 I would have known to go into User.hbm.xml and modify the table name reference, but the current quickstart project uses Hibernate 3 annotations. I checked the docs quickly, suspecting I need to change the @Entity annotation on the User class. The docs pointed me at the free Chapter 2 sample of the Manning Java Persistence with Hibernate book. It showed that I needed to add @Table(name="bogus_user") to the User class declaration. I did that, and redeployed.

This time I ran into a hilarious error. The bogus_user table has thousands of entries, and my simpleminded script was trying to display all of them at once. So -- next step -- limit the number of entries returned. That will be after lunch, as my wife and I are going to meet for lunch for the first time in ages.

I'll probably be buying that Manning book this weekend.

Changing a data source

It's very quiet here this morning. Snow and ice last night, freezing rain this morning. A lot of people are working from home today.

I'm going to try something wacky this morning. Having successfully finished the 'quickstart' demo provided by the fine Struts 2 folks, I've already changed the names of my packages and classes to match the activities I actually plan to undertake (quickstart.service.PersonServiceImpl changes to com.clientname.project.UserDaoImpl, for instance).

Now I'm going to do something more entertaining. I'm going to try to dump a copy of our dev MySQL database and build it locally, and then point this toy app at it. I may not have the right privs for that. So if that doesn't work, I'm going to point my app at our dev database and see what happens. I'm pretty sure I know what will have to change.