Sunday April 15, 2007 Jeff Kunkle
I’ve noticed two topics generating a lot of buzz in the storage world lately.
- Hardware-Encrypted Disk Drives
- Solid State Disk Drives
I believe the first topic is a direct result of the disclosure of scores of high-profile data breaches resulting from stolen computers. The highest profile instance occurred in May of 2006 when a U.S. Department of Veteran’s Affairs laptop containing the names, social security numbers, dates of birth, phone numbers, and addresses of 26.5 million American veterans was stolen. Unauthorized information disclosure has unfortunately become an all too common occurrence in today’s world.
In response, vendors such as Seagate have introduced hardware-encrypted notebook hard drives to counter the unintended loss of sensitive information when a computer is stolen. The idea is that data stored on the hard drive is encrypted at all times, rendering the information practically useless to thieves. The encryption is done in specialized hardware to reduce the performance impact associated with the extra computation.
The second topic is the result of a continued increase in flash-based memory capacity to the point where it can now be considered as an alternative to traditional hard drives. Both Samsung and Sandisk have recently announced 64GB (Samsung) and 32GB (Sandisk) products targeted for laptop usage. Since there are no moving parts, flash-based memory consumes much less power than the spinning platters of a traditional hard disk, which is obviously a big benefit for laptop users. As a side benefit, these flash-based memory “drives” are rumored to be anywhere from 4-10x faster than a traditional hard drive, which brings me to the main point of this post.
How long will it be until we see solid-state “drives” such as the ones from Samsung and Sandisk coupled with dedicated hardware-based encryption as demonstrated on the new Seagate drive? A considerable downside to full-disk encryption of traditional drives is the performance impact. Since the new flash-based drives are several times faster, why not bake in full-drive encryption now when nobody will notice it. What you’d end up with are flash-based drives that provide full-disk encryption and still outperform traditional drives. Waiting to add full-disk encryption to later versions of flash-based drives will only introduce the performance debate that faces today’s traditional drives, making the purchasing decision all that much more difficult. Why not avoid the confusion and help customers better secure their data in the process?
Split is a very useful command-line utility for breaking large files into smaller pieces. Unfortunately, the version Apple ships with OSX 10.4 cannot split files into pieces 2GB or larger. Luckily, there is a newer version of split that you can compile and use instead of the Apple-supplied one.
There is a nice article/post over at macosxhints.com describing how to upgrade split. It's really not very hard but I thought I'd write a script so people could upgrade with a minimum of effort.
The short script is shown below. Please note that it is currently hard-coded for version 1.21 of split. You may want to check and see if there is a newer version before running the script.
#!/bin/bash
curl -o split.c "http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/usr.bin/split/split.c?rev=1.21&content-type=text/plain"
perl -p -i -e 's/#include <sys\/cdefs.h>/#include <sys\/stat.h>\n#include <sys\/cdefs.h>/' split.c
cc -Os -o split split.c
sudo mv /usr/bin/split /usr/bin/split.apple
sudo cp split /usr/bin/split
sudo chmod ugo-w /usr/bin/split
sudo chmod ugo+rx /usr/bin/split
sudo chown root:wheel /usr/bin/split
echo "Upgrade Complete."
Every once in a while I need to login to a hidden administrator account on my Mac. It's a simple process after you invest lots of time trying to remember the key combination needed to expose the User Name and Password prompt instead of the usual graphical user account login prompt (which doesn't show the hidden admin accounts of course). Many times I've searched all over the Internet to uncover the secret and never seem to find anything quickly, so I thought I'd document it here for future reference.
Let's assume that you're staring at the usual graphical login prompt. There's a nice little picture to the left of each of your user accounts, but no way to enter a username. If you only have one user account on your Mac, you're already being prompted for the password to that account. If this is the case, hit Esc.
Now you should only see a list of all the user accounts on your Mac, and should not see a password field. Press the down arrow key until you see a gray background behind one of your user accounts. It doesn't matter which one.
Next, press Option-Enter. Your normal graphical login screen should now be replaced with two text entry fields, one for your User Name and one for your Password. Now you can enter the username and password of your hidden administrator account (or any account for that matter). That's it.
Please note that I don't know if the Option-Enter key combination does anything if you don't have any hidden accounts. You'll have to try it for yourself and see.
A few days ago, some people in the office were debating the value of our time honored tradition of guarding debug log statements. If you're not sure what I'm talking about, a short code sample should clear things up quickly.
if (log.isDebugEnabled()) {
log.debug("Log message number " + 1);
}
It's generally accepted that guarding your log statements with an if block will improve performance if your log statements involve some kind of string concatenation, such as the example above. But what kind of performance improvement are we really talking about here?
To determine the impact of this log guarding, I wrote a simple program that repeatedly calls log statements with three different logging implementations. See attachment for the actual code.
- UnguardedLogger.java has no if block guarding the log statement at all.
- IfGuardedLogger.java has the traditional if block surrounding the log statement, just like the example above.
- AspectGuardedLogger.java acts just like the UnguardedLogger except that it has an AspectJ aspect guarding it's log statement. The idea here was to allow you to abandoned typing the if block guard while still enjoying its benefits.
All three implementations were asked to log one million statements in three successive batches. Each log statement included a string concatenation of six components. Each batch ran the three implementations in a different order. The results of the test run are shown below.
| Implementation | Test 1 | Test 2 | Test 3 |
|---|---|---|---|
| UnguardedLogger | 1341ms | 1783ms | 1345ms |
| IfGuardedLogger | 35ms | 32ms | 30ms |
| AspectGuardedLogger | 1343ms | 1612ms | 1364ms |
Broken down by average time per log statement, that's
- UnguardedLogger - 1.49µs/statement
- IfGuardedLogger - 0.03µs/statement
- AspectGuardedLogger - 1.44µs/statement
From these numbers, I've drawn two conclusions
- Guarding your log statements is a lot faster (in this case, 50x faster) than not guarding them at all or guarding them using an aspect. Even so, we're still down in the microsecond range here. If you're doing a lot of logging, you may notice a small improvement from guarding your log statements. Otherwise, you may not want to bother.
- Guarding your log statements with an around aspect is really no better than not guarding them at all. I'm not familiar with the internals of AspectJ, but my hunch is that even an around aspect will have to do the log statement string concatenation so that it could potentially be exposed as context for an advice. Hence, it ends up being about the same as not guarding the log statements at all.
To one extent or another, I‘ve been involved with many projects whose goal was to replace an existing system while adding some new features. On the surface, these sound like rather straightforward efforts. After all, the existing system is the source for all your legacy system replacement requirements, right? Unfortunately, these projects often struggle and many are never deployed. Why? Because they don‘t start with something new.
In my experience, legacy replacement projects first focus on supplanting the features of the old system rather than building new capabilities. In many cases, the legacy system was built over a period of years where new features were continually added and refined. However, new development efforts are rarely given years to write replacement software for the legacy system. The result is usually an initial version that doesn‘t even provide the same capabilities as the old application and adds little or nothing new. Why would a busy user spend their valuable time to learn a new system that provides less functionality than what they have today? In many cases they don‘t and the system is labeled a failure before it even gets rolling.
Why not start by building the new features first? Hook your users with new capabilities they don‘t have today and slowly replace the legacy features until the existing system is no longer needed or used. As a side benefit, your users may decide they don‘t need certain features of the legacy system anymore or that they should work differently based on their experiences with the new capabilities you‘ve given them. At the end of the day, I think you‘ll end up with a more successful application that better addresses users‘ current needs.
I recently decided to try out Hibernate's annotation-based validation framework. It seems like a great solution for ensuring consistent validation of the domain model regardless of the presentation tier (web interface, web service, etc.). It very much reminds me of the declarative validation available within Rails model objects.
To make a long story short, things did not go as well as I had hoped using a Hibernate Core 3.2.0 CR2 and Hibernate Annotations 3.2.0 CR1 combination. I struggled for hours trying to figure out why I was getting a "not-null property references a null or transient value" error rather than a validation error when attempting to use the NotNull validation annotation. Eventually I discovered the problem was not that the NotNull validation wasn't working properly, but that it wasn't running at all.
In order for the annotation-based validation framework to execute, Hibernate Annotations provides a ValidatePreInsertEventListener and ValidatePreUpdateEventListener to fire the annotation validations prior to persisting any changes to the domain model. "Pre-insert" and "pre-update" events are fired after other events, in this case SaveOrUpdateEvent(s). By default, Hibernate uses the DefaultSaveOrUpdateEventListener, which is an extension of AbstractSaveEventListener. The AbstractSaveEventListener checks for null values corresponding to non-nullable database columns ( AbstractSaveEventListener:284 ) before adding the insert or update events to the execution queue( AbstractSaveEventListener:290 ) responsible for triggering pre-insert or pre-update event listeners. In case that didn't make complete sense, the bottom line is the Hibernate Core checks entities against the null constraints of the database before the annotation-based validation framework has a chance to run.
Luckily, there is a workaround, although it feels like a bit of a hack. As I mentioned previously, the DefaultSaveOrUpdateEventListener gets a chance to run before any "pre-insert" or "pre-update" events so we can use that to our advantage. The following SaveOrUpdateEventListener simply extends the Hibernate Annotations-provided ValidateEventListener and calls the validation method on "save-update" events rather than "pre-insert" and "pre-update" events.
public class ValidationEventListener
extends ValidateEventListener
implements SaveOrUpdateEventListener {
public void onSaveOrUpdate(SaveOrUpdateEvent event) throws HibernateException {
final SessionImplementor source = event.getSession();
final Object object = event.getObject();
if (!source.getPersistenceContext().reassociateIfUninitializedProxy(object)) {
// only validate the entity if it has been changed
final Object entity =
source.getPersistenceContext().unproxyAndReassociate(object);
super.validate(entity, event.getSession().getEntityMode());
}
}
}
One extemely important thing to remember when using this workaround is that you must configure the DefaultSaveOrUpdateEventListener as the second "save-update" listener (following the ValidationEventListener) or your objects will never be persisted.
&JSP 2.0 introduced a handy new capability allowing you to use JSP Expressions directly within the template text (i.e. outside of tag libraries or tag files) of a web page. This allows developers to easily expose data and objects stored in application, session, request, or page scope using a simple Ant-style syntax. It allows you to replace this
<table>
<c:forEach var="book" items="${books}">
<tr>
<td><c:out value="${book.title}"/></td>
<td><c:out value="${book.author}"/></td>
<td><c:out value="${book.isbn}"/></td>
</tr>
</c:forEach>
</table>
with this
;code>
<table>
<c:forEach var="book" items="${books}">
<tr>
<td>${book.title}</td>
<td>${book.author}</td>
<td>${book.isbn}</td>
</tr>
</c:forEach>
</table>
As you can see, the syntax in the second example is much cleaner. But, “with great power comes great responsibility.” The chink in the armor that every tutorial I‘ve read (including Sun's Java EE 5 Tutorial) fails to mention is that the expression syntax does not escape HTML characters. Therefore, any web application using JSP Expressions to output unsanitized, user-entered data will be vulnerable to Cross Site Scripting (XSS) attacks.
Unfortunately, the cure for this XSS vulnerability leads us right back to our first example. As section 2.2.2 of the JSP 2.0 Specification reads, “In cases where escaping is desired (for example, to help prevent cross-site scripting attacks), the JSTL core tag c:out can be used.”
My advice is to use JSP Expressions only as tag library attribute values and stick to using JSTL‘s c:out tag for writing text to a page. Deciding which instances of template text expression usage are safe and which are not is simply too error prone and the consequences of a mistake too dangerous.

