Showing posts with label software. Show all posts
Showing posts with label software. Show all posts

Friday, January 13, 2017

Feeling safer online with Firefox

The latest privacy and security improvements in Firefox

 

[This post originally appeared on Medium]

Firefox is the only browser that answers only to you, our users; so all of us who work on Firefox spend a lot of effort making your browsing experience more private and secure. We update Firefox every 6 weeks, and every new change ships to you as fast as we can make and verify it. For a few releases now, we have been landing bits and pieces of a broader set of privacy and security changes. This post will outline the big picture of all these changes.

Site Identity and Permissions Panel

The main project involved improvements to the way Firefox handles permission requests from sites that want to do things that the web platform doesn't allow by default - like accessing your laptop's camera or GPS sensor. To find out how our existing model fares, we ran it through a number of user studies and gathered feedback from users and web developers alike.

old-prompt.png

What we found was clear: users were having trouble making full use of web permissions. Here are some of the observations:
  • It’s easy by design to dismiss a permission prompt, to prevent websites from nagging you. But it’s not so obvious how to get back to an inadvertently dismissed prompt, which users found confusing.
  • Managing the permissions of an individual site was hard, due to the multitude of presented options.
  • It was cumbersome to grant access to screen sharing. This was because it was difficult to select which area of the screen would be shared and because screen sharing was only permitted on websites included in a manually curated list.
In order for the open web platform to be on par with the capabilities of native, closed platforms, we needed to fix these issues. So we first focused on putting all privacy and security related information in one place. We call it the Site Identity and Permissions Panel, or more affectionately, the Control Center™.

control-center.png

The Site Identity panel appears when you click on the circled “i” icon – “i” for “information” – on the left side of the Awesome Bar. The panel is designed to be the one-stop shop for all security and privacy information specific to the site you’re on. This includes encrypted connections certificate, mixed content warning messages, tracking protection status, as well as non-default permissions. We were happy to see Chrome adopt a similar UI, too.

Elevated Privileges for Non-Default Permissions

By default, web sites need an elevated level of privilege to access your computer hardware like camera, microphone, GPS or other sensors. When a site requests such a permission and the user grants it, the Site Identity panel will display the allowed item along with an "x" button to revert it. In the case of particularly privacy-sensitive permissions, like microphone or camera access, the icon will have a red hue and a gentle animation to draw attention.


When a site has been granted elevated privileges, the "i" icon in the URL bar is badged with a dot that indicates the additional information present in the Site Identity panel. This lets you assess the security properties of your current session with a quick glance at the awesomebar, where the "i" and lock icons are displayed together.



Users who want even more fine-grained control over all available permissions can go to the Permissions tab in the Page Info dialog (right arrow in the Identity panel -> More Information).

Permission Prompt and Dialog

Permission dialogs are now more consistent than before, both in terms of available actions and messaging.

When a site asks for a permission, a permission prompt appears with a message and iconography specific to the type of permission being requested and the available actions. Most of the time, there will only be two: allow or don’t allow access. The default action will stand out in a blue highlight, making the common action easier to perform.


In the few cases of permission prompts with more than two actions, a drop-down menu will appear next to the secondary action button.


Permanently allowing or rejecting a permission for a site is done by checking the always present "Remember this decision" option.



We have received a lot of feedback about how these prompts are easy to dismiss and how users often couldn't figure out how to bring them back. In the new design, permission prompts stay up even when you interact with the page. You can of course ignore it and continue to use the page normally. But thanks to the persistence of the prompt, it’s now easier to associate site misbehavior – webcams that don’t work, locations that won’t display – with an allow/don’t allow button that needs your response.

Furthermore, disallowed permission requests are now displayed as strikethrough icons in the Awesome Bar to hint at the potential cause of site breakage. For example a video conferencing site will probably not be functioning very well if you reject its camera permission request. So the crossed-out camera icon will remain afterwards, next to the "i" icon, to remind you of that fact.


Going to a different tab will hide the prompt (because it’s specific to the site you have open on each tab), but when the prior tab is selected again, the prompt will reappear.


Audio, Video and Screen Sharing Permissions

WebRTC-related permissions have even more new changes.

For starters, screen sharing now doesn't require sites to be added to a separate whitelist. This means that all sites can now use WebRTC screen sharing in Firefox.

Also, screen sharing now includes a preview of the content that will be shared to make it easier to identify the right screen, application or window to share.


In the riskiest of cases, such as sharing the entire screen or sharing the Firefox application, a scary warning message is displayed to ensure you know what you are about to do.

screen-sharing.png

Moreover, when you have granted a video conferencing site access to both your camera and microphone, reverting the permission grant for one permission will also revert it for the other. This will help you avoid accidentally leaking your private data.

Add-on Panel Improvements

While working on these security improvements we fixed some old platform panel bugs that used to affect all kinds of panels, including those created by add-ons. Therefore if you are using an add-on that displays popup panels you should have an improved experience even if the panels are not related to permission prompts.

Error Pages

And finally, error pages also received some new smarts.

The most common cause for secure connection errors turns out to be user systems having the wrong time. Firefox will now detect when your clock seems way off and will suggest in the error message how to fix it.



Another common cause for broken connections is the presence of a captive portal. Firefox will now detect that case and prompt you to log in the captive portal. Even though some operating systems have built-in support for detecting captive portals, if you regularly use social network accounts to log in, the experience with Firefox will be smoother. This change is now in Nightly and Developer Edition versions and should ship soon in the stable release.


Looking back at what we managed to accomplish in the last few months makes me proud to work with this fabulous team of talented and passionate engineers, designers, user researchers, QA engineers, product and project managers. But of course we are far from being done with privacy and security improvements for our users. Stay tuned for more exciting Firefox privacy and security updates in 2017!

[Many thanks to Bram Pitoyo, Nihanth Subramanya, Tanvi Vyas, Peter Dolanjski, Florian Quèze, and Johann Hofmann for reviewing drafts of this post.]

Monday, February 13, 2012

Debugging JavaScript

tl;dr: Firefox nightlies now ship with an experimental JavaScript debugger. It's not ready for end-users yet, but we are feverishly working on filling in the missing bits.

Debugging is always hard, especially when debugging other people's code. Adding a bunch of console.log() statements, becomes tedious after a while and that's assuming that you are familiar enough with the code in question to know where to place them. Tools like Firebug are a tremendous help in this case. Having the ability to pause the execution at any point or inspect variables and stack frames, provides valuable insights into the runtime behavior of a program.

Firebug depends on the traditional JSD API in SpiderMonkey to perform its magic, but nowadays JSD is limiting in a number of ways. In order to overcome these limitations, the new JSDBG2 API was designed and implemented by Jim Blandy and Jason Orendorff, and the first version of this work landed in Firefox 8. Jim, being the kind of guy that has forgotten more about debuggers than I will ever know, designed a remote debugging protocol to separate the debugger from the browser, allowing for debugging desktop browsers, mobile browsers, mail readers and even more exotic software in the future. Dave Camp got a great protocol implementation up and running, but was assigned managerial duties (poor sap!) before having a chance to finish the job, so Victor Porof, Mihai Șucan and yours truly lined up to carry the torch. The culmination of this work landed in mozilla-central last week, including the remote protocol implementation and a prototype UI (disabled by default). It is now available in nightlies for adventurous souls to play with.

Let me be clear about that last point: what's in there right now is not useful for day-to-day work, even for experienced developers. There is no UI to add breakpoints besides the experimental Graphical Command Line Interface (whose awesomeness deserves a separate post), no stepping, no variable inspection besides call parameters and 'this', and numerous other limitations. In short: it's not ready, yet. In the course of the next weeks we will be furiously working to add the missing bits and improve the UI, so that it becomes a worthy addition to our existing suite of developer tools.

If, however, you are the kind of person who doesn't take no for an answer, here is what you need to do to play with it: type about:config in the awesomebar and set the preference devtools.debugger.enabled to true. Also, set devtools.gcli.enable to true in order to be able to set breakpoints. Restart your browser and select Tools -> Web Developer -> Script Debugger to open it. You will need to reload the page for the debugger to become aware of the scripts in the page (see? It's not ready!). Browse through the source scripts to find a place where you would like to add a breakpoint, then open the web console and type break add line <url> <line>. Don't worry about memorizing it, GCLI will guide you along the way, suggesting among the available options at each step. When the code hits the breakpoint, the debugger will display the stack frames and the variables in scope (not all of them, it's not ready, remember?) for inspection.

If you have better things to do with your time than play with experimental, uninished debuggers, but would still like to take a peek into what's in store already, here is a short screencast that should give you a broad idea. Note that the stepping functionality in the demo is not in the nightlies yet, because, you guessed it, it's not quite ready.



Ready or not, we would love to have any feedback you might have. Drop by #devtools for a chat, or file bugs to torturehelp us get things fixed. Rest assured, when it's done, debugging web apps will never be the same again.

Tuesday, October 12, 2010

GSS/Pithos update: iPhone, Android, Mongo & getting to 2.0


It has been a while since my last post on GSS/Pithos, and there have been quite a few important announcements that I have not blogged about, only tweeted:
  • a shiny native iPhone client
  • a brand new Android client
  • a back-office application for statistics and admin tasks
  • public folders for hosting static web sites
  • a slew of performance, usability and bug fixes for the web & desktop clients
  • new user registration and quota upgrade workflows
And since the code base has been adopted by other European NRNs, we have also improved our release engineering process and documentation. This however was just the tip of the iceberg. What we are now close to completing, is nothing short of an almost complete rewrite of the core GSS server, aiming at even greater performance and scalability. We are about to release GSS 2.0.

The initial plan was far more modest. We had identified the PostgreSQL installation that stores all of the the system’s data besides the actual files, as a future bottleneck when user adoption would start to accelerate. In such an event there were a few low-to-medium-cost solutions that we could pursue, like replication and manual sharding, but since the problem seemed best suited for a NoSQL solution, we decided to bite the bullet and attempt a transition.

Our field search included lots of products, like Mongo, Couch, Cassandra, Riak, Redis, HBase, MemcacheDB and others. The basic requirements were high write throughput, sharding, replication and easy migration. Most of the products boasted high performance for writes and lots supported replication. In the end we discarded the ones that didn’t support sharding, which limited the options a lot, and we investigated the various APIs, trying to figure out what it would mean to rewrite GSS for one of these. In the end we chose Mongo for its rich query support, since it was the best fit for our problem at hand, making the transition of the existing code base easier. Although we’d love to have had the opportunity to try different ports for each of those datastores, it is most likely that we would have had to rewrite the entire server from scratch, which was not an option.

The initial plan was to try to isolate the changes to the data access layer, essentially building a new adapter for the middle tier that would appear almost identical to the existing code. This effort was fruitless however, since we could not find satisfactory solutions for various issues, like transactions, data mapping, entity lifecycle, etc. We came to the conclusion that even if we managed to build a not-too-leaky abstraction for Mongo, it would have probably killed the performance, making the switch from PostgreSQL pointless.

So we ditched JPA and container-managed transactions and decided to make a simpler POJO mapping to Mongo using Morphia, a Java data mapper for Mongo that takes care of the nitty-gritty details and repetitive coding, for only a minor performance hit. Since Mongo only supports atomicity for a single query, we reverted to manual transaction handling and decided to ditch the EJBs that formed the backbone of GSS altogether, since they didn’t buy us anything at that point. The new GSS server would be a set of servlets, service and domain objects, wired together via Google Guice. Since we could host the new server in a plain servlet container, we went from JBoss to Jetty, which vastly reduced the server startup time from 60 seconds down to 2. As a consequence, development round-trip times went down, and coding was fast again. Lots of object copying and conversions from entities to DTOs and back again were now unnecessary, since there was no more a transaction boundary at the session bean layer. This kept memory usage low, increased cache locality and boosted performance. There are lots of interesting details about our approach, but they would be better discussed in separate blog posts. If you are interested, you can already check out the code in the gss2 branch of the GSS repository.

The next generation GSS server is not production ready yet, but we'll be getting there soon, and so far it has been a great ride!

Friday, February 19, 2010

The Cloud Desktop


This was originally posted in the mynetworkfolders.com blog.

We live in a browser these days. E-mail, word-processing, spreadsheets, chat, are all occupying a browser window in front of me. Maybe not for everyone yet, but the trend is clear, and has been clear for quite some time now. This is a good thing. The Web of today bears little similarity to the Web of the millennium. It's fast, it's powerful and it's ubiquitous. A consequence of that evolution is the simplicity with which I can continue my work when leaving my Linux workstation at work and sit in front of my Mac at home. Since my applications are web-based, they are OS- and browser-agnostic. More importantly, my data are always there when I need them. Readily accessible from my desktop, laptop or mobile phone. I may keep local backups for archival purposes, but my master copies are always online.

Assuming this trend is indeed inescapable, one can't help but wonder: what are the applications that have not migrated to the cloud, yet? What kind of things would I like to do online, seamlessly, from every device I own? What are the barriers that make our current online experience less powerful than the desktop one?

One thing that we noticed was missing from the online desktop, was the... desktop! Yes, my spreadsheets are online. Sure, my pictures are online. Yes, I can upload arbitrary files in various storage services. But, I couldn't find a way to use a familiar file manager interface to work with my data in the cloud, as I do on my computer. This was the need we decided to fulfill with MyNetworkFolders.

MyNetworkFolders attempts to provide an answer to the question: how would my desktop look in the cloud? And even though we are nowhere near done yet, the results so far are exciting and very promising.

MyNetworkFolders offers a familiar file manager interface for my cloud storage needs, with advanced functionality and ease of use. Documents can be viewed, modified, searched or sent to the trash. They can be shared with other users or among the members of a group using flexible ACLs, or even be publicly accessible from the whole internet. They can be versioned for tracking changes more effectively. The service can be accessed from a variety of ways, including a web browser, a desktop client, a WebDAV network share or (really soon) a mobile phone. But most importantly, there is no vendor lock-in. There is a REST-like API available, battle-tested from all the above client implementations, giving everyone the opportunity to access his data in his own particular way. Or even get them out of our service altogether.

Oh, and one other thing. It's all based on open-source software. The core infrastructure, the various clients we have built, even API helpers in various programming languages, are all available for anyone and everyone to see, use and modify. Or maybe used to setup and run YourNetworkFolders, if you feel really ambitious.

If however you just want a true desktop environment, with unparalleled flexibility, and maybe also see how far the cloud desktop rabbit-hole goes, then come along. It's going to be an exciting ride.

Thursday, August 20, 2009

Spell-checking in JavaScript

I just heard of Atwood's Law a few days ago. Apparently Jeff Atwood first published his discovery a couple of years ago, which is like a century in Internet time, but I can't keep up with everything. The Law states that "any application that can be written in JavaScript, will eventually be written in JavaScript". As Atwood explains, it is based on Tim Berners-Lee's Principle Of Least Power:

Computer Science spent the last forty years making languages which were as powerful as possible. Nowadays we have to appreciate the reasons for picking not the most powerful solution but the least powerful. The less powerful the language, the more you can do with the data stored in that language.


I think there is a common theme between Atwood's Law, Zawinski's Law ("Every program attempts to expand until it can read mail. Those programs which cannot so expand are replaced by ones which can") and Greenspun's Tenth Rule ("Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp"). The inevitability of the specified outcome makes the world seem like a much simpler place. There is no need to argue. Believe and ye shall be redeemed.

I might have been in this state of mind, when I came across Peter Norvig's "How to Write a Spelling Corrector", because I knew instantly I had to port the algorithm to JavaScript. The algorithm is quite simple and has already been ported to many different languages, so I seized the opportunity to study the differences in expressiveness, performance and style, between these languages and my latest affection, JavaScript.

After a few night's work I have it working and free (as in beer and speech) for anyone to use. Check out the speller project on GitHub. If you want to understand how the algorithm works, you should go and read Norvig's article, although you might get some hints from the comments in my code. The modestly condensed version of the code is 53 lines:


var speller = {};
speller.train = function (text) {
var m;
while ((m = /[a-z]+/g.exec(text.toLowerCase()))) {
speller.nWords[m[0]] = speller.nWords.hasOwnProperty(m[0]) ? speller.nWords[m[0]] + 1 : 1;
}
};
speller.correct = function (word) {
if (speller.nWords.hasOwnProperty(word)) return word;
var candidates = {}, list = speller.edits(word);
list.forEach(function (edit) {
if (speller.nWords.hasOwnProperty(edit)) candidates[speller.nWords[edit]] = edit;
});
if (speller.countKeys(candidates) > 0) return candidates[speller.max(candidates)];
list.forEach(function (edit) {
speller.edits(edit).forEach(function (w) {
if (speller.nWords.hasOwnProperty(w)) candidates[speller.nWords[w]] = w;
});
});
return speller.countKeys(candidates) > 0 ? candidates[speller.max(candidates)] : word;
};
speller.nWords = {};
speller.countKeys = function (object) {
var attr, count = 0;
for (attr in object)
if (object.hasOwnProperty(attr))
count++;
return count;
};
speller.max = function (candidates) {
var candidate, arr = [];
for (candidate in candidates)
if (candidates.hasOwnProperty(candidate))
arr.push(candidate);
return Math.max.apply(null, arr);
};
speller.letters = "abcdefghijklmnopqrstuvwxyz".split("");
speller.edits = function (word) {
var i, results = [];
for (i=0; i < word.length; i++)
results.push(word.slice(0, i) + word.slice(i+1));
for (i=0; i < word.length-1; i++)
results.push(word.slice(0, i) + word.slice(i+1, i+2) + word.slice(i, i+1) + word.slice(i+2));
for (i=0; i < word.length; i++)
speller.letters.forEach(function (l) {
results.push(word.slice(0, i) + l + word.slice(i+1));
});
for (i=0; i <= word.length; i++)
speller.letters.forEach(function (l) {
results.push(word.slice(0, i) + l + word.slice(i));
});
return results;
};


It may not be as succinct as Norvig's Python version (21 lines), or the record-holding Awk and F# versions (15 lines), but is much better than C (184 lines), C++, Perl, PHP, Rebol and Erlang. There is even a Java version with 372 lines. It must contain some sort of spell-checking framework in there, or something. The condensed version above, although correct, has terrible performance in most JavaScript engines, however. For real-world use you should pick the regular version which may be slightly longer, but performs much better.

Since this was a toy project of mine, I wanted to play with ServerJS modules as well, in order to run it as a shell script. I turned the code into a securable module, so you can run it from the command line, using narwhal. I have a couple of scripts to that end. Of course since this is JavaScript, you can try it from your browser, by visiting the demo page, courtesy of GitHub Pages. Be sure to use a modern browser, like Firefox 3.5, Safari 4 or Chrome 3 (beta), otherwise you won't be able to run the test suite, since I used the brand-new Web Workers to make the long-running tasks run in the background.

Norvig's Python implementation took 16 seconds for test 1 and the best I got was 25 seconds with Safari 4 on my Mac. Narwhal is using Rhino by default, so it is definitely not competitive in such tests (139 seconds), but I'm planning to fix support for v8cgi and give that a try.

And it goes without saying that I'd love to hear about ways to improve the performance or the conciseness of the code. If you have any ideas, don't be shy, leave a comment or even better fork the code and send me a pull request on GitHub.

Tuesday, July 7, 2009

GSS authentication

As I've described before, the GSS API is a REST-like interface for interacting with a GSS-based service, like Pithos. Using regular HTTP commands a client can upload, download, browse and modify files and folders stored in the GSS server. These interactions have two important properties: they store no client state to the server and they are not encrypted. I have already mentioned the most important benefits from the stateless architecture of the GSS protocol, namely scalability and loose coupling along the client-server boundary. In the same line of thought, SSL/TLS encryption of the transport was avoided for scalability reasons. Consequently, these two properties of the communication protocol, lead to another requirement: message authentication for each API call.


Since no session state is stored in the server, the client must authenticate each request as if it were the first one. Traditional web applications use an initial authentication interaction between client and server, that creates a unique session ID, which is transmitted with each subsequent request in that same session. While using this ID, the client does not need to authenticate again to the server. Stateless protocols, like WebDAV for instance, cannot rely on such an ID and have to transmit authentication data in each call. Ultimately the tradeoff is a minor increase in the message payload, in return for a big boost in scalability. The HTTP specification defines an Authorization header for use in such cases and WebDAV uses the HTTP Digest Access Authentication scheme. The GSS API uses a slight variation in that theme, blended with some ideas from OAuth request signing.

Essentially the standard HTTP Authorization header is populated with a concatenation of the username (for identifying the user making the request) and a HMAC-SHA1 signature of the request. The request signature is obtained by applying a secret authentication token to a concatenated string of the HTTP method name, the date and time of the request and the actual requested path. The GSS API page has all the details. The inclusion of the date and time helps thwart replay attacks using a previously sniffed signature. Furthermore, a separate header with timestamp information, X-GSS-Date, is used to thwart replay attacks with a full copy of the payload. The authentication token is issued securely by the server to the client and is the time-limited secret that is shared between the client and server. Since it is not the user password that is used as a shared secret, were the authentication token to be compromised, any ill effects would have been limited to the time period the token was valid. There are two ways for client applications to obtain the user's authentication token, and they are both described in detail in the API documentation.

In my experience, protocol descriptions are one thing and working code is a totally different one. In that spirit, I'm closing this post with a few tested implementations of the GSS API signature calculation, for client applications in different languages. Here is the method for signing a GSS API request in Java (full code here):


public static String sign(String httpMethod, String timestamp,
String path, String token) {
String input = httpMethod + timestamp + path;
String signed = null;

try {
System.err.println("Token:" + token);
// Get an HMAC-SHA1 key from the authentication token.
System.err.println("Input: " + input);
SecretKeySpec signingKey = new SecretKeySpec(
Base64.decodeBase64(token.getBytes()), "HmacSHA1");

// Get an HMAC-SHA1 Mac instance and initialize with the signing key.
Mac hmac = Mac.getInstance("HmacSHA1");
hmac.init(signingKey);

// Compute the HMAC on the input data bytes.
byte[] rawMac = hmac.doFinal(input.getBytes());

// Do base 64 encoding.
signed = new String(Base64.encodeBase64(rawMac), "US-ASCII");

} catch (InvalidKeyException ikex) {
System.err.println("Fatal key exception: " + ikex.getMessage());
ikex.printStackTrace();
} catch (UnsupportedEncodingException ueex) {
System.err.println("Fatal encoding exception: "
+ ueex.getMessage());
} catch (NoSuchAlgorithmException nsaex) {
System.err.println("Fatal algorithm exception: "
+ nsaex.getMessage());
nsaex.printStackTrace();
}

if (signed == null)
System.exit(-1);
System.err.println("Signed: " + signed);
return signed;
}


Here is a method for signing GSS API requests in JavaScript, that uses the SHA-1 JavaScript implementation by Paul Johnston (full code here):


function sign(method, time, resource, token) {
var q = resource.indexOf('?');
var res = q == -1? resource: resource.substring(0, q);
var data = method + time + res;
// Use strict RFC compliance
b64pad = "=";
return b64_hmac_sha1(atob(token), data);
}

Here is a Tcl implementation by Alexios Zavras (full code here):


proc ::rest::_sign {id} {
variable $id
upvar 0 $id data
set str2sign ""
append str2sign $data(method)
append str2sign $data(date)
set idx [string first ? $data(path)]
if {$idx < 0} {
set str $data(path)
} else {
incr idx -1
set str [string range $data(path) 0 $idx]
}
# append str2sign [::util::url::encode $str]
append str2sign $str
puts "SIGN $str2sign"
set sig [::base64::encode [::sha1::hmac -bin -key $::GG(gsstoken) $str2sign]]
set data(signature) $sig
}

I'd love to show implementations of the signature calculation for other languages as well, but since my time is scarce these days, I could use some help here. If you've written one yourself and you'd like to share, leave a comment and I'll update this post, with proper attribution of course.

Wednesday, June 17, 2009

Retrying transactions with exponential backoff

The method I described in my previous post about retrying transactions could use some improvement. A deficiency that will only become relevant in highly congested servers is the constant retry interval. When two or more transactions try to commit at the same time and fail, with the code from the last post they will retry probably simultaneously again. Random runtime events (like process/thread scheduler decisions, JIT compiler invocations, etc.) might help avoid collisions, but in general the collided transactions may well collide again. And again. And then again, until the specified maximum number of retries is reached.


The general method to alleviate such problems is to randomize the retry intervals. The most well-known algorithm in this category is called exponential backoff. This is one way to implement it for the utility method in my previous post (full code in gss):


public T tryExecute(final Callable<T> command) throws Exception {
T returnValue = null;
// Schedule a Future task to call the command after delay milliseconds.
int delay = 0;
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
for (int i = 0; i < TRANSACTION_RETRIES; i++) {
final int retry = i;
ScheduledFuture<T> future = executor.schedule(new Callable<T>() {

@Override
public T call() throws Exception {
return command.call();
}
}, delay, TimeUnit.MILLISECONDS);

try {
returnValue = future.get();
break;
} catch (ExecutionException e) {
Throwable cause = e.getCause();
if (!(cause instanceof EJBTransactionRolledbackException) ||
retry == TRANSACTION_RETRIES - 1) {
executor.shutdownNow();
throw new Exception(cause);
}
delay = MIN_TIMEOUT + (int) (MIN_TIMEOUT * Math.random() * (i + 1));
String origCause = cause.getCause() == null ?
cause.getClass().getName() :
cause.getCause().getClass().getName();
logger.info("Transaction retry #" + (i+1) + " scheduled in " + delay +
" msec due to " + origCause);
}

}
executor.shutdownNow();
return returnValue;
}
The notable changes in this version are the delayed invocations of the Callable and the retry interval calculation. The former is accomplished using a ScheduledFuture from the excellent utilities in java.util.concurrent, which gets executed after a random time period. The interval calculation is something that can be implemented in a number of ways, either with monotonically increasing intervals or not. I opted for the formula above since it provided the fastest collision elimination in my tests, much faster than the monotonically increasing interval formulas I tried. The MIN_TIMEOUT constant is more of a black art. It should be tuned to the particular hardware and network setup in order to attain maximum efficiency: the minimum number of retries with the minimum interval between retries.

Another issue raised in a comment in my previous post was that the exception I am using as guard, EJBTransactionRolledbackException, may be too generic for this purpose. This is definitely true, EJBTransactionRolledbackException just wraps an OptimisticLockException as one would expect for this case, which in turn wraps the Hibernate-specific StaleObjectStateException that is thrown initially. However, contention in a database table, index or row might not necessarily result in optimistic locking violations, but in deadlocks as well, when one transaction holds exclusive locks on resources that are needed by the other and vice versa. Deadlock detection is performed by the DBMS, which forces a rollback of both transactions. This time however the initial exception (at least in my testing, might be DBMS or JDBC driver-specific) is GenericJDBCException which gets wrapped in a PersistenceException, which in turn is put inside a EJBTransactionRolledbackException before being received by our call site. Therefore, the generic nature of the guard is not a problem in this case. On the contrary it covers more relevant cases and one might argue even that there is no better service that can be offered to the caller, than retrying in all such occasions.

Monday, June 15, 2009

Retrying transactions in Java

When building a web-based system, the most often encountered persistence solution is the use of a relational DBMS. This provides many benefits, but probably the most important one is that its behavior and usage are widely understood after so many decades of use. The simplicity of the ACID transactional model, coupled with the nice fit of the HTTP request/response protocol, make for a killer combination.

The simplicity starts to break down however, when the load on the DBMS server increases, causing transactions to start failing. The DBMS can guarantee that no data corruption will occur, but handling such cases is not that simple. The scalable Optimistic Locking model requires transactions to be resubmitted when failure occurs. Since this is supposed to be rather rare (otherwise optimistic locking should probably not be used), developers often cheat by not dealing with such errors, letting them bubble up to the user, who is then responsible for resubmitting the transaction. This leads to simple, elegant codebases and unhappy users.

Adding ORM to the mix only makes things worse. In JavaEE, the JPA API hides much of the complexity of dealing with relational databases, but corner cases become harder. Add the container-managed transaction boundaries provided by session EJBs and one might be tempted to raise his hands up in despair.

However a solution is not as hard as it sounds. Provided that session EJB methods are sufficiently free from side effects, one can replay a transaction for a number of times from the call site (external to the session bean) with a small amount of code and a small added complexity to the call sites. Since I seem to come up against this requirement quite often, I thought I should describe one way to do it. The following tryExecute() helper method, retries any action supplied in a standard Callable interface for a number of times, in the face of optimistic locking violations:


public T tryExecute(Callable<T> command) throws Exception {
T returnValue = null;
for (int i = 0; i < TRANSACTION_RETRIES; i++) {
try {
returnValue = command.call();
break;
} catch(EJBTransactionRolledbackException trbe) {
if (i == TRANSACTION_RETRIES - 1)
throw trbe;
} catch (Exception e) {
throw e;
}
logger.debug("Transaction retry: " + (i+1));
}
return returnValue;
}


By abstracting away the session bean method code in a parameterized class we can reuse this method in every place that needs to retry transactions. Using a Callable as a parameter lets us return any values received from the session bean. For supplying bean methods that do not return values, we could create Runnable instances and wrap them to Callable using Executors.callable(). You can see the original code here.

If you use the above code in a JavaSE environment without EJB, you should probably need to catch OptimisticLockException for JPA, or even StaleObjectStateException, if you don't mind depending on a Hibernate API. I use the above helper method like this in my code:


file = new TransactionHelper<FileHeaderDTO>().tryExecute(new Callable<FileHeaderDTO>() {
@Override
public FileHeaderDTO call() throws Exception {
return sessionBean.createFile(user.getId(), folder.getId(), name, mimeType, uploadedFile);
}
});


Java's verbosity makes this not that easy to understand at first sight, but what happens is very simple indeed: I wrap a one-liner call to the session bean method createFile(), in a Callable and supply it to tryExecute(). The result from the bean method is received in a local variable as usual. One thing to keep in mind though is that since Java doesn't have closures, the above anonymous function can only receive final variables as parameters. In my example user, folder, name, mimeType and uploadedFile are all final. You might have to resort to using temporary local variables for storing final values on some occasions.

Thursday, April 23, 2009

An introduction to the GSS API

Application Programming Interface or API design is one of my favorite topics in programming, probably because it is both a science and an art. It is a science because there are widely accepted principles about how to design an API, but at the same time applying these principles within the constraints of a given programming language, requires the finesse of an experienced practicioner. Therefore it is with great pleasure that I'll try to explain the ins and outs of the GSS REST-like API, as promised before. As I've already mentioned, GSS is both the name of the source code project in Google Code, as well as the GRNET-sponsored service for the Greek research and academic network (although, it's official name after leaving the beta stage will be Pithos). Since anyone can use the open-source code to setup a GSS service, in this post I'll use generic examples, so anyone writing a client for the GRNET service should modify them accordingly.

When developing an application in a particular programming language, we are used to thinking about the APIs presented to us by the various libraries, which are invariably specified in that same language. For instance, for communicating with an HTTP server from a Java program we might use the HttpClient library API. This library presents a set of Java classes, interfaces and methods for interacting with web servers. These classes hide the underlying complexity of making the low-level HTTP protocol operations, allowing our mental process to remain constantly in a Java world. We could however interact with a web server without such a library, opting to implement the HTTP protocol interactions ourselves instead. Unfortunately, there is no such higher-level library for GSS yet, wrapping the low-level HTTP communications. Therefore this post will present a low-level API, in the sense that one has to make direct HTTP calls in his chosen programming language. The good news is that the following discussion is useful for programmers with any background, since there is support for the ubiquitus HTTP protocol in every modern programming language.

A RESTful API models its entities as resources. Resources are identified by Uniform Resource Identifiers, or URIs. There are four kinds of resources in GSS: files, folders, users & groups. These resources have a number of properties that contain various attributes. The API models these entities and their properties in the JSON format. There is also a fifth entity that is not modeled as a resource, but is important enough to warrant special mention: permissions.

Users · Users are the entities that represent the actual users of the system. They are used to login to the service and separate namespaces of files and folders. User entities have attributes like full name, e-mail, username, authentication token, creation/modification times, groups, etc. The URI of a user with username paul would be:

http://host.domain/gss/rest/paul/
The JSON representation of this user would be something like this:
{
"name": "Paul Smith",
"username": "paul",
"email": "paul@gmail.com",
"files": "http://hostname.domain/gss/rest/paul/files",
"trash": "http://hostname.domain/gss/rest/paul/trash",
"shared": "http://hostname.domain/gss/rest/paul/shared",
"others": "http://hostname.domain/gss/rest/paul/others",
"tags": "http://hostname.domain/gss/rest/paul/tags",
"groups": "http://hostname.domain/gss/rest/paul/groups",
"creationDate": 1223372769275,
"modificationDate": 1223372769275,
"quota": {
"totalFiles": 7,
"totalBytes": 429330,
"bytesRemaining": 10736988910
}
}


Groups · Groups are entities used to organize users for easier sharing of files and folders among peers. They can be used to facilitate sharing files to multiple users at once. Groups belong to the user who created them and cannot be shared. The URI of a group named work created by the user with username paul would be:
http://host.domain/gss/rest/paul/groups/work
The JSON representation of this group would be something like this:

[
"http://hostname.domain/gss/rest/paul/groups/work/tom",
"http://hostname.domain/gss/rest/paul/groups/work/jim",
"http://hostname.domain/gss/rest/paul/groups/work/mary"
]


Files · Files are the most basic resources in GSS. They represent actual operating system files from the client's computer that have been augmented with extra metadata for storage, retrieval and sharing purposes. Familiar metadata from modern file systems are also maintained in GSS, like file name, creation/modification times, creator, modifier, tags, permissions, etc. Furthermore, files can be versioned in GSS. Updating versioned files retains the previous versions, while updating an unversioned file replaces irrevocably the old file contents. The URI of a file named doc.txt located in the root folder of the user with username paul would be:
http://host.domain/gss/rest/paul/files/doc.txt
The JSON representation of the metadata in this file would be something like this:

{
"name": "doc.txt",
"creationDate": 1232449958563,
"createdBy": "paul",
"readForAll": true,
"modifiedBy": "paul",
"owner": "paul",
"modificationDate": 1232449944444,
"deleted": false,
"versioned": true,
"version": 1,
"size": 802,
"content": "text/plain",
"uri": "http://hostname.domain/gss/rest/paul/files/doc.txt",
"folder": {
"uri": "http://hostname/gss/rest/aaitest@uth.gr/files/",
"name": "Paul Smith"
},
"path": "/",
"tags": [
"work",
"personal"
],
"permissions": [
{
"modifyACL": true,
"write": true,
"read": true,
"user": "paul"
},
{
"modifyACL": false,
"write": true,
"read": true,
"group": "work"
}
]
}


Folders · Folders are resources that are used for grouping files. They represent the file system concept of folders or directories and can be used to mirror a client's computer file system on GSS. Familiar metadata from modern file systems are also maintained in GSS, like folder name, creation/modification times, creator, modifier, permissions, etc. The URI of a folder named documents located in the root folder of the user with username paul would be:
http://host.domain/gss/rest/paul/files/documents
The JSON representation of this folder would be something like this:

{
"name": "documents",
"owner": "paul",
"deleted": false,
"createdBy": "paul",
"creationDate": 1223372795825,
"modifiedBy": "paul",
"modificationDate": 1223372795825,
"parent": {
"uri": "http://hostname.domain/gss/rest/paul/files/",
"name": "Paul Smith"
},
"files": [
{
"name": "notes.txt",
"owner": "paul",
"creationDate": 1233758218866,
"deleted":false,
"size":4567,
"content": "text/plain",
"version": 1,
"uri": "http://hostname.domain/gss/rest/paul/files/documents/notes.txt",
"folder": {
"uri": "http://hostname.domain/gss/rest/paul/files/documents/",
"name": "documents"
},
"path": "/documents/"
}
],
"folders": [],
"permissions": [
{
"modifyACL": true,
"write": true,
"read": true,
"user": "paul"
},
{
"modifyACL": false,
"write": true,
"read": true,
"group": "work"
}
]
}

Working with these resources is accomplished by sending HTTP protocol requests to the resource URI with GET, HEAD, DELETE, POST, PUT methods. GET requests retrieve the resource representation, either the file contents, or the JSON representations for the resources specified above. HEAD requests for files return just the metadata of the file and DELETE requests remove the resource from the system. PUT requests upload files to the system from the client, while POST requests perform various modifications to the resources, like renaming, moving, copying, moving files to the trash, restoring them from the trash, creating folders and more. The operations are numerous and I hope to cover them in more detail in a future post.

One important aspect of every RESTful API is the use of URIs to allow the client to maintain a stateful conversation. For example, fetching the user URI would provide the files URI for fetching the available files and folders. Fetching the files URI would in turn return the URIs for the particular files and folders contained in the root folder (along with other folder properties). Returning to the parent of the current folder would entail following the URI contained in the parent property. This mechanism removes the state handling from the server and puts the burden on the client, providing excellent scalability for the service. Furthermore, since the URIs are treated opaquely by the client, the API allows client reuse across server deployments. A client can target multiple GSS services, as long as they speak the same RESTful API. Moreover, links from service A can refer to resources in service B without a problem (in the same authentication domain, e.g. the same Shibboleth federation). This is the same as using a single web browser to communicate with multiple web servers, by following links among them.

Monday, April 20, 2009

Reconciling Apache Commons Configuration with JBoss 5

There are many ways to configure a JavaEE application. Among the available solutions are DBMS tables, JNDI, JMX MBeans and even plain old files in a variety of formats. While we have used most of the above in various occasions, I find that plain files resonate with all types of sysadmins, when no other administrative interface is available. For such scenarios, Apache Commons Configuration is undoubtedly the best tool for the job. Recently, I came across an undocumented incompatibility when using Commons Configuration with JBoss 5 and I thought I should describe our solution for the benefit of others.

Usually we are storing our configuration files in the standard place for JBoss, which is JBOSS_HOME/server/default/conf, for the default server configuration. This has the disadvantage that is not as easy to remember as /etc in UNIX/Linux or \Program Files and \Windows in Windows systems, but it has the important advantage of being specified as a relative path in our code, making it more cross-platform without cluttering it with platform-specific if/else path resolution checks.

Commons Configuration can reload the configuration files automatically when changed in the file system, which helps prolong the server uptime. However JBoss 5 has introduced the concept of a virtual file system that caches all file system accesses performed through the context class loaders using relative paths. Unfortunately this generates resource URLs in the form vfsfile:foo.properties that Commons Configuration does not know how to deal with. Fixing this requires extending FileChangedReloadingStrategy, like we do in gss. Alternatively, one could patch Commons Configuration with the following change and use the standard FileChangedReloadingStrategy unchanged:


Index: FileChangedReloadingStrategy.java
===================================================================
--- FileChangedReloadingStrategy.java (revision 764760)
+++ FileChangedReloadingStrategy.java (working copy)
@@ -46,6 +46,9 @@
/** Constant for the jar URL protocol.*/
private static final String JAR_PROTOCOL = "jar";

+ /** Constant for the JBoss MC VFSFile URL protocol.*/
+ private static final String VFSFILE_PROTOCOL = "vfsfile";
+
/** Constant for the default refresh delay.*/
private static final int DEFAULT_REFRESH_DELAY = 5000;

@@ -161,7 +164,8 @@

/**
* Helper method for transforming a URL into a file object. This method
- * handles file: and jar: URLs.
+ * handles file: and jar: URLs, as well as JBoss VFS-specific vfsfile:
+ * URLs.
*
* @param url the URL to be converted
* @return the resulting file or null
@@ -181,6 +185,18 @@
return null;
}
}
+ else if (VFSFILE_PROTOCOL.equals(url.getProtocol()))
+ {
+ String path = url.getPath();
+ try
+ {
+ return ConfigurationUtils.fileFromURL(new URL("file:" + path));
+ }
+ catch (MalformedURLException mex)
+ {
+ return null;
+ }
+ }
else
{
return ConfigurationUtils.fileFromURL(url);


Neither of these solutions covers the case of storing configuration files in zip or jar containers, but since it is something I haven't found a use for yet, I can't test a fix for it. If anyone is interested in such a use case, I'd advise extending FileChangedReloadingStrategy, combining the logic in jar: and vfsfile: URL handling.

Monday, April 13, 2009

GSS architecture

Handling a large number of concurrent connections requires many servers. Not only because scaling vertically (throwing bigger hardware at the problem) is very costly, but also because even if an application can be designed to scale vertically, the underlying stack probably can not. Java applications for instance, like GSS, run on the JVM and although the latter is an excellent piece of engineering, using huge amounts of heap is not something it's tuned for. Big Iron servers with many cores and 20+ GB of RAM are usually running more than one JVM, since garbage collection is not all that efficient with huge heaps. And since running application instances with a 4-8 GB heap size can be done with cheap off-the-shelf hardware, why spend big bucks on Big Iron?

So having a large number of servers is a sane choice, but brings it's own set of problems. Unless one partitions users to servers (having all requests a particular user makes be delivered to the same server), all servers must have a consistent view of the system data, in order to deliver meaningful results. Assigning user requests to particular servers, usually requires expensive application layer load-balancers or customized application code on each server, so it would rarely be your first option. Having all servers work on the same data is a more tractable problem, since it can be solved by having the application state being replicated among server nodes. Usually, only a small part of the application state needs to be replicated, for each user, that is the part which concerns his current session. But even though session clustering solutions have been a well studied field and implementations abound, having no session to replicate is an even better option.

For GSS we have implemented a stateless architecture for the core server, that should provide us with good scalability in a very cost-effective manner. The most important part in this architecture is the REST-like API that moves part of the responsibility for session state maintenance to the client applications, effectively distributing the system load to more systems than the available server pool. Furthermore, client requests can be authenticated without requiring an SSL/TLS transport layer (even though it can be used if extra privacy is required), which would entail higher load on the application servers or require expensive load balancers. In the server side, API requests are being handled by servlets that enlist the services of stateless session beans, for easier transaction management. Our persistence story so far is JPA with a DBMS backing, plus high-speed SAN storage for the file contents. If or when this becomes a bottleneck, we have various contingency plans, depending on the actual characteristics of the load that will be observed.


The above image depicts the path user requests will travel along, while various nodes interact in order to serve them. A key element in this diagram is the number of different servers that can be found, effectively specializing in their own particular domain. Although the system can be deployed on a single physical server (and regularly is, for development and testing), consisting of a number of standalone sub-services instead of a big monolithic service is a boon to scalability.

This high-level overview of the GSS architecture should help those interested to find their way around the open-source codebase and explain some of the design decisions. But the most interesting part from a user's point of view would be the REST-like API, that allows one to take advantage of the service for scratching his own itch.

So that will be the subject of my next post.

Wednesday, April 8, 2009

Introducing GSS

During my recent work-induced blog hiatus, I've been working on a new software system, called GSS. I've been more than enjoying the ride so far and since we have released the code as open-source, I thought discussing some of the experience I've gained might be interesting to others as well.

GSS is a network, er grid, er I mean cloud service, for providing access to a file system on a remote storage space. It is the name of both a service (currently in beta) for the Greek research and academic community and the open source software used for it, that can also be used by others for deploying such services. It is similar in some ways to services like iDrive, DropBox and drop.io, but it can also be regarded as a more high-level Amazon S3. Its purpose is to let the desktop computer's file system meet the cloud. The familiar file system metaphors of files and folders are used to store information in a remote storage space, that can be accessed from a variety of user and system interfaces, from any place in the world that has an Internet connection. All usual file manager operations are supported and users can share their files with selected other users or groups, or even make them public. Currently there are four user interfaces available, a web-based application, a desktop client, a WebDAV interface and an iPhone web application, in various stages of development. Underlying these user interfaces is a common REST-like API that can be used to extend the service in new, unanticipated ways.


The main focus of this service was to provide the users of the Greek research and academic community with a free, large storage space that can be used to store, access, backup and share their work, from as many computer systems as they want. Since the available user base is close to half a million (although the expected users of the service are projected to the low ten thousands), we needed a scalable system, that would be able to accommodate high network traffic and a high storage capacity at the same time. A Java Enterprise Edition server coupled with a GWT-based web client and a stateless architecture were our solution. In future posts I will describe the system architecture with all the gory details. The exposed virtual file system features file versioning, trash bin support, access control lists, tagging, full text search and more.

All of these features are presented through an API for third party developers to create scripts, applications or even full blown services that will fulfill their own particular needs or serve other niches. This API has a REST-like design and though it will probably fail a formal RESTful definition, it sports many of the advantages of such architectures:

  • system resources such as users, groups, files and folders are represented by URIs
  • GET, HEAD, POST, PUT and DELETE methods on resources have the expected semantics
  • HTTP caching is explicitly supported via Last-Modified, ETag & If-* headers
  • resource formats for everything besides files are simple JSON representations
  • only authenticated requests are allowed, except for public resources

Users are authenticated through the GRNET Shibboleth infrastructure. User passwords are never transmitted to the GSS service. Instead GSS-issued authentication tokens are used by both client and server to sign the API requests after the initial user login. SSL transport can provide even stronger privacy guarantees, but it is not required, nor enabled by default.

The GSS code base is GPL-licensed and therefore anyone can use it as a starting point to implement his own file storage service. We have yet to provide binary downloads, due to the various dependencies, but the build instructions should be enough to get someone started. We are always interested in source or documentation patches, of course (did I mention it's open source?). Most importantly, the REST API will ensure that clients developed for one such service can be reused for every other one.

I will have much more to say about the API in a future post. In the meantime you can peruse the code and documentation, or even try it out yourself. I'd be very interested in any comments you might have.

Saturday, March 14, 2009

Applications meme

It's been a while since my last post, but I've been wanting to write about the interesting stuff that I've been working on. Unfortunately it took longer than anticipated, but now we're almost done and I'll be posting soon with the gory details. However, since I got tagged, I suppose I can throw in a not-so-interesting post, about my Linux application preferences.


  1. Which desktop manager do you use more often ?

    Gnome. It's the only one that reminds me of OS X somewhat (appearance- and functionality-wise).

  2. Which desktop application you would not like to see implemented again on linux? And why?

    A desktop manager. If you are going to provide yet another incarnation of a 30-year old idea, please don't bother. The concept is pretty well understood: icons/workspace/file manager/drag'n'drop/mouse/menus/launcher/whatever, it's been done before. Like a million times. If you want to be useful, try to fix some of the shortcomings of the existing ones (notification mechanisms, translucent effects, faster access patterns, application integration, etc.).

  3. Which desktop application you definitely would like to see implemented on linux? Describe it briefly or point out to a similar application.

    Adobe Premiere, or iMovie, or any other semi-decent video editing software. This was the main reason I bought a Mac.

  4. Write the name of the last project (not the very best, the last!) that made you wish to thank their developers so you can thank them now!

    Git. You guys rock! (And if you improve on the UI, you will jazz!)

  5. (Optional) Link the blogs of 1-3 people you’d like to take part to this meme. (no more than three). you can skip this question if you like.

    Let's hear from chstath, ckotso and keramida (who may s/Linux/FreeBSD/ at his discretion).



Sunday, December 9, 2007

How programming languages grow


Q: How do programming languages grow?
A: With pain.


Unless you were lost on a deserted island far away from civilization, you must have heard something about the ECMAScript v4 shenanigans. ECMAScript 4 is the future standard specification for what most people will undoubtedly refer to as JavaScript 2. This forthcoming specification brings a whole slew of features into the JavaScript language, about a decade after the current ECMAScript 3 standard came out. Many of the new features are about facilitating the creation of large programs in the language and improving their safety and robustness. As an intentional side-effect, they maintain and improve the syntactic similarity between JavaScript and Java, so that most young programmers will find it easy to learn and use (in case you didn't know, Java is one of the languages students learn in almost every CS curriculum nowadays).

The breadth of the changes as well as the increased similarity to Java has led some people to protest against them, while they portray the future of their beloved language with gloom and doom. Some members of the ECMAScript standardization committee decided to get off the standardization effort and pursue an ECMAScript v3.1, that is much less ambitious in scope, though still quite vague. These members are Microsoft and Douglas Crockford and while Redmond's move has set off the (usually correct) conspiracy reflexes of the community, Crockford's objections carry more weight to those who pay attention.

Doug Crockford has been known for many things in the JavaScript community, but what I respect most is his simplified JavaScript parser, which is itself written in the same simplified JavaScript. This is the second JavaScript parser in JavaScript that I'm aware of, the first being Brendan Eich's Narcissus. This subset of JavaScript that Crockford advocates for (and is still available in the ECMAScript 4 specification) prefers a more functional style of programming with heavy use of prototypal inheritance, compared to the traditional object-oriented style used in Java and proposed for ECMAScript 4. To cut a long story short, the creator of JavaScript convincingly makes the point that JavaScript must evolve if it is to keep up with the needs of its users, otherwise they will pick something else, like Silverlight or Flex.

In that presentation, Brendan Eich makes a reference to a talk given by Guy Steele in 1998, titled "Growing a Language". It was about the time that the Java community was having a debate about adding generics, operator overloading, complex numbers and other features. In the end some of the proposed changes made it to the standard, like generics, while others like operator overloading, did not. Today another debate is raging in the Java community, about adding closures to the language. Though perhaps less emotional than the JavaScript debate, it is still highly divisive and the reactions are occasionally overboard. It seems changing a language always involves fear, anxiety and pain. Guy Steele's talk provides some insights.

Its been about a decade since that talk, and it shows. No Powerpoint/Keynote slides, just hand-written ones manually placed on the projector. Even the haircut is out of fashion. However the actual presentation is sheer genius. Steele uses the form of his talk to illustrate the point of his argument. In order to demonstrate the difference between small languages and big languages in terms of the programs they can create and the difficulty of their use, he picks a subset of English as a given, all words with one syllable, and every time he needs to use another word he provides a definition. In the same way you provide a class definition of a Person in Java and then go on talking about that person in your business code as if it was a language primitive, Steele makes a talk sound like a computer program and as he confesses in the end, creating that talk was a lot like writing a program.

It may seem weird at first, but as you get the hang of it, it's an eye-opener. Not suitable for computer-language illiterate people of course. Your girlfriend definitely ain't gonna like it. Even the jokes are kinda geeky. Just watch it when you have an hour to spare and nobody is watching you. Then perhaps you might be able to understand Brendan Eich's passion. And do the right thing: use Firefox.

Creative Commons License Unless otherwise expressly stated, all original material in this weblog is licensed under a Creative Commons Attribution 3.0 License.