Sunday, 4 September 2011

From java.awt.print.Printable to PDF

When you want to convert a java.awt.print.Printable to a pdf file, your first course of action might be to Google for "java printable pdf". If you do that though, all you get is links about how to send a pdf file to the (hardware) printer in java or how to "read" a pdf file in Java. No one seemed to have written an article about how to convert a java.awt.print.Printable to a pdf file. This is that article.

The java code in this article requires the single itext jar to work. I used itext-5.0.2.jar, but I'm sure newer versions will also work.

Update: Some people informed me that as of version 5, itext does not have a business-friendly license anymore. Therefore, I also tested this solution with itext-2.1.7.jar and I am happy to report that this works just fine, the 2.1.7 jar is drop-in compatible. End of update.

This example works for A4 documents only, but once you understand the code, it is fairly trivial to adapt it to any size you want.

So, without further ado, here's the code! I'm going to explain 2 fundamental elements that make it work afterwards.

PdfPrinter.java:
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.Printable;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;

import com.itextpdf.text.Document;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.FontMapper;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfWriter;

/**
 * With this class, you can print a {@link Printable} to a pdf. You get the pdf as a byte-array, that can be stored in a file or sent as an email attachment.
 * 
 * @author G.J. Schouten
 *
 */
public class PdfPrinter {

    /**
     * Prints the given {@link Printable} to a pdf file and returns the result as a byte-array. The size of the document is A4.
     * 
     * @param printable       The {@link Printable} that has to be printed.
     * @param clone           A clone of the first {@link Printable}. Needed because internally, it must be printed twice and the Printable may keep state and may not be re-usable.
     * @param orientation     {@link PageFormat}.PORTRAIT or {@link PageFormat}.LANDSCAPE.
     * @return                A byte-array with the pdf file.
     */
    public static byte[] printToPdf(Printable printable, Printable clone, int orientation) {
        
        Rectangle pageSize;
        if(orientation == PageFormat.PORTRAIT) {
            pageSize = PageSize.A4;
        } else {
            pageSize = PageSize.A4.rotate();
        }
        
        //The bytes to be returned
        byte[] bytes = null;
        
        //We will count the number of pages that have to be printed
        int numberOfPages = 0;
        
        //We will print the document twice, once to count the number of pages and once for real
        for(int i=0; i<2; i++) {
            try {
                Printable usedPrintable = i==0 ? printable : clone; //First time, use the printable, second time, use the clone
                
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                
                Document document = new Document(pageSize); //This determines the size of the pdf document
                PdfWriter writer = PdfWriter.getInstance(document, bos);
                document.open();
                PdfContentByte contentByte = writer.getDirectContent();
                
                //These lines do not influence the pdf document, but are there to tell the Printable how to print
                double a4WidthInch = 8.26771654; //Equals 210mm
                double a4HeightInch = 11.6929134; //Equals 297mm
                Paper paper = new Paper();
                paper.setSize(a4WidthInch*72, a4HeightInch*72); //72 DPI
                paper.setImageableArea(72, 72, a4WidthInch*72 - 144, a4HeightInch*72 - 144); //1 inch margins
                PageFormat pageFormat = new PageFormat();
                pageFormat.setPaper(paper);
                pageFormat.setOrientation(orientation);
                
                float width = ((float) pageFormat.getWidth());
                float height = ((float) pageFormat.getHeight());
                
                //First time, don't use numberOfPages, since we don't know it yet
                for(int j=0; j < numberOfPages || i==0; j++) {
                    Graphics2D g2d = contentByte.createGraphics(width, height, new PdfFontMapper());
                    int pageReturn = usedPrintable.print(g2d, pageFormat, j);
                    g2d.dispose();
                    
                    //The page that we just printed, actually existed; we only know this afterwards
                    if(pageReturn == Printable.PAGE_EXISTS) {
                        document.newPage(); //We have to create a newPage for the next page, even if we don't yet know if it exists, hence the second run where we do know 
                        if(i == 0) { //First run, count the pages
                            numberOfPages++;
                        }
                    } else {
                        break;
                    }
                }
                document.close();
                writer.close();
                
                bytes = bos.toByteArray();
            } catch(Exception e) {
                //We expect no Exceptions, so any Exception that occurs is a technical one and should be a RuntimeException
                throw new RuntimeException(e);
            }
        }
        return bytes;
    }
    
    /**
     * This class maps {@link java.awt.Font}s to {@link com.itextpdf.text.pdf.BaseFont}s. It gets the fonts from files, so that the pdf looks identical on all platforms.
     * 
     * @author G.J. Schouten
     *
     */
    private static class PdfFontMapper implements FontMapper {

        public BaseFont awtToPdf(Font font) {
            
            try {
                if(font.getFamily().equalsIgnoreCase("Verdana")) {
                    if(font.isBold()) {
                        if(font.isItalic()) {
                            return this.getBaseFontFromFile("/META-INF/fonts/verdana/", "VERDANAZ.TTF");
                        }
                        return this.getBaseFontFromFile("/META-INF/fonts/verdana/", "VERDANAB.TTF");
                    } else if(font.isItalic()) {
                        return this.getBaseFontFromFile("/META-INF/fonts/verdana/", "VERDANAI.TTF");
                    } else {
                        return this.getBaseFontFromFile("/META-INF/fonts/verdana/", "VERDANA.TTF");
                    }
                } else { //Times new Roman is default
                    if(font.isBold()) {
                        if(font.isItalic()) {
                            return this.getBaseFontFromFile("/META-INF/fonts/timesnewroman/", "TIMESBI.TTF");
                        }
                        return this.getBaseFontFromFile("/META-INF/fonts/timesnewroman/", "TIMESBD.TTF");
                    } else if(font.isItalic()) {
                        return this.getBaseFontFromFile("/META-INF/fonts/timesnewroman/", "TIMESI.TTF");
                    } else {
                        return this.getBaseFontFromFile("/META-INF/fonts/timesnewroman/", "TIMES.TTF");
                    }
                }
                
            } catch(Exception e) {
                throw new RuntimeException(e);
            }
        }

        public Font pdfToAwt(BaseFont baseFont, int size) {
            
            throw new UnsupportedOperationException();
        }
        
        /**
         * To get a {@link BaseFont} from a file on the filesystem or in a jar. See: http://www.mail-archive.com/itext-questions@lists.sourceforge.net/msg02691.html
         * 
         * @param directory
         * @param filename
         * @return
         * @throws Exception
         */
        private BaseFont getBaseFontFromFile(String directory, String filename) throws Exception {
            
           InputStream is = null;
           try {
               is = PdfPrinter.class.getResourceAsStream(directory + filename);
    
               ByteArrayOutputStream bos = new ByteArrayOutputStream();
               byte[] buf = new byte[1024];
               while(true) {
                   int size = is.read(buf);
                   if(size < 0) {
                       break;
                   }
                   bos.write(buf, 0, size);
               }
               buf = bos.toByteArray();
               BaseFont bf = BaseFont.createFont(filename, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED, BaseFont.NOT_CACHED, buf, null);
               return bf;
           } finally {
               if(is != null) {
                   is.close();
               }
           }
        }
    }
}
FontCreator.java:
import java.awt.Font;
import java.io.InputStream;

/**
 * With this class, you can create a {@link java.awt.Font} from TTF-files that are packaged with the jar.
 * 
 * @author G.J. Schouten
 *
 */
public class FontCreator {

    public static Font createFont(String name, int style, int size) {

        try {
            Font baseFont;
            
            if(name.equalsIgnoreCase("Verdana")) {
                if(style == Font.BOLD) {
                    if(style == Font.ITALIC) {
                        baseFont = FontCreator.getBaseFontFromFile("/META-INF/fonts/verdana/", "VERDANAZ.TTF");
                    }
                    baseFont = FontCreator.getBaseFontFromFile("/META-INF/fonts/verdana/", "VERDANAB.TTF");
                } else if(style == Font.ITALIC) {
                    baseFont = FontCreator.getBaseFontFromFile("/META-INF/fonts/verdana/", "VERDANAI.TTF");
                } else {
                    baseFont = FontCreator.getBaseFontFromFile("/META-INF/fonts/verdana/", "VERDANA.TTF");
                }
            } else { //Times new Roman is default
                if(style == Font.BOLD) {
                    if(style == Font.ITALIC) {
                        baseFont = FontCreator.getBaseFontFromFile("/META-INF/fonts/timesnewroman/", "TIMESBI.TTF");
                    }
                    baseFont = FontCreator.getBaseFontFromFile("/META-INF/fonts/timesnewroman/", "TIMESBD.TTF");
                } else if(style == Font.ITALIC) {
                    baseFont = FontCreator.getBaseFontFromFile("/META-INF/fonts/timesnewroman/", "TIMESI.TTF");
                } else {
                    baseFont = FontCreator.getBaseFontFromFile("/META-INF/fonts/timesnewroman/", "TIMES.TTF");
                }
            }
            
            Font derivedFont = baseFont.deriveFont(style, size);
            return derivedFont;
            
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    /**
     * To get a {@link Font} from a file on the filesystem or in a jar.
     * 
     * @param directory
     * @param filename
     * @return
     * @throws Exception
     */
    private static Font getBaseFontFromFile(String directory, String filename) throws Exception {
        
       InputStream is = null;
       try {
           is = FontCreator.class.getResourceAsStream(directory + filename);
           
           Font font = Font.createFont(Font.TRUETYPE_FONT, is);
           return font;
       } finally {
           if(is != null) {
               is.close();
           }
       }
    }
}
Okay, so what makes this code work? If you've studied it, you will have noticed 2 things: the fact that we print the document twice and the fact that we get the fonts from files. I'm going to explain both of them.

Running the print twice
A characteristic of the java.awt.print.Printable class is that the return value of the print method tells the caller whether the print that was just requested (by calling the method) was actually valid (PAGE_EXISTS) or that the document has ended (NO_SUCH_PAGE). In other words, we only know whether a page existed after we've printed it. The Document class from itext however, requires a new page to be created before it is printed. So, we have to create a new page in the pdf Document before we know whether it even exists! This way, we always end up with a blank page at the end of a document. Therefore, the entire print sequence is run twice. Once to count the number of pages and once to use that number to prevent an extra page from being created. Granted, the first run didn't have to include everything, but since an extra millisecond didn't really matter in my case, I kept the code as short as possible. If you want to optimize it, then by all means do!

Getting the fonts from files
A nasty thing that I ran into was that itext treats fonts just a little bit different from how java.awt.print.Printable treats them. The widths and heights of the characters are just a little bit different between them, even if using the exact same parameters (style, size, bold/italic, etc). This leads to problems when you use FontMetrics.stringWidth(), for example.

The solution here is to make both of them not use their own fonts but the fonts from font-files that are packaged with the jar. In your implementation of java.awt.print.Printable, you have to use the FontCreator to create the java.awt.Fonts from the files instead of just using the java.awt.Font constructor. We also need the PdfFontMapper in the PdfPrinter to map those java.awt.Fonts to itext's own BaseFont class. The PdfFontMapper uses the same font-files to create the itext BaseFonts, so that itext and java.awt.print.Printable now use exactly the same fonts. Problem solved!

The code that retrieves the file names for the different fonts is a little duplicated between the two. If you have many fonts that you want to use, you may want to optimize this.

So that's it. Now, you can easily convert your java.awt.print.Printables to pdf files and use them for whatever you like!

Monday, 29 August 2011

Implicit Categories

A seemingly simple task such as putting items in categories can take more effort than you would think. I would like to share a really simple pattern that makes this a lot easier, but is only suitable for simple cases. Luckily, this covers a lot!

Like The Iterative Reference Model, this "pattern" is something that I discovered while programming. It made my life a lot easier on many occasions.

Normally, when you have an entity, say "BlogPost" and you want to group BlogPosts into "Categories", you would define a class named "BlogPost" and a class named "Category". Each Category then holds a list of BlogPosts. This is a perfectly good design, but in some cases it can be done a lot simpler.

Suppose that we know that each BlogPost will be in only one Category and that Categories do not contain other Categories. Then, instead of a class Category, we could simply give each BlogPost a String property that holds the name of the Category that this BlogPost is in. We don't have Category objects, just this String for each BlogPost. Then, the user interface can group the BlogPosts in Categories, while the server stays really simple. In fact, it doesn't even know about Categories. For the server, it's just a String property of BlogPost.

Of course, you don't want to type the Category for each BlogPost every time, because more often than not, you want to put a BlogPost in an existing Category. This is no problem. You can get a list of all Categories by doing a "SELECT DISTINCT BlogPost.category" on the database. This way, you can give the user a drop-down box with existing categories and a textfield for when he wants to specify a new one.

When applying this approach, it saves you a lot of CRUD functionality on Categories. In fact, when a Category is "empty", it will "automatically" disappear! You can write some smart SQL statements that take into account the category column for when you want to do things such as selecting or deleting all BlogPosts in a certain category.

Obviously, this approach is only suitable for simple cases, but even in the most complex applications, there will always be simple scenarios among the complex ones. This approach is not suitable if:

  • Items need to be in multiple categories. This is because you cannot store a list of Strings in a database row. You need a separate table for this and if you need a separate table anyway, you might as well opt for the fully fledged Category class.

  • Categories need metadata. Maybe a Category needs a creation date? In this case, the implicit category pattern will not help.

  • Categories need to be able to contain other categories (Container / Component pattern). This too, cannot be achieved with this approach.
So yeah, this is only for simple cases, but when you encounter ony of those, it makes your life a lot easier! Also, it is very easy to migrate this data to a fully fledged Category table at a later time.

Tuesday, 31 May 2011

The Zenbi Architecture

I would like to give you an insight into the Zenbi Architecture: a new Client-Server Architecture, built around RMI, JavaFX and Cloud Computing, that rivals J2EE in many aspects and has the best theoretically achievable scalability. I will discuss the technical aspects, compare the Zenbi Architecture to JEE and discuss the main benefits and drawbacks. This architecture is entirely built on Open Source components, so if you want to use it, you don't have to be afraid of costs.

I created the Zenbi Architecture for basically two reasons. First, during my experience at a large financial institution, where I was the technical lead of the J2EE Websphere team, I saw that software development was extremely complex and that the J2EE Architecture itself was also too complex for what it could provide. I wanted to create a new architecture that was aimed at mainly one thing: simplicity. The architecture should be easy to understand and developing and maintaining systems with it should also be easy. At the same time, it should provide all the things that J2EE was meant to offer: scalability, performance, security, robustness, etc.

The second motivation was the fact that more and more things start moving towards the Cloud. Google was and is one of the main driving forces behind this trend. My architecture should be suited for Cloud-based deployment, relieving users from needing a technical infrastructure.

Being a long-time Java fan, the architecture was going to be based on Java. Javier Paniza, a Spanish developer, blogged about Java and simplicity and said something that perfectly represented my feelings about Java vs. software development:

"The productivity in Java world is a cultural problem, not a technical one. That is not a Java fault, it's our fault, we, the Java developers, need to design very beautiful architectures, to apply everywhere the GoF patterns, to do everything reusable, to put 3 tiers in all our systems and to use web services for all. We are not looking for simplicity, therefore we have not found it. But, Java is a very elegant language that allows simpler approach to software development."

The name "Zenbi" is a Japanese word, that you can write in 2 different ways: the first means "Perfection and Completeness", the second means "The good and the beautiful". That was my aim.

Technology
The Zenbi Architecture is a completely outside-the-box design:

  • There is no application server or webserver. The server side consists out of a cluster of standalone Java applications
  • There is no SOAP/WSDL for communication
  • There is no HTML/JavaScript on the Client side
  • Everything is Java
The Zenbi Architecture is aimed at:

  • Simplicity
  • Scalability
  • Performance
  • Security
  • Robustness
The Zenbi Architecture uses the best industry-standard Open Source Java technologies:

  • JavaFX based Java Applets
  • Java 6
  • Spring
  • Hibernate
  • Dozer
The Zenbi Architecture is fully IPv6 compatible and provides the best scalability and robustness that is theoretically possible.

An architectural overview of the architecture is provided in the following picture (click to enlarge):


Now follows an explanation of the different components:

The Client
The Client is a Rich Internet Application, a JavaFX based Java Applet. Ever since the introduction of JavaFX (Java 6u10), the applet engine has improved, namely with the addition of the Java Deployment Toolkit. The most important thing about the Client is that the client holds the user-session! This is possible, because new (signed) Java Applets are actually first-class applications, despite being launched from the Cloud.

The RMI-Server
Server-side we have a cluster of RMI-Servers. An RMI-Server is a stateless standalone Java Application, that communicates with the Client via RMI. It is completely stateless, holds no session information and treats every request from a Client independently. If we do have to remember some state at the server-side, then we use the database for this, but this is extremely rare. The RMI-servers are 100% independent and know nothing of each other. Each one runs on a separate machine and has a separate ip-address. Because they are stateless, nothing has to be managed or replicated among the server instances.

The Communication
As you can see in the picture, the Applet Jar, that is started from the index.html file, receives a list of ip-addresses where the RMI-Servers are located. When the user logs in initially, the Client remembers the username and the password and uses those for each individual request. The server is stateless and authentication and authorization therefore has to be done each time. How does a Client pick a server? Easy, it just picks one at random. If it doesn't respond, it picks another. So what do we have here? Infinite scalability, load balancing and fail-over, all basically for free! And all possible because we have a Rich Client that holds the user session.

Actually, the list of ip-addresses is refreshed before each individual request, so that, in case of permanent server failure, that server can be removed from the list, so that it won't be tried again. Also, you can modify and migrate your RMI-Servers without the Client even having to restart their application. Each RMI-Server also has a weight, so that you can add heavier hardware later and that hardware will be used more often than the less powerful older servers. This weight is also specified in the text-file.

SSL is used for communication. The public key truststore is downloaded by the Client from the HTTP-Server.

A big advantage is that everything is Java! Client-side, Transport-side and Server-side. That means no XML/SOAP, no HTML/JavaScript and no mapping from one format to another. Developers only have to know Java. Also, we all know that input validation has to be done both Client-side and Server-side. With the Zenbi Architecture, the same code can be used at both sides. Furthermore, injection attacks are a thing of the past, since a strongly typed String is a strongly typed String, from Client to Server. And if you use prepared statements at the DB-side, injection is just impossible. In fact, this architecture is immune to the entire OWASP Top-10.

RMI Issues
Despite the advantages, RMI has 2 main drawbacks, for which I had to come up with a solution:

  • In RMI, when you serialize an object, all referred objects are also serialized. That means that if the Client requests one object, possibly thousands of other objects could be serialized as well, severely degrading performance.

  • If an Exception is thrown at Server-side, you don't want it to propagate to the Client, since the stacktrace could contain security sensitive information.
In order to solve these problems, we created a separate layer between the domain objects and the Client: The Transfermodel (click to enlarge):

Basically the Controllers specify all the methods that can be called by the Client. They call the DAO's to retrieve the necessary objects from the database and they return something from the domain-model. But as explained earlier, these domain objects are not suited to be sent over to the Client, because of the Serialization issues. They have to be mapped to the Transfermodel first.

The Transfermodel is designed to be sent over the line. So, it might not be the most beautiful OO-model, since that would mean lots of references, but objects of the Transfermodel (Transferobjects) are small and efficient. The Client does not actually call the Controllers, but rather the RMI-Controllers, which in turn call the Controllers and map the result to the Transfermodel using Dozer. If an Exception occurs, then the RMI-Controllers determine whether it is a functional exception (checked) or a technical one (unchecked), and a TransferError object is returned with the appropriate error message.

This means that the return type of all methods of the RMI-Controllers is GenericTransferObject, which can be a TransferError or another Transferobject from the TransferModel. It could also be a TransferVoid, if there is nothing to return.

Comparison to JEE

  • Simplicity: This architecture is far simpler than JEE. You don't need a complex application server anymore. Since there is only 1 Classloader, you never have Classloader issues anymore and because you have a Java-based Client, the developers don't have to deal with XML/SOAP/Namespaces/HTML/JavaScript/... anymore.

  • Performance: RMI over JRMP is much faster than text-based protocols, such as SOAP over HTTP. Also, there is no more marshalling involved and no more application server overhead. Starting and stopping instances is much faster too. These are two charts depicting the speeds of the various protocols. SOAP can be compared to XML-RPC. Clearly, RMI is superior:


    Also when it comes to local development, the performance advantage is clearly noticeable. No more lengthy server reboots and redeploys that can take from dozens of seconds to minutes. Both RMI-Server and Client start within 3 seconds (using Application and Applet run configurations in Eclipse). Compare that to this!

  • Robustness: This architecture is extremely robust, since every RMI-server is completely independent from the others. The Java application itself, the Operating System, even the hardware can be deployed redundantly. Every RMI-Server instance is completely separate from the others, does not even know about the others and is certainly not affected by a crash of one of the others, be it at hardware, OS or Java level. There is no single-point-of-failure between the Client and the Server. Also, the simplicity of the architecture and the absence of the application server layer make errors much less likely.

  • Scalability: Maybe the strongest point of the architecture, besides its simplicity. Because the cluster of RMI-Servers can be expanded infinitely, because there is no single-point-of-throughput between the Client and the Server and because there is no overhead associated with scaling up, this architecture provides the best theoretically possible scalability.
How could it be?
Many people will wonder how it is possible that a single person comes up with something that might be superior to JEE in several ways. It's simple: The entire JEE / J2EE architecture comes from a different time. It comes from a time when there was no Spring, no Hibernate, no Dozer, no C3P0, no Jakarta Commons, etc. The standalone platform just wasn't suited for enterprise development. Now, it is. And because of Java(FX) Applets, we can now hold user-state at the Client-side and do scalability, load balancing and fail-over at the Client-side, opening up new possibilities. All the problems an application server was designed to solve, can now be solved in another way, making the application server no longer necessary.

The Development Factory
Besides the architecture itself, I have also created a reference implementation in the form of a Skeleton System, that serves as a template for customer systems that can be created with the Zenbi Architecture. This Skeleton System already has role-based authorization, multi-language support and usage-statistics, so this doesn't have to be implemented separately for each system. Of course, a development factory needs a buildserver. I have described the Zenbi Buildserver here.

And finally, I have created a System Manager, that takes care of deploying the various applications to the actual servers. The RMI-Servers are deployed to in parallel, so that a new version can be brought online very fast. File-transfers to the servers are done via SFTP, so that it is safe for Cloud-based deployments across the Internet.

Drawbacks
Of course, no architecture is without drawbacks. This one too, has three drawbacks that I will discuss now:

  • First of all, a JavaFX Applet does not integrate very well with HTML based platforms, such as Google Maps, Facebook or YouTube. Since integration is increasingly important, this is a serious drawback. You have to consider whether this is important to you before choosing Rich Internet Applications.

  • The need for a Java Virtual Machine on the client computer. For most people, this is no problem, but large bureaucratic companies may have policies about this.

  • "Write Once, Run Everywhere", the paradigm that made Java great, is dead. Completely and utterly dead. The cause? Smartphones and Tablets. These two are powerful hand-held computers now and their capabilities will keep increasing. Users will more and more use them as replacements for PCs. Still, none of these devices support Java SE, which is necessary to run the Client. Most of them (i.e. all except Apple) support proprietary versions of Java ME, that allow them to run Java-based fat clients, but Java Applets are not supported and I don't think they ever will be. Oracle does not seem to give it much priority and while it is a drawback of the Zenbi Architecture, it is a complete fiasco for Java.
Conclusion
I hope this article gives you a good understanding of how the Zenbi Architecture works and the benefits it provides. In the end, my primary aim was simplicity, since that is the base of all other good things. If you have any further questions or if you like to know more about this architecture, you can of course always contact me.

Perfection is achieved
not when there is nothing more to add,
but when there is nothing left to take away

- Antoine de Saint-Exupéry -

Wednesday, 16 March 2011

If Programming Languages ran the Airlines...

We all know the hilarious page titled "If Operating Systems ran the airlines". If you don't know it, you can find it here.

I thought it was time for another version, with Programming Languages instead of Operating Systems. Enjoy!

PL/1 Mainframe Air:
You arrive at the airport. It's not really an airport, but actually an old wooden building next to the river. You ask why there isn't a real airport. A very old man answers you that they have been building with wood ever since the beginning of construction, so it must be good. You ask where you can check in and when your plane leaves, but you are answered that they really don't have any planes, because they think planes are too modern. Instead, you must place your luggage and yourself into a rowing boat in the river. This is because people have been using rowing boats for centuries, so rowing boats have proven that they work very good. You argue that a rowing boat can't possibly take you to your destination 2000 miles away, but the old man insists that you try. After all, the rowing boat has never let HIM down. The fact that he only ever went as far as 2 miles up the river can't convince him. In the end, with no choice left, you decide to give it a try. At first, all goes quite well. The old man can steer the rowing boat very fast down the river, but when you finally arrive at sea, the old man has a heart-attack and dies. You are now in the middle of the ocean, with nothing but a pair of paddles. Good luck.

C++ Air:
When you enter the airport, there are 5 entrances. You walk towards one, but then someone warns you that, if you choose one, you can never switch back and everything, including the destination of your journey, will depend on it. After thorough consideration, you find the entrance that is best for you. You go to a checkin terminal to check in, and you receive a ticket with everything from you name and address to the name of your dog (which you left home) and the contents of your wardrobe. You ask why there is so much information that isn't necessary for the flight and you are answered that this is good, because then, you are in complete control of what you are doing. When you sit down at a table at a restaurant, the waiter won't bring you anything, because you have the wrong flight-ticket. If you ask what this has to do with getting food, you are told that you should have thought of this before you chose a ticket. A bit confused, you enter the airplane. You are given a meal with a couple of slices of bread and a samurai-sword to cut them. Around you, you see everyone try to slice the bread, while accidentally cutting of their own limbs and fingers. You ask the man next to you why they don't just give you a normal knife to slice your bread and you tell him that swords are very dangerous, but the man says that only a samurai sword is sharp enough to slice bread and that you are stupid and a noob if you can't do it. At least, the airplane is very very fast and you get to your destination in a very short time, but when you approach the destination airport, the pilot receives a message that the landing airstrip has changed. Because the airplane is unable to change the destination landing strip after take-off, the pilot returns to the airport where it left from and will have to start the flight all over again.

PHP Air:
When you arrive at the airport, you see a lot of people dragging parts of airplanes around. When you ask why this is, a man says that you need airplanes in order to fly, so that's why they are building them. You ask if there aren't any pre-made planes that have passed security tests, but the man has already disappeared with a big wing under his arm. Once you board your airplane (the left wing is still not finished, but the stewardess promises that it will be before take-off), you meet a little kid. You ask him if this is his first flight, but it appears that he is actually the pilot of the plane. The kid tells you all about the fact that he has played with toy-airplanes when he was a baby and that he has a real pilot's uniform, so he is more than qualified to fly the plane. When the plane takes off, there is a lot of turbulence, but after a while, it gets better and the plane is on it's way. When you fly above the ocean, the plane is suddenly hit by a thunderstorm. The little kid gets a little frightened, but he tries his best to save the situation. When more and more people start to panic, the little kid begins to cry and gives up. You try to steer the plane yourself, but there is no usermanual anywhere to be found. When the plane heads towards crashing in the ocean, you look outside the window and you see a man screaming in a rowing boat. At least, you will not go down alone...

.NET Airlines:
You arrive at a very modern terminal. There is only one counter where you can check in, but you don't have to wait and you are helped by a very friendly woman. After checking in, you decide to get something to eat. There is only one restaurant, and it's pretty expensive, but the food is very good, so you don't mind. You are guided to your plane by another very friendly stewardess. There is only one corridor through which you can walk, so you don't really need the help, but on the other hand, it's quite comfortable. Your plane is the only plane at the airport, but it's a very nice one, with very nice chairs and a wonderful in-flight dinner. After about 20 minutes in the air, you land at exactly the same airport as where you left from, because it's the only airport where .NET airplanes can land.

Java Air:
You arrive at the airport with all your luggage. It's kinda heavy, so you sigh: "I wish I had someone to carry it for me..." Immediately, out of all corners of the terminal, people start running towards you, offering you their services. Some ask a little money, but most of them do it for free, because they like hauling with luggage. At first, you are totally overwhelmed by this many people offering you stuff, but after a while, you get to know some of them and they are quite nice. When arriving at your airplane, there is not one, but five, all totally different, but they are all airplanes and they all bring you where you want to go. Some of them are even free. You see some people, especially people with suits and ties, who don't trust all the free airplanes and are anxious to choose from so many options. They all walk into a big blue building. You hear that you can let the people inside the blue building do everything for you and make the choices for you, if you pay them enough, but since you're a little short on cash and also because you never actually see anyone come OUT of the blue building again, you decide to fly with one of the free airplanes. After a pleasant flight, you arrive at your destination. You try to convince your friends to travel with Java Air too, but all they can say is: "Was it FREE??? Then it cannot be good..."

Ruby Air (courtesy of Ryan Daum):
You arrive at the airport, which is actually a nightclub. There is a band playing at the check-in desk. They are playing music which sounds like New Wave from the early 80s, but the band is made up of people born after 1975. You swear that the woman at the ticket counter looks like Adele Goldberg, but she just looks at you funnily, and won't let you past until you exchange your Dell laptop for a MacBook Pro. You are told the only place the plane will land is Portland. The interior of the plane is retro-chic, and the pilot has piercings and spikey hair. After take-off, the landing gear of the plane won't retract, and is missing oxygen masks, but the pilot says that's O.K., because when he build the plane he used unit tests. Halfway through the flight, the plane runs out of fuel, and you are all forced to transfer onto a new plane after a brief landing on a pacific island. You see Jack Sheppard on the island.

JavaFX Air (courtesy of Sven Hafner):
This one is an offspring of a very traditional airline which is around for some ten years, with a good safety record, but aircraft available in grey color and flying to all business destinations. The new sibling starting up is very colorful and supposed to fly to hip and colorful holiday destinations using whatever aircraft you like, including hot air balloons, surfboards and submarines, though it doesn't offer flights on freely available aircraft (not yet). You buy a ticket with fancy 3D holograms printed on it and the flight attendants are not walking along the aisle but sliding in from the side. Before you board you can easily choose the color scheme of the plane, but then you need to paint it by yourself because there is no one doing that for your with fancy tools. It is fun flying with them. They are competing with the other budget airlines, Silverlight Air and Flex Air.

Erlang Air (courtesy of Zubin Wadia):
You arrive at a fairly rudimentary airport on your way to Tokyo. Only Herring is available in the food court, along with bottled water. No explanation is given in regards to the dearth of options. Stranger still, all the staff appear to have very restricted linguistic skills coupled with awkward inflections in their speech. Regardless, you figure out how to check in and get on the plane. Predictably, more Herring is served. Unfortunately, the plane hits an air pocket, resulting in a massive drop in altitude, in turn leading to the engines burning out. Strangely, all the staff are calm, they move to the exit doors and open them mid-flight... Absolute chaos reigns momentarily as we get sucked out of the aircraft and into thin air. Just as suddenly, we’re back in an aircraft, it appears to be the same one, we all have the same seats and we’re still heading to Tokyo.