Thursday, August 26, 2010

iBatis Mapping for One-to-Many Relationship

Perhaps you had a problem, like I did, understanding the iBatis writeup on loading complex collection properties, i.e. populating a list-based field in a domain object where that list is the many-side of a one-to-many relationship. I'll admit it took me a few iterations to totally grasp what was being said, so for my future reference and yours, I'll write down my notes here.

Monday, August 23, 2010

Monitor Changes to a File

There are undoubtedly many options out there for file-system change monitors - i.e. a component that will notify your Java object when a given file or directory has changed, among many others I'm sure: jnotify and jpathwatch, the latter of which is based on upcoming Java 7 NIO enhancements.

I gave jpathwatch a try recently, at a time when I was under a tight deadline and didn't want to reinvent a wheel. This presents a perfectly fine API that worked out quite well and quite quickly for me in my Windows environment, but alas when I deployed to Linux, I bumped into an unsatisfied link error. The problem was around libc.so.6 and GLIBC_2.4, and I gave it a reasonable first effort to try quickly finding the resolution - assuming I'd deployed incorrectly, or my Linux box was out of date, etc. It was neither of these - OK, in fairness, it could be an out-of-date Linux box, but my experiment was to just try it out on our customers' target system - and the same problem occurred. So out-of-date becomes a moot point.

As I mentioned, I was under a tight deadline, so I began some quick prototyping to see if I could reinvent something but without relying on native libraries (as jpathwatch did). That would give me the added advantage of a smaller runtime footprint, which was another customer requirement. Since we are really talking about an Observer pattern, here's how I started:

public interface FileChangeListener {

void fileModified(String file);

void fileDeleted(String file);

void fileCreated(String file);
}

That specifies the observer. Here's a simple monitor interface:

public interface AbstractFileChangeMonitor {

void watch(FileChangeListener listener, final String filename);
}

This one is a bit limited, since it supports just one listener (observer) for one file. But my goal is not (yet) to provide a full-featured framework - I just need to knock out the problem at hand without any gold-plating. I'm a firm believer in doing the least I have to for a given problem - first make it work, then make it fast, then extend it, ... etc., but only if subsequent steps are called for. In either event, here's an implementation of the monitor - this one polls the file in question to detect changes, running in a thread so the client process isn't blocked:

public class PollingFileChangeMonitor implements AbstractFileChangeMonitor
{
private final Logger logger = LoggerFactory.getLogger(getClass().getSimpleName());
private FileChangeListener listener;
private boolean done;
private Thread watch;
private int pollingInterval;

public PollingFileChangeMonitor(int interval) {
pollingInterval = interval;
}

public void watch(FileChangeListener theListener, final String filename)
{
if (watch == null) {
listener = theListener;
watch = new Thread() {
public void run() {
try {
init(filename);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
};
watch.start();
}
}

private void init(String filename) throws Exception
{
boolean exists = false;
long modTime = -1;
File file = new File(filename);
if (file.exists())
{
exists = true;
modTime = file.lastModified();
logger.info("====> File '" + filename + "' exists; change monitor is running...");
} else
{
logger.info("====> File '" + filename + "' does NOT exist; change monitor is running...");
}

while (!done) {
try {
watch.sleep(pollingInterval);
} catch (InterruptedException e) {
// ignore for now
}

if (!exists && file.exists()) {
exists = true;
logger.info("====> File '" + filename + "' has been created; notify listener...");
listener.fileCreated(filename);
} else if (exists && !file.exists()) {
exists = false;
logger.info("====> File '" + filename + "' has been deleted; notify listener...");
listener.fileDeleted(filename);
} else if (exists && file.exists()) {
long timestamp = file.lastModified();
if (timestamp > modTime) {
modTime = timestamp;
logger.info("====> File '" + filename + "' has been modified; notify listener...");
listener.fileModified(filename);
}
}
}
}
}

What the listener does when notified is not really important in the context of this post; it can be anything. While I've to a certain extent "reinvented" something here, I've gotten away from reliance on native code and the potential deployment headaches around that, I've reduced my runtime footprint, and for that matter I've solved the problem with about the same amount of code I needed for the boiler-plate suggested by jpathwatch.

Handling Partially Failed Transactions

Requirements for a web application I've developed include submitting a form that results in many database operations, including a mix-and-match of updates, inserts and deletes. Since there can be hundreds of such operations, my initial approach was to execute them in a single batch for a reasonable response time. Now, if what you want is fast execution and truly an all-or-nothing transactional behavior, using batch execution is a good idea. However, on iteration with my customers, the requirements evolved into something a bit more sophisticated: if any operations in the submitted set fails - e.g. attempting to insert a duplicate row with a unique index - we want all other operations to succeed, and we want to identify the operations that did not succeed.

What's I've described of course is not strictly "transactional" - i.e. the all-or-nothing constraint is not in play, but for the application's needs, this is perfectly acceptable. And since what I describe involves removing the batch execution, the overall set of operations will take longer to execute - but again, this is acceptable for the application's goals. You won't always find these trade-offs to be acceptable, but when you do, I'll describe one mechanism for making it work - my strategy includes the following:

1 - Execute the collection of operations in a single transaction, albeit without batch
2 - Catch and deal with exceptions around operations that fail
3 - Provide return values that includes lists of successes and failures

Here are some snippets that provides these things. I start with an enumeration of the operation types I care about:
    private enum OP {add, update, delete}
Next I provide a result object that can be examined by the client to identify successes and failures. I use generics for type-safety - the Key and Value types represent your application-specific database key and object classes:
public class OperationResult {
    private Map<Key, Value> successes = new HashMap();
    private Map<Key, Exception> failures = new HashMap();

    public OperationResult(Map<Key, Value> succeeded, Map<Key, Value> failed) {
        successes.putAll(succeeded != null? succeeded : new HashMap());
        failures.putAll(failed != null ? failed : new HashMap());
    }

    public Map<Key, Value> getSuccesses() {
        return successes;
    }

    public Map<Key, Exception> getFailures() {
        return failures;
    }
}
Finally I provide the workhorse method that executes all operations, capturing exception details in the face of any failures, and proceeding in either event so it can commit as many successes as possible. Due to the way this API is set up, clients can execute only one operation type at a time:
    private OperationResult execute(Map<Key, Value> map, String query, OP operation) {
        Map<Key, Value> successes = new HashMap();
        Map<Key, Exception> failures = new HashMap();
        try {
            try {
                sqlMapClient.startTransaction();
            } catch (SQLException e) {
                throw new IllegalStateException("None of the operations succeeded - txn could not be started",
                    e);
            }

            for (Map.Entry<Key, Value> entry : map.entrySet()) {
                try {
                    switch (operation) {
                        case add:
                            return sqlMapClient.add(query, entry.getValue()); break;
                        case update:
                            return sqlMapClient.update(query, entry.getValue()); break;
                        case delete:
                            return sqlMapClient.delete(query, entry.getKey()); break;
                    }
                    successes.put(entry.getKey(), entry.getValue());
                } catch (Exception e) {
                    failures.put(entry.getKey(), e);
                }
            }

            try {
                sqlMapClient.commitTransaction();
            } catch (SQLException e) {
                throw new IllegalStateException("None of the operations succeeded - txn could not be committed",
                    e);
            }
        } finally {
            try {
                sqlMapClient.endTransaction();
            } catch (SQLException e) {
                throw new IllegalStateException("Problem ending the txn - you should check whether the operations succeeded or not",
                    e);
            }

            return new OperationResult(successes, failures);
        }
    }
I'm using iBatis 2.x, as you might be able to guess from the SQL Map Client reference; it is assumed that the client passes in the correct query ID for the given operation.

One guideline from my Computer Science education that always stuck with me is "make it right first, then make it fast". The solution provided here is, as noted, not going to be as fast as batch execution - but it does meet the requirements correctly. If there is still a need to make it "faster", we could consider using an asynchronous API so that the response time from the user's point of view is improved. This type of approach is in fact used in many enterprise settings, leveraging so called BASE behavior instead of the traditional ACID approach.

Tuesday, August 10, 2010

Open a PDF Resource in Browser Tab

Though the title refers to a PDF, the MIME type of the resource is not crucial to this. My goal was to open a PDF file in a new browser tab, and with IceFaces I initially tried the outputResource widget. This seemed like the right way to go, and according to the TLD I should have been able to open the resource directly in the browser as opposed to simply downloading it - but I could not get that to work as expected.

Instead, I backed off to a more straightforward link approach, i.e. use of the outputLink control, as follows:
<ice:form>
    <ice:outputLink value="doc/some-PDF-file.pdf"
                    target="_blank">
        <ice:outputText value="Open PDF"/>
    </ice:outputLink>
</ice:form>
The plain vanilla HTML is dirt-simple:
<a target="_blank" href="doc/some-PDF-file.pdf">Open PDF</a>
This assumes the "doc" directory location is under your webapp root context.

SELECT Menu Doesn't Grey-Out When Disabled in Firefox 3.6.8

The title of this post pretty much says it all, just in case you've been Google'ing it to find some kind of reference to this. Since I found nothing, and since I solved my problem by manually changing the background-color on my SELECT item, I write it up here.

The problem does not occur in Firefox 3.5.11, and only occurs in 3.6.8 when you specify the SIZE attribute in a SELECT menu with MULTIPLE selectable items. Leave off the SIZE attribute, and the desired greying-out happens just fine, albeit with a 20-item window into your menu items. If that's not acceptable, you can use some JavaScript to convince the menu to grey out and then come back to normal as you disable and enable it. For example, given some HTML that defines some menu items, which should be disabled when a given checkbox is selected:
<input type="checkbox" name="items" onclick="enableDisable(this, formname)" .../>
....
<select id="menu" multiple size="4">
....
</select>
...here's the JS that can be used:
function enableDisable(input, formName) {
    if (input.checked) {
        disable(formName);
    } else {
        enable(formName);
    }
}
function disable(theForm) {   
    if (theForm.menu != null) {
        theForm.menu.disabled = true;
        // address the Firefox problem - grey it out manually:
        theForm.menu.style.backgroundColor="#DBDBDB";
    }
}
function enable(theForm)
{
    if (document.forms[theForm].menu != null) {
        document.forms[theForm].menu.disabled = false;
        // address the Firefox problem - reset to normal color, manually:
        document.forms[theForm].menu.style.backgroundColor="#FFFFFF";
    }
}
Disclaimer: As is typical with my code snippet posts, I've extracted the relevant statements from my production code, manually editing field names, method names, etc. to protect my client's IP. Since I've not taken the time to confirm the extracted code (I'm lazy, I'm too busy, it's left as a reader exercise, etc.), I can't guarantee that your copy-paste exercise will "just work" - but I do believe I've given you the basic building blocks to make it work for your application.

Get Full Filepath from IceFaces Webapp

Just a quick little snippet - if you need the "real path" for a resource residing within your webapp directory tree, here's how I've done it in IceFaces:
        FacesContext fc = FacesContext.getCurrentInstance();
        ExternalContext ec = fc.getExternalContext();
        ServletContext servletCtx = (ServletContext)ec.getContext();
        String filePath = servletCtx.getRealPath("/directory-under-webapp-root/somefile.pdf");
In principle the same idea applies regardless of IceFaces or any other web framework - you just need to get the ServletContext. That said, it appears that J2EE servers are not required to return anything beyond null from the getRealPath method.

My setting is with an exploded WAR - but if your webapp remains unexploded, here are some tips to get around that.