The Blog of Maxim Porges
  • I Want a Nook

    With my birthday and Christmas approaching, my wife always asks the inevitable question of what I’d like to receive as a gift. Due to the fact that I’m either totally spoiled or have meagre requirements for self-actualization, I’ve got pretty much everything that I could ever want, and it isn’t often that a material possession of any kind should take my fancy. However, with all these eBook readers coming out, now is one of those rare times.

    There’s been something of an electronic reading revolution going on for a while, which has continued to gather steam as more mobile devices appeared and laptops dropped in price. For a long time, I couldn’t get used to reading on a computer; there was just something about holding a book and learning to “know” where to open it for a piece of reference material (being a programmer, most of my books are technical and reference is my primary use case). However, in the last few years I’ve really come to love being able to search and annotate electronic documents, and the fact that my Macs print everything to PDF natively has facilitated this to the point where I’m completely paperless. Having an iPhone has allowed me to experience electronic reading on a small screen, which works, but has highlighted for me why a bigger screen would be better and a different device is needed.

    My boss got a Kindle shortly after they came out, and I definitely liked it, but it seemed limited in implementation. Now that B&N has come out with the Nook, the whole concept of a viable eBook reader seems to be getting off the ground.

    Nook vs. Kindle
    There are a few things I like better about the Nook when compared to the Kindle. Firstly, Nook has the Android OS. I can see B&N opening the device up to third party developers at some point, which would be pretty cool. Even if they don’t, I expect that future software revisions will have all sorts of cool stuff in them. Secondly, the Nook has WiFi access. Third, it has a memory expansion slot for a microSD card, and I intend to start using such a card to hold all my technical notes and reference materials as well as my eBooks. Fourth, it supports the EPUB format, which O’Reilly (the premier technical publisher) is embracing as one of their electronic book formats, which means I don’t need to wait for B&N to offer certain titles (and their programming reference library for eBooks is surprisingly poor considering the geek factor associated with eBooks). Finally, it has a replaceable battery.

    On the con side, the Nook doesn’t read .txt or .doc files, but these features seem well in the range of paltry software updates in future revisions. Also, there doesn’t appear to be a way to wirelessly get my self-generated content (such as files from work) on to the Nook, although you obviously can transfer documents via microSD or USB. I’d be willing to pay a small fee to get documents on to the device wirelessly, or to maintain my own online library of images and eBook content. Again, since the phone runs on Android, these are use cases that I see coming to fruition in the future – and if they don’t, it won’t be the end of the world.

    Use cases
    Almost immediately after getting a Nook, I can see myself writing a few handy AppleScripts to convert web pages, software design discussions in emails, technical documents from work and the web, and other stuff to PDF to be saved in a “pending transfer” directory on my computer, which I’ll copy over to my microSD card before powering down at the end of the day. I can then refer to this library of reference materials wherever I may be. Ideally, if the Nook offered a basic web browser one day, I could set the script up to FTP content to a location on my personal web site where I could retrieve it wirelessly on-demand; that would be the bomb.

    Why not wait?
    Of course, there are some other really interesting things happening in this space. The Plastic Logic QUE is not going to be released (or maybe just the details of it) until January 7th, 2010 (although there is a video of its prototype on YouTube). But since the QUE is aimed at business users, will probably be more expensive, is bigger than I would really like my eReader to be, and won’t run Android AFAIK, I’m not sure this is the device for me.

    And then there’s always the possibility that the ubiquitously non-existent Apple tablet should show up some time when we least expect it. We all know that the iPhone rumors took several years to come to fruition, but they finally did – and look at the impact that device has had on its market. Again, an Apple tablet is likely to cost a fortune; $200.00 to $300.00 all-in for a Nook with accessories seems like a good deal to me.

    Now, if only my wife read my blog… :)

    2009.12.05 - Tags: Gadgets / 3 responses / add a response

  • Configuring Tomcat SSL Client/Server Authentication

    Setting up Tomcat to provide self-signed SSL certificates allowing secure client/server communication is well-documented and relatively easy to set up. It’s almost as easy to set up a configuration where Tomcat requires the client to specify an SSL certificate as well, but sadly nowhere near as well-documented since this is a much less-common use case.

    It’s exactly this use case that we needed to put in to place with a secure internal system that we’re working on. To make it as hack-proof as possible, we’re requiring that both the client and the server know about each other and authenticate each other before beginning any communication. I found all the individual fragments of what I needed to get this set up online, but no single resource covered the entire process in detail.

    Since I spent the better part of the day today figuring out how to get this to work, I figured I would share the complete configuration with the world, including Tomcat config, keytool commands, and how to connect a secured client/server from both Java and Safari on Mac OS X. Hopefully this will save the next guy some time.

    Overview

    With traditional SSL, a server presents a certificate to a user agent (such as a web browser) to both

    (a) allow the user agent to validate the server’s identity, and
    (b) begin the process of establishing a secure connection handshake.

    In this configuration, the user agent is not required to authenticate itself, since it is only the server that may be suspect – you don’t want to send your credit card details to a rogue server, but any server (rogue or legitimate) will happily accept credit cards or other sensitive data from any client willing to submit them, so there’s no need to validate the clients as far as the server is concerned.

    In higher-security configurations, the server also requires the client to identify itself by providing its own certificate. Examples include cases where the server has access to sensitive data/capabilities that it doesn’t want to allow any client to access. To facilitate this, the server maintains a keystore containing copies of certificates issued to trusted clients to whom the server will allow access. If a client is unable to supply a certificate, or the certificate that they supply is not in the server’s keystore of trusted certificates, access to the server’s resources is denied.

    The Setup

    The server is configured so that it will present a certificate to clients when an SSL connection is requested. This is achieved by setting up a keystore and providing the location and credentials for the keystore to Tomcat.

    As part of this configuration, Tomcat is told to require clients to present their own certificates authenticating them for access to the server. This is achieved by generating client certificates, and installing copies of these certificates in to the same server keystore used for presenting server SSL (described in the previous paragraph).

    When a client connects to the server, it needs to present a certificate when challenged to do so. To find the certificate that it will provide to the server, the client references its own keystore containing a copy of its certificate. As described in the previous paragraph, a copy of the same client certificate is installed in the server’s keystore (the one with the list of trusted client certificates).

    Finally, if the server’s certificate is self-signed and could be considered untrustworthy, then the client’s keystore must contain a copy of the server’s certificate so that it knows that the server is trustworthy. To facilitate this, a copy of the server’s public SSL certificate is installed in the client keystore as well.

    Keystore and Certificate Generation With keytool

    Java provides a handy command-line tool called keytool that you can use to generate keystores. Each keystore has a private key within it which cannot be exported, so in order to allow the client to share a certificate with the server, keytool lets you export a public version of the private key as a certificate. Exactly why and how this works is beyond the scope of this blog post, so check out some info on how SSL works elsewhere.

    By following the instructions below, you will create two keystores: client.jks (for the client to use) and server.jks (for the server to use). In order to provide copies of the client’s certificate to the server (and vice versa), you will export public certificates based on the private keys. Finally, you will install the server’s public certificate in to the client’s keystore and vice versa, allowing both the client and server to properly authenticate and trust each other when a secure connection is established.

    Generate the Client and Server Keystores

    keytool -genkeypair -alias serverkey -keyalg RSA -dname "CN=Web Server,OU=Application Development,O=Highwinds,L=Winter Park,S=FL,C=US" -keypass password -keystore server.jks -storepass password
    keytool -genkeypair -alias clientkey -keyalg RSA -dname "CN=client,OU=Application Development,O=Highwinds,L=Winter Park,S=FL,C=US" -keypass password -storepass password -keystore client.jks

    Export the Client’s Public Certificate and Import it in to the Server’s Keystore

    keytool -exportcert -alias clientkey -file client-public.cer -keystore client.jks -storepass password
    keytool -importcert -keystore server.jks -alias clientcert -file client-public.cer -storepass password -noprompt
     
    # view the contents of the keystore (use -v for verbose output)
    keytool -list -keystore server.jks -storepass password

    Export the Server’s Public Certificate and Import it in to the Client’s Keystore

    keytool -exportcert -alias serverkey -file server-public.cer -keystore server.jks -storepass password
    keytool -importcert -keystore client.jks -alias servercert -file server-public.cer -storepass password -noprompt
     
    # view the contents of the keystore (use -v for verbose output)
    keytool -list -keystore client.jks -storepass password

    Configure Tomcat for SSL Using the Server Keystore
    Make the following addition to {tomcat.home}/conf/server.xml, substituting the entries for keystoreFile, keystorePass, truststoreFile, and truststorePass with the appropriate paths/passwords from your implementation. These paths will point to the keystores created earlier in this process.

    Note the clientAuth attribute – this is the attribute that causes Tomcat to require the client to provide a certificate; with this option, if no certificate is provided by the client then the connection is terminated by Tomcat immediately. If this is set to false, no client certificate is required or validated (useful for testing server-only SSL configuration). If clientAuth is set to want, client certificates are validated if presented to the server, but are not required.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    <Connector
       clientAuth="true" port="8443" minSpareThreads="5" maxSpareThreads="75"
       enableLookups="true" disableUploadTimeout="true"
       acceptCount="100" maxThreads="200"
       scheme="https" secure="true" SSLEnabled="true"
       keystoreFile="/Users/mporges/Desktop/tomcat-ssl/final/server.jks"
       keystoreType="JKS" keystorePass="password"
       truststoreFile="/Users/mporges/Desktop/tomcat-ssl/final/server.jks"
       truststoreType="JKS" truststorePass="password"
       SSLVerifyClient="require" SSLEngine="on" SSLVerifyDepth="2" sslProtocol="TLS"
    />

    You must restart Tomcat in order for this configuration change to take effect. Note that you must also restart Tomcat whenever you change the contents of the keystore; they are cached at launch and are not re-examined until the server process is bounced.

    Sample Client Application in Java

    The client application presented below uses Apache’s Commons HTTPClient library to establish a secure connection to the Tomcat server. Note the system properties defined in the static initializer; these properties tell the JVM where to find the trust store (telling it which self-signed server certificates it should trust) and the keystore (telling it where to find its own client certificates, which it will then present to the server when challenged). We happened to store both these items in the same keystore, but if you really wanted to you could split them up in to separate files.

    import java.io.IOException;
     
    import org.apache.commons.httpclient.HttpClient;
    import org.apache.commons.httpclient.HttpException;
    import org.apache.commons.httpclient.URI;
    import org.apache.commons.httpclient.methods.GetMethod;
     
     
    public class ClientConnectionTest
    {
       static
       {
          System.setProperty("javax.net.ssl.trustStore", "/Users/mporges/Desktop/tomcat-ssl/final/client.jks");
          System.setProperty("javax.net.ssl.trustStorePassword", "password");
          System.setProperty("javax.net.ssl.keyStore", "/Users/mporges/Desktop/tomcat-ssl/final/client.jks");
          System.setProperty("javax.net.ssl.keyStorePassword", "password");
       }
     
       /**
        * @param args
        * @throws IOException 
        * @throws HttpException 
        */
       public static void main(String[] args) throws HttpException, IOException
       {
          HttpClient client = new HttpClient();
          GetMethod method = new GetMethod();
          method.setURI(new URI("https://localhost:8443", false));
          client.executeMethod(method);
     
          System.out.println(method.getResponseBodyAsString());
       }
    }

    Configuring a Client Certificate in Safari on Mac OS X

    Typically, in a secure client/server setup such as the one we are creating, the client and server will both be application servers. However, nothing stops you from setting up your browser to be a secure client with its own certificate. This is an ultra-secure way to set up restricted access to a web site so that only certain known users can access the web site.

    Unfortunately, the creation and installation of the client certificates and the setup process for letting the browser know to send the certificate to the server are both operating system- and browser-dependent processes. The only one I have had success trying is Safari on Mac OS X, so I have included those instructions here. For instructions on setting up another browser (such as Firefox or Internet Explorer) on other operating systems, Google is your friend.

    How To Create and Install a Client Certificate in Safari on Mac OS X
    In order to connect to the secure server we created earlier from Safari in Mac OS X, a certificate must be created in the Keychain Access application and installed in the server’s keystore before connections will be accepted. This is achieved as follows.

    1. Open Keychain Access.
    2. In the menu bar, go to Keychain Access => Certificate Assistant => Create a Certificate…, which launches an assistant.
    3. Provide a name for the certificate (your own name is appropriate). Select “Self-Signed Root” for “Identity Type” and “SSL Client” for “Certificate Type.” At your option, you may select the “Let me override defaults” setting to specify items such as the key pair key size/algorithm (2048/RSA by default), Key Usage Extensions (only “Signature” by default), Extended Key Usage Extensions (only “SSL Client Authentication” by default), Basic Constraints Extension, Subject Alternate Name Extension, and keychain Location for Certificate (login by default).
    4. After completing the assistant, you will find a new certificate under My Certificates in Keychain Access (under Category in the lower left hand side of the window).
    5. To export the certificate, right click it and select “Export {certificate name}…” Choose a suitable location to save the certificate as a .cer file.
    6. In the command line, navigate to the location of the server.jks keystore created earlier, and enter the following command:
      keytool -importcert -keystore server.jks -alias safari-osx-cert -file {exported certificate file name}.cer -storepass password -noprompt
    7. Restart Tomcat if it is running so that it will pick up the new client certificate that you just installed.
    8. Open Safari and navigate to the location of the Tomcat server. If Tomcat is using a self-signed certificate, you will be prompted with a warning and will have to establish trust for the certificate (either permanently by checking the appropriate checkbox, or just for the life of the browser process by selecting “Continue”).
    9. You will then be challenged for a client certificate; accept the challenge. Safari should select the certificate you created, or alternatively it will prompt you to select a certificate; if so, select the certificate you created in this process. Once a certificate is selected you should not be challenged again for the life of the browser process.
    10. If Safari is unable to select the appropriate certificate automatically, you may need to set up an Identity Preference in Keychain Access. To do so, follow the instructions on Apple’s knowledge base.

    2009.11.18 - Tags: Coding / 5 responses / add a response

  • Blackberry’s Days as the Fruitiest Phone Are Numbered

    As Tyler Durden once said, on a long enough timeline the survival rate for everyone drops to zero. He was talking about the Blackberry.

    If these numbers are to be believed, Apple is totally killing it in gaining smartphone market share. In a few short years they have reached 30% of the smartphone market compared to BB’s 40%. You know the Blackberry is going down over the long term.

    Having owned both phones, I must say I really liked the Blackberry when I had one, but the iPhone is simply awesome (although in fairness I haven’t touched an updated BB model – the last time I touched one was two years ago). It’s the apps, baby. One thing I can say in positive terms about the BB is that although I’ve gotten used to the iPhone’s touch-only keyboard and can blast out an email in short order, I think I was still slightly faster on the BB.

    AAPL just took a massive hit today, so it’s going to be time to get back in soon before they unleash the iPhone in China.

    2009.10.28 - Tags: Apple Gadgets / no responses / add a response

  • Highwinds Described as “Internet Superpower”

    Some say there’s a new cold war brewing; data centers are cold, after all. Not to worry – Highwinds is already working on cyberdecks, so we’ll be ready to save the world when the time comes.

    2009.10.13 - / 2 responses / add a response

  • Calculating the Differences Between Consecutive Rows with SQL

    Just a quickie. I’m sure this is obvious to the seasoned SQL freaks out there, but it was a new solution for me.

    We were recently trying to figure out a simple query to generate a set of differences in values between sets of consecutive rows in a database table. Basically, the table is full of timestamp snapshots with associated values, and we need to calculate the differences between the values in each snapshot for a time period.

    The simple solution I came up with is to normalize the dates, and then use them as a join criteria with a virtual table and some simple date math. You treat the original unmodified table as one side of the join, and a virtual select of the same table with some date math applied to the normalized dates as the other side of the join. So, for tables with timestamps five seconds apart, you simply pull the date back by five seconds on the virtual table and join the dates together. You end up with a single row joining each row with its previous peer, and you can then do direct math within the row to calculate the differences in values.

    Here’s a simple MySQL script that demonstrates the technique, which will probably make more sense than my explanation.

    CREATE TABLE consecutiveDates (
      id INT NOT NULL AUTO_INCREMENT,
      valueDate TIMESTAMP NOT NULL,
      value NUMERIC(10) NOT NULL,
      PRIMARY KEY (id)
    )
     
    INSERT INTO consecutiveDates VALUES (NULL, TIMESTAMP('2009-08-01 10:00:00'), 10);
    INSERT INTO consecutiveDates VALUES (NULL, TIMESTAMP('2009-08-01 10:00:05'), 25);
    INSERT INTO consecutiveDates VALUES (NULL, TIMESTAMP('2009-08-01 10:00:10'), 50);
     
    SELECT    actual.*,
              virtual.*,
              IFNULL((virtual.virtualValue - actual.value), 0) AS 'difference'
    FROM      consecutiveDates actual
              LEFT OUTER JOIN (
                 SELECT   id AS 'virtualId',
                          valueDate AS 'actualVirtualDate',
                          value AS 'virtualValue',
                          TIMESTAMPADD(SECOND, -5, valueDate) AS 'adjustedVirtualDate'
                 FROM     consecutiveDates
              ) virtual ON virtual.adjustedVirtualDate = actual.valueDate

    If you have dates that don’t line up perfectly like my example, you can use basic date math/conversion to normalize the dates in each row in to a rounded representation (i.e. nearest five seconds, nearest minute, etc.). With basic SQL grouping, you can then easily smash together rows that are close enough to each other to be combined in to a single row and achieve the same effect as shown in the script.

    This technique is obviously not limited to dates; you can use a similar approach with virtual row IDs in MySQL. By creating an integer session variable representing a row ID, and then selecting/incrementing it with each row in the query, you can apply basic math to the virtual table’s select statement to offset the session variable by -1 and use that as the join criteria.

    2009.10.01 - Tags: Coding SQL / no responses / add a response

  • Stop Sending Me Email

    Not really, but I did read an interesting post on CodingHorror that struck a chord.

    The average consumer/business user doesn’t really get email, in my opinion. At the end of the day, it’s an asynchronous medium for the kind of communication that either (a) requires a documentation trail from the get-go, or (b) doesn’t require an immediate response.

    I grow tired of email marketing, joke chains, and supposedly funny pictures in my inbox. My inbox is for bills, password change confirmations, and (hopefully brief) communications/notifications from entities that I do business with. It’s also the new proxy for communication across the Atlantic with my family in the UK and across state lines, but in this regard it should do nothing more than act as an electronic form of a physical letter.

    Want to sell me something? Get it on a search engine or an ad on a blog that I follow. Nothing bothers me more than unwarranted solicitation (electronic or otherwise) from companies that I have never heard of.

    Where does the irreverent fun go? Facebook. Post me links and random comments, send me stupid pictures of your cat, whatever you want. For me, Facebook is for bathroom reading, Twitter-esque brevity, and random photos captured in the whim of the moment. I may miss your funny post, but I’ll live. In direct opposition to my stance on email, don’t market to me on Facebook or bring up business concerns – It’s not the right channel. It’s for this very reason that I wonder how Facebook makes any money, since I find nearly all their apps to be obnoxious and marketing-driven; I avoid them like the plague.

    If you send me your opinions via email, I’m unlikely to read them while sifting through my unread messages. If I care about what you think, you’ll find yourself in my Google Reader subscriptions. If you don’t have a medium for publishing to RSS, I’m happy to do without your wisdom.

    Luckily, I think the next generation has already figured this all out. Their instant embrace of text messaging as the medium of choice is proof enough. I’ve already realized that I am starting to miss the boat because it takes me a while to give up my old paper- and desktop-computer-based ways in favor of iPhone-enabled solutions. Not because I am reticent to do so, but because my brain is not wired to think of my iPhone as the Swiss Army knife of daily living. I’m getting better at it with some effort, but my kids won’t even have to think about it; they’ll just instinctively know which gadget/app/thought helmet will serve their needs the best.

    2009.09.30 - / 4 responses / add a response

  • Directing vs. Enabling

    Fowler has a few interesting posts on his blog regarding enabling attitude and directing attitude.

    There’s no such thing as choosing one or the other; they are two extremes that you have to find a balance for in your software team. I’ve both wasted time attempting to direct the untalented/unmotivated and provided excessive direction to those that didn’t need it. Hopefully I’m getting better at metering out each in appropriate doses.

    These days, I’m lucky to have a small enough team that we can selectively hire, but I feel for larger organizations that don’t have that luxury and find themselves needing to fill a seat due to the sheer volume of work. Sooner or later you can find yourself forced to compromise and hire somebody that might get the work done, but ultimately will do more harm than good. No level of direction will save you from these guys; you’ll either be looking over their shoulder all the time (in which case you might as well just do the work yourself) or will take a peek at their work occasionally and wish you’d never hired them.

    The Orlando tech market is unfortunately stocked with mediocre programming talent, especially for Java programmers. This is mainly Sun’s fault. The original J2EE spec was so badly conceived and over-marketed that almost every Java developer with any work experience has been trained in the J2EE way: start with the manufacture of an anemic domain model, tie it inexorably to the container, and sweeten the deal by making it impossible to test. Couple this with the sheer volume of Java projects undertaken by nearly every organization on Earth (most of which were led by non-technical managers with no respect for/understanding of solid software development principles) and you end up with a pretty sorry state of affairs.

    I’ve been fortunate to either know or find some good talent in our murky local talent pool, but it can take me months of tedious interviews to hire for a single position. For the future of Highwinds, I’m hoping that as we continue to grow we can support a co-op program. It’s sadly true that the most talented engineers don’t tend to use traditional recruitment means to find work, so you have to find them while they are still young, impressionable, and in college and snap them up before anybody else gets to them. I’m being overly sinister of course, but getting the first shot at hiring somebody from college in exchange for a few semesters of apprenticeship under a seasoned team strikes me as a good deal for everybody involved.

    And once they are on board? Let the enablement begin.

    2009.09.30 - / no responses / add a response

  • Duct Tape, Astronauts, and Everything In Between

    Joel Spolsky recently blogged about what he calls a “duct-tape programmer.” The blog post made the rounds with a few people at the office after my boss brought it up at lunch yesterday.

    From what I read, Joel’s opinion is that it’s better to ship usable software than spend time engaging in architecture cosmology, which nobody with a moderate amount of experience and any sense would disagree with. However, Joel makes his point by drawing two totally different extremes and making some statements that completely conflict with both my experience and common sense.

    The first extreme is the “duct-tape programmer.” Joel lists a person of this description’s attributes as the following.
    - Not afraid to speak up when another programmer is pitching an overly-complicated solution.
    - Pragmatic.
    - Comfortable with a 50%-good solution that ships over a 99% solution that nobody uses.
    - Focuses on shipping as a top concern.
    - Avoids complicated coding and difficult frameworks/libraries/languages/techniques that are too hard for the human brain to handle.
    - Doesn’t give a shit what others think about them.
    - Spends little to no time writing unit tests.
    - Smart enough to do things like xor the “next” and “prev” pointers into a single DWORD (which makes you wonder how they are not smart enough to work with some of the languages/libraries/frameworks/techniques that are apparently so hard to grasp – but whatever).

    The second extreme is the “architecture astronaut.” Here’s the attribution for this person.
    - Likes over-complicated solutions too hard for the average programmer to understand.
    - Bullies other programmers in to using their frameworks/solutions by relying on the level of complication in code that only they understand as a barrier to objections.
    - Likes multiple inheritance.
    - Spends more time writing frameworks than shippable code.
    - Attends design pattern meetings.
    - Speaks at conferences, writes articles and books.

    I have a little perspective on this since (like most software engineers with a few years under their belt) I’ve both been and dealt with both personality extremes during my career. Both extremes are equally damaging to software products in different ways.

    Let’s start with the duct-tape guy. I’d say most of the attributes for this personality type are positive qualities; pragmatism, the self-confidence to disagree with bad ideas, a tendency for choosing the simpler approach, and a focus on making a shippable product. The quality of this guy that really bothers me is the lack of use/respect for unit testing, and the tone Joel takes with unit testing in general in his post. The argument seems to be that unit testing takes too long to do and doesn’t support rapid development and isn’t worth it.

    This is utter nonsense. I’ve been on projects where the only reason we were able to ship in a short timeframe is because we unit tested early and often, and were able to spot bugs within seconds of them being added to the codebase, which saved time we would have spent debugging broken code later in the product lifecycle.

    The thing with unit testing is that you have to be pragmatic with it as well. Looking to hit 100% coverage? You’re wasting your time. Only testing a handful of methods? You’re missing out on important stuff that will bite you in the ass later. Writing the core of an app with a GUI? If you wait to test until the GUI starts coming together, how do you know your code works? I’ve met nobody, and I mean truly nobody that is good enough to write code the first time and get it right every time. In the absence of unit tests, you find yourself manually testing everything from whatever presentation technology/transport you are using to ensure that it works, which is both slow and error-prone. The point is, choose a reasonable amount of coverage (60%-80% tends to be the sweet spot in my experience) and use it to your advantage.

    So what about the astronaut? Well, this guy suffers from two problems: he’s not a very good programmer, and he has an awful manager.

    Why isn’t he a good programmer? My experience has been that programmers go through four stages when learning a language or framework. As a novice, they barely understand what they are doing and mainly follow the advice of others. As they gain competence, they acquire understanding at a level that makes them productive. As an advanced user, they start pushing the limits of the technology and doing tricks with it, but their solutions are complicated and inelegant. As an expert, they learn to get the seemingly complicated stuff done in a manner that others can understand with little effort and put to use just as easily.

    Clearly, if the astronaut is making frameworks that are that complicated, they aren’t helping anybody. Using Spring as an example, the reason it’s such an excellent framework is that that it makes every part of itself so easy to use. The source code I have seen in Spring is terse and easy to follow, even though it deals with advanced techniques. This is the mark of expert programming worthy of respect.

    Why does his manager suck? Because they’re not keeping this guy on a tight enough leash to focus on what’s important, which is getting stuff done and writing maintainable code that anybody can walk in to and change. Instead, the astronaut is stroking his ego writing frameworks and libraries to “prove” how awesome he is at using the language. I can say this with confidence because I’ve been there and done it earlier in my career, and just like the astronaut, I pulled the wool over my manager’s eyes as well. Luckily, I came through the other side, but I’ve seen lots of people get stuck at this stage. They are easy to spot: they are the people who write code that only they understand – not because it’s good, but because it’s unreadable and unnecessarily complicated. A true expert with the language would certainly be able to slog through this spaghetti, but they’d spend most of that time wanting to punch the astronaut in the face for making it so hard on everybody else instead of making something elegant.

    That’s not all. There are some other things that simply don’t make sense or conflict with themselves in Joel’s post.

    If you write an app with no unit tests and ship it, unless you either (a) never have to change it again or (b) wrote a trivially tiny app, you are likely to find yourself having a really hard time refactoring/adding to it. After all, you’ve got no test suite to validate your changes and prove that you haven’t broken anything. The only way you could even say that you were confident the code still worked in the absence of a test suite is to be able to hold all the details about the app in your head (every branch, every scenario, etc.) which Joel already stated is too hard for the average programmer.

    If you are using a technology that is overcomplicated and/or is buggy (C++ on Windows, CORBA, COM, etc.), then use something else. Techniques like multiple inheritance aren’t so much difficult to grasp as they are generally acknowledged as a code smell in 90% of use cases. And to say that multi-threading is going to doom your project because it can be complicated is really kind of a stupid thing to say; there are some things that simply can’t be done effectively/performantly without multi-threading. Its use should be limited in scope accordingly and written by somebody who understands threading. I don’t think anybody writes multi-threaded code for the fun of it, except perhaps for the astronaut who we’ve already proven is an epic douche to begin with.

    There’s a general tone to the post that any time spent on creating utility library/framework code and/or researching elegant solutions is wasteful. This is a bad generalization. I once had to help save an app at the eleventh hour that one of the guys on my team had spent two months writing. It had failed an embarrassing number of QA drops due to input validation failures. Why? He hadn’t spent any time writing a basic framework for the validation concerns of the app, and every time he patched the bugs found in QA, something else got broken or acted inconsistently with other parts of the app. I sent everybody except me and my programmer home, spent an hour writing a simple validation framework, and we spent the night plugging it in to the app. It passed QA the next day. When I asked him why he hadn’t taken the time to do this in the first place, he said he was under pressure to get the app delivered and didn’t have time to implement a cleaner solution. It goes without saying that excessive architecture is wasteful and damaging, but no architecture at all is just as bad. Liek everything else, you need to be pragmatic and pick sensible battles.

    To summarize: no doubt, programmers should spend 90% of their time writing shippable code that is useful to end users, implemented in the simplest way, using the easiest thing that works. However, to unilaterally throw out unit testing and claim that any level of design work or framework implementation is a bad quality is a dangerous oversimplification that I fear many of the people following Joel’s blog may be inclined to interpret as good advice.

    2009.09.30 - Tags: Coding / no responses / add a response

  • JDK 1.7.0 on Snow Leopard

    I wanted to try out the DirectoryStream class in nio.2, but unfortunately it’s in Java’s 1.7.0 JDK and Apple hasn’t seen fit to get that out yet.

    The obvious solution is to use OpenJDK, and there are some great articles out there (here and here) about installing it on OS X versions earlier than Snow Leopard.

    I was able to get most of the way through the build process described, and hit a roadblock.

    Compiling /Users/mporges/bsd-port/hotspot/src/share/vm/runtime/arguments.cpp
    rm -f arguments.o
    g++ -D_ALLBSD_SOURCE -D_GNU_SOURCE -DIA32 -DPRODUCT -I. -I../generated/adfiles -I../generated/jvmtifiles -I/Users/mporges/bsd-port/hotspot/src/share/vm/asm -I/Users/mporges/bsd-port/hotspot/src/share/vm/c1 -I/Users/mporges/bsd-port/hotspot/src/share/vm/ci -I/Users/mporges/bsd-port/hotspot/src/share/vm/classfile -I/Users/mporges/bsd-port/hotspot/src/share/vm/code -I/Users/mporges/bsd-port/hotspot/src/share/vm/compiler -I/Users/mporges/bsd-port/hotspot/src/share/vm/gc_implementation -I/Users/mporges/bsd-port/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep -I/Users/mporges/bsd-port/hotspot/src/share/vm/gc_implementation/g1 -I/Users/mporges/bsd-port/hotspot/src/share/vm/gc_implementation/parallelScavenge -I/Users/mporges/bsd-port/hotspot/src/share/vm/gc_implementation/parNew -I/Users/mporges/bsd-port/hotspot/src/share/vm/gc_implementation/shared -I/Users/mporges/bsd-port/hotspot/src/share/vm/gc_interface -I/Users/mporges/bsd-port/hotspot/src/share/vm/interpreter -I/Users/mporges/bsd-port/hotspot/src/share/vm/libadt -I/Users/mporges/bsd-port/hotspot/src/share/vm/memory -I/Users/mporges/bsd-port/hotspot/src/share/vm/oops -I/Users/mporges/bsd-port/hotspot/src/share/vm/opto -I/Users/mporges/bsd-port/hotspot/src/share/vm/prims -I/Users/mporges/bsd-port/hotspot/src/share/vm/runtime -I/Users/mporges/bsd-port/hotspot/src/share/vm/services -I/Users/mporges/bsd-port/hotspot/src/share/vm/utilities -I/Users/mporges/bsd-port/hotspot/src/cpu/x86/vm -I/Users/mporges/bsd-port/hotspot/src/os/bsd/vm -I/Users/mporges/bsd-port/hotspot/src/os_cpu/bsd_x86/vm -I../generated -DHOTSPOT_RELEASE_VERSION="\"16.0-b08\"" -DHOTSPOT_BUILD_TARGET="\"product\"" -DHOTSPOT_BUILD_USER="\"mporges\"" -DHOTSPOT_LIB_ARCH=\"i386\" -DJRE_RELEASE_VERSION="\"1.7.0-internal-mporges_2009_09_23_22_44-b00\"" -DHOTSPOT_VM_DISTRO="\"OpenJDK\"" -DCOMPILER2 -DCOMPILER1 -fPIC -fno-rtti -fno-exceptions -pthread -fcheck-new -m32 -march=i586 -mstackrealign -pipe -O3 -fno-strict-aliasing -DVM_LITTLE_ENDIAN -Werror -Wpointer-arith -Wconversion -Wsign-compare    -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -c -o arguments.o /Users/mporges/bsd-port/hotspot/src/share/vm/runtime/arguments.cpp 
    cc1plus: warnings being treated as errors
    /Users/mporges/bsd-port/hotspot/src/share/vm/runtime/arguments.cpp: In static member function 'static void Arguments::set_aggressive_opts_flags()':
    /Users/mporges/bsd-port/hotspot/src/share/vm/runtime/arguments.cpp:1398: warning: format '%d' expects type 'int', but argument 3 has type 'intx'
    make[6]: *** [arguments.o] Error 1
    make[5]: *** [the_vm] Error 2
    make[4]: *** [product] Error 2
    make[3]: *** [generic_build2] Error 2
    make[2]: *** [product] Error 2
    make[1]: *** [hotspot-build] Error 2
    make: *** [build_product_image] Error 2

    A little Googling led to this thread on the OpenJDK mailing lists. Apparently there are some issues installing 1.7.0 on Snow Leopard, although one of the guys got it working by using gcc-4.0 and g++-4.0 rather than the 4.2.x versions installed on Snow Leopard.

    I don’t really feel like installing older gcc and g++ versions on to my Mac and modifying the build to use them, so I’m hoping they can get this addressed soon so that OpenJDK will build on the mostly-vanilla Snow Leopard distribution.

    2009.09.24 - Tags: Apple Coding / no responses / add a response

  • Using Axis’s wsdl2java in a Maven Build

    When using Apache Axis, you often want to generate the service stub code from a WSDL file provided by the remote API developer. We recently ran in to this situation at Highwinds.

    What we wanted to do was keep the WSDL in source control, and use Maven to build the source from the WSDL at build time during the generate-sources lifecycle hook. This seemingly simple task ended up taking me some time to figure out – not because it was hard, but because there are so many bad implementations of Axis plugins for Maven out there, and Google has all of them in its sights.

    Finally, I found a really nice implementation by the Codehaus guys. Here’s the POM for those of you who might be interested. Enjoy!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    
    <?xml version="1.0"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <name>Sample Axis-Maven Project</name>
      <groupId>com.maximporges</groupId>
      <artifactId>axis-maven</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <packaging>jar</packaging>
      <build>
        <plugins>
          <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>axistools-maven-plugin</artifactId>
            <version>1.3</version>
            <configuration>
              <!-- The plugin navigates your source src/main/** tree and finds your WSDLs for you; just name them individually in a <wsdlFiles/> element. -->
              <wsdlFiles>
                <wsdlFiles>MyWsdlFile.wsdl</wsdlFiles>
                <wsdlFiles>MyOtherWsdlFile.wsdl</wsdlFiles>
              </wsdlFiles>
              <!-- This is optional, but lets you explicitly namespace the generated code. -->
              <packageSpace>com.whatever.i.want</packageSpace>
            </configuration>
            <dependencies>
              <!-- Required for attachment support; you can remove these dependencies if attachment support is not needed. Note that if you do want it, you have to specify the dependencies both here in the plugin and also in the POM dependencies. -->
              <dependency>
                <groupId>javax.mail</groupId>
                <artifactId>mail</artifactId>
                <version>1.4.1</version>
              </dependency>
              <dependency>
                <groupId>javax.activation</groupId>
                <artifactId>activation</artifactId>
                <version>1.1</version>
              </dependency>
            </dependencies>
            <executions>
              <execution>
                <goals>
                  <goal>wsdl2java</goal>
                </goals>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
      <dependencies>
        <dependency>
          <groupId>org.apache.axis</groupId>
          <artifactId>axis</artifactId>
          <version>1.4</version>
        </dependency>
        <dependency>
          <groupId>javax.xml</groupId>
          <artifactId>jaxrpc-api</artifactId>
          <version>1.1</version>
        </dependency>
        <dependency>
          <groupId>javax.mail</groupId>
          <artifactId>mail</artifactId>
          <version>1.4.1</version>
        </dependency>
        <dependency>
          <groupId>javax.activation</groupId>
          <artifactId>activation</artifactId>
          <version>1.1</version>
        </dependency>
      </dependencies>
    </project>

    2009.09.23 - Tags: Coding / no responses / add a response