Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

Friday, July 12, 2013

Android Advanced Cursors

If you have been doing Android development for even a little while, you have probably used the provided SQLite functionality to store and retrieve data in a local database on the Android device.

If not, you can head over to Lars Vogel's excellent tutorial website to see how it all works, or to get a refresher.

For many applications, the basic Cursor is all you need. But what about more advanced requirements? What follows is an overview of some of the more advanced cursor options.

CursorJoiner

If you need data from two or more database tables, the most straightforward thing to do is to use a SQL query that joins the two tables. However, sometimes you already have sources for two separate queries (such as a CursorLoader based on a ContentProvider), and you may wish to just re-use them, without creating a new query (without, e.g., modifying the ContentProvider).

This is where CursorJoiner comes in handy. Unlike the other options listed below, CursorJoiner doesn't produce a new cursor. Instead, it provides an Iterable interface over the joined cursors.

If the only reason that you want to join the two tables is to produce a Cursor, say, for use by a CursorAdapter, then you should probably just stick with writing SQL that joins the two tables. However, if you just want to iterate the data once, then CursorJoiner can be very helpful.

One of the more interesting uses that I've seen for CursorJoiner is for determining what has changed when a LoaderManager.LoaderCallbacks<Cursor> implementation gets an updated Cursor in it's onLoadFinished method.

You can use the CursorJoiner to join the 'old' cursor and the 'new' cursor on their primary key, and then compare them. As you iterate the CursorJoiner, you get a CursorJoiner.Result enum that indicates whether the current row exits in the left cursor, the right cursor, or both.

Here is some sample code that determines what has been added to or deleted from a ContentProvider as a CursorLoader driving a CursorAdapter is updated:

   
@Override
public void onLoadFinished(Loader<cursor> loader, Cursor updatedCursor) {
   if(adapter.getCursor() != null) {
      Cursor oldCursor = adapter.getCursor();
      CursorJoiner joiner = 
         new CursorJoiner(oldCursor, 
             new String[] {Thing.ID},
             updatedCursor,
             new String[] {Thing.ID}
      );
            
      for(CursorJoiner.Result joinerResult : joiner) {
         switch (joinerResult) {

            case RIGHT:
               int thingId = updatedCursor.getInt(
                  updatedCursor.getColumnIndex(Thing.ID)
               );
               
               String thingName = updatedCursor.getString(
                  updatedCursor.getColumnIndex(Thing.NAME)
               );
                      
               Log.d(TAG, "new Thing added - id: " + thingId 
                    + ", name: " + thingName);
            break;

            case LEFT:
               int thingId = updatedCursor.getInt(
                  oldCursor.getColumnIndex(Thing.ID)
               );
               
               String thingName = updatedCursor.getString(
                  oldCursor.getColumnIndex(Thing.NAME)
               );
                      
               Log.d(TAG, "Thing deleted - id: " 
                    + thingId + ", name: " + thingName);
            break;

            case BOTH:
               int thingId = updatedCursor.getInt(
                  oldCursor.getColumnIndex(Thing.ID)
               );
               
               String thingName = updatedCursor.getString(
                  oldCursor.getColumnIndex(Thing.NAME)
               );  

               Log.d(TAG, "Thing unchanged - id: " 
                    + thingId + ", name: " + thingName);
            break;                                   
      }
   }        
   updatedCursor.moveToFirst();
   adapter.swapCursor(updatedCursor);
}


MatrixCursor

MatrixCursor is useful if you have a collection of data that is not in the database, and you want to create a cursor for it.  You simply construct it with an array of column names, and optionally an initial capacity. Then, you can add one row at a time to it by passing either an array of objects or an Iterable to its addRow() method. Then use it like any other cursor:
  
String[] columnNames = {"Column1", "Column2"};
MatrixCursor matrixCursor = new MatrixCursor(columnNames);
matrixCursor.addRow(new String[]{"value1", "value2"});
adapter.swapCursor(matrixCursor);

MergeCursor

MergeCursor allows you to present two or more cursors as a single cursor. You could get almost the same effect by doing a sql UNION query. However, MergeCursor allows you to have different columns in the various cursors, and still merge them.

This is very useful if you are presenting a list of heterogeneous items. I've used it with Emil Sjölander's StickyListHeaders library, using a different cursor for each 'section', all presented to the StickyListHeader implementation as a single cursor via a MergeCursor.

One thing to be aware of when presenting heterogeneous items through a MergeCursor - you'll probably want to add a pseudo-column to each of the cursors to make it easy for your ListAdapter (or whatever will be using the cursor) to be able to determine which type of data it's dealing with on each row. A seemingly little-known trick here is that you can include literal columns when creating a cursor using the ContentProvider's query method (and similar methods in other database classes). Just include the literal in the projection parameter to the query:
  
String projection = {Thing.ID, Thing.NAME, "'TYPE_THING' as TYPE"};
Cursor c = qBuilder.query(mDb,
                projection,
                selection,
                selectionArgs,
                groupBy,
                having,
                sortOrder);

CursorWrapper

Finally, CursorWrapper is a Wrapper class for Cursor that delegates all of its calls to the actual cursor object. As the documentation stated, the primary use for this class is to extend a cursor while overriding only a subset of its methods.

One example for the use of CursorWrapper is when you already have a cursor, but only want to present the first few rows of the cursor to a CursorAdapter. Override the getCount() method to return a number smaller than the actual cursor's count, and the adapter will only present that number of results. This is similar to what you would have gotten had you added a LIMIT clause to the sql that produced the cursor.

With this collection of advanced Cursor tricks, you should be able to accomplish whatever you want with Cursors and Adapters.

Wednesday, January 30, 2013

HTTPS with Client Certificates on Android

Many Android applications use REST or another HTTP based protocol to communicate with a server.

Working with HTTP and HTTPS on Android is generally fairly straightforward and well documented. Depending on the version of the Android OS, either HTTPClient or HttpURLConnection “just work”. Either one can be used, but the official recommendation is to use HttpURLConnection for Gingerbread or later, and HTTPClient for Froyo and earlier.

Things get interesting, however, once you go past “plain vanilla” HTTPS. On a recent project, we needed to communicate with an HTTPS server that required client certificates, and which used a self-signed server certificate. This presented some interesting challenges. The rest of this post describes how we met those challenges.

If you want to follow along by trying out the code, it’s available on GitHub. You will, of course, have to set up your own HTTPS server and your own server and client certificates. One way that you can do this is by using the Apache mod_ssl module.

Before starting out with using HTTP or HTTPS on Android, make sure that your application has permission to access the network by adding the following to your AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET"/>

Challenge #1: Using a Client Certificate

Our client certificate was issued in the PKCS 12 format, as a .p12 file.

To give our application access to the certificate, we used the DDMS utility to copy the certificate file to the root directory of the phone’s sdcard. If you are using an emulator, you can do the same thing to copy the certificate to the emulator’s sdcard.

You can also choose to mount the phone as a disk drive when you plug in the USB cable, and copy the file that way.

In our final application, we “imported” the certificate by copying it to application-specific storage, and then deleting the original from the sdcard root directory.

The secret to using our client certificate is setting up a custom KeyStore containing the certificate, and then using it to create a custom SSLContext.

Once we have a reference to a File containing the client certificate and the password for the certificate, we load it into an appropriate KeyStore (see the sample SSLContextFactory.java):

keyStore = KeyStore.getInstance("PKCS12");
fis = new FileInputStream(certificateFile);
keyStore.load(fis, clientCertPassword.toCharArray());

Now that we have the KeyStore containing the client certificate, we can use it to build an SSLContext:

KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
kmf.init(keyStore, clientCertPassword.toCharArray());
KeyManager[] keyManagers = kmf.getKeyManagers();

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, null, null);

The SSLContext can then be used with an HTTPUrlConnection to connect to the server:

String result = null;

HttpURLConnection urlConnection = null;
try {
    URL requestedUrl = new URL(url);
    urlConnection = (HttpURLConnection) requestedUrl.openConnection();
    if(urlConnection instanceof HttpsURLConnection) {
        ((HttpsURLConnection)urlConnection).setSSLSocketFactory(sslContext.getSocketFactory());
    }
    urlConnection.setRequestMethod("GET");
    urlConnection.setConnectTimeout(1500);
    urlConnection.setReadTimeout(1500);

    lastResponseCode = urlConnection.getResponseCode();
    result = IOUtil.readFully(urlConnection.getInputStream());
    lastContentType = urlConnection.getContentType();

} catch(Exception ex) {
    result = ex.toString();
} finally {
    if(urlConnection != null) {
        urlConnection.disconnect();
    }
}

Challenge #2: Trusting a Self-Signed Server Certificate

We now have Android client code that can connect to an HTTPS server and present a client certificate.

However, this only works if the server’s certificate is trusted. In practice, this means that the server certificate must be signed by one of the major certificate authorities, such as VeriSign, Thawte, Geotrust, Comodo, etc. CA certificates for these well-known providers are pre-loaded into the phone’s default trust store.

In our case, the certificate was self-signed. This means that the default TrustManager in our SSLContext will not trust the server’s certificate, and the SSL connection will fail.

If you search the web for ways to trust self-signed server certificates, you’ll likely see a lot of really bad advice. Most responses to this sort of question involve setting up a custom TrustManager that simply trusts everything. This approach is both pointless and obviously insecure.

What we really want to do is to set up a custom TrustManager that trusts our self-signed certificate, and provide that TrustManager to our custom SSLContext. To do this, we need a copy of the server’s certificate chain, which will have to include at least the self-signed CA Certificate and the Intermediate Certificate that is signed by the CA. If you simply export the server’s certificate, you will actually get a file with three certificates - the CA certificate, the Intermediate Certificate, and the server’s certificate. This is fine - the extra certificate is not needed, but won’t hurt, either (usually - see Challenge #3 below).

The method for trusting the self-signed server certificate is very similar to the method for providing a client certificate - we load the certificate into a KeyStore, use that KeyStore to produce an array of TrustManagers, and then use those TrustManagers to create the SSLContext.

In our app, we included the server certificate file in the application resources (since it doesn’t change from user to user, and doesn’t change very often), but you could put this in an external file as well.

Here’s the code for loading server certificate chain into a KeyStore that will be used as a “trust store”, based on having the base-64-encoded (.pem) certificate as a String:

byte[] der = loadPemCertificate(new ByteArrayInputStream(certificateString.getBytes()));
ByteArrayInputStream derInputStream = new ByteArrayInputStream(der);
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(derInputStream);
String alias = cert.getSubjectX500Principal().getName();

KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null);
trustStore.setCertificateEntry(alias, cert);

Now that we have the “trustStore” KeyStore with the server’s certificate, we use it to initialize the SSLContext. Adding to the code that we had before, production of the SSLContext now becomes:

KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
kmf.init(keyStore, clientCertPassword.toCharArray());
KeyManager[] keyManagers = kmf.getKeyManagers();

TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(trustStore);
TrustManager[] trustManagers = tmf.getTrustManagers();

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, null);

Now, we can securely connect to our server, trust its certificate (but not others), and present our client certificate. If all goes well, you’re done, and can safely stop reading.

Challenge #3: Handling an Improperly Ordered Server Certificate Chain

As it turns out, some Apache mod_ssl installations (and possibly other SSL providers), whether due to a bug or mis-configuration, provide the server certificate chain in the wrong order.

To work properly, the certificates in the server’s certificate chain must start with the “root”, or CA certificate, followed by any intermediate certificates. If the server’s certificate is included, it must come last. Each certificate in the chain (other than the root) must be preceded by the certificate that was used to sign it.

In our case, a mis-configured server was producing a certificate chain that started with the root CA certificate, but which had the server’s certificate next, followed by the intermediate certificate. This caused the SSL handshake to fail.

To work properly, the certificates in the server’s certificate chain must start with the server certificare, followed by any intermediate certificates. If the “root”, or CA certificate, is included, it must come last. Each certificate in the chain (other than the root) must be followed by the certificate that was used to sign it.

In our case, a mis-configured server was producing a certificate chain that started with the server certificate, but which had the root CA certificate next, followed by the intermediate certificate. This caused the SSL handshake to fail.

The most straightforward thing to do in this situation, if you can, is to correct the server’s certificate chain. If you can’t, then the out-of-order certificate chain must be handled by the client.

In our Android app, we handled the out-of-order certificate chain by implementing a custom X509TrustManager that re-orders the chain.

The code for the custom X509TrustManager is too large to include here (see the full source on GitHub), but once we have it implemented, we use it in the creation of the SSLContext by replacing the ‘trustStores’ array with one containing the custom implementation:

KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
kmf.init(keyStore, clientCertPassword.toCharArray());
KeyManager[] keyManagers = kmf.getKeyManagers();

TrustManager[] trustManagers = {new CustomTrustManager(trustStore)};

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, null);

By loading our own certificates into our keystore and trust store, and re-ordering the server’s certificate chain, we were able to provide an SSLContext that let us connect to an SSL server with client a certificate and a badly-ordered, self-signed server certificate chain.

Friday, December 16, 2011

OpEd: Yammering about Scala, Java, and Winning

This is an opinion piece that represents the views of the author, and does not represent the official stance of Chariot Solutions. But sometimes it is fun to weigh in on the news of the day.

There has been quite a dust up in the blogosphere about Yammer's decision to switch from using Scala to Java. Google "yammer switching to scala" and you'll get a sampling. You can read it for yourself, but the gist of story seems to be that Yammer, the self described "enterprise social network" e.g. private label Twitter, decided to move their development from Scala to Java. The Scala guys at Typesafe heard about this (disclosure: Chariot Solutions is a Typesafe partner), and privately asked Coda Hale, infrastructure architect at Yammer, the reasoning behind the move. Coda responded in another private-but-soon-made-public email. It is a very long and detailed email, but in summary:
"The essence of it is that the friction and complexity that
comes with using Scala instead of Java isn't offset by enough productivity
benefit or reduction of maintenance burden for it to make sense as our default
language."
Then Hale clarified the purpose of his email and Yammer clarified their official position on Scala.  Yammer's decision has (re)ignited the debate over whether Scala is better or worse than Java.  If you read the comments section of this blog post from Stephen Colebourne, you get the general gist but here is a sampling that I can reprint and still keep this blog "family friendly":

In favor of Scala:
Some pretty serious masochists out there at Yammer. I wonder if their investors now about it.

Not in favor of Scala:
The code and build process was a rat's nest of pain and people should avoid using scala at all costs on any commercial project where these things can become a big issue.
I've used and learned lots of different languages but can't recall anything more painful than scala. Cross compiling and maintaining c codebases with native ui on different Linux/winblows envs was easier and less traumatizing than this. It's fine for academic pursuits and personal projects but you're smoking some serious crack to try and justify it commercially.

Opinion #1: Java is winning (like Tebow not Sheen)

The biggest thing that I take away from this whole incident is that Java is winning.  I don't mean Java is winning vs Scala or that Java is better than Scala.  I mean that Java is, in my opinion, an aging language with many quirks, with a fairly dysfunctional governing body in the JCP, and a yet-to-be-fully-trusted new caretaker in Oracle, full of the baggage of vendor compromises which nearly ruined JEE, and faces many very strong competitors on the JVM itself like Scala, Groovy, and Clojure, and yet -- against all odds and in very Tim Tebowesque form -- is winning over highly skilled engineering shops like Yammer.

I've been a Java programmer for 10+ years and there are many things about Java that irritate me like the slew of boilerplate code that you need in every single class.  But while innovation in the language itself can only go so far while still maintaining backward compatibility, innovation in the wider Java community is still thriving.  Java may not be the best language out there, but the battle tested JVM runtime combined with the countless mature open source tools and frameworks make the Java ecosystem as a whole arguably the richest one on the planet.

For a while I was convinced Java was the new Cobol: a language past its prime which due to its huge popularity will never die out completely, but not on the short list of candidate languages for a green field project.  Now I've changed my mind.  Java has its warts but there is value in maturity, and it still can compete head to head with any language ecosystem out there.

Opinion #2: Scala is winning too

Yes, Yammer moved from Java to Scala, but they make explicitly clear that they like Scala too.  It is just that Java make more sense for them and their specific business needs.  Yammer was able to build an impressive high volume system with a small engineering team using Scala.  One shop moving away from Scala does not constitute a trend.  Scala has a relatively young ecosystem but already some very impressive open source projects like Akka.  I expect the combination of Jonas Boner and Martin Odersky together in a single company to provide a real boost to Scala.

I've never used Scala on a real project but I'm in love with the syntax and what you can do with the language.  I know people who use it all the time and love it as well.  Scala is undeniably more complex than Java but with the complexity comes more power.  Scala isn't better or worse than Java.  It is just different.

Opinion #3: Polygot is hard

I really like the idea of polygot programming: pick the programming language that fits your problem space.  I worked with many large clients that solve every problem with the same Java stack even when it is obviously a bad fit.  But one of the things that Hale mentioned in his email was that the choice for Yammer wasn't between Scala and Java, it was between Scala/Java and Java.  The context switching caused by thinking in multiple languages was a drag on the team.  When you think about it, a programmer is required to know many languages just to build a basic web app using a Java stack: Java, SQL, HTML, Javascript, and CSS.  Adding more languages to the mix makes things that much more complex.  Then you also include build tools, language idioms, and the ecosystem of libraries and frameworks.

I work in both Java and Groovy, and I find those languages similar enough to minimize the difficulty of context switching, but I can imagine switching between diverse languages like Java and Clojure might make my head explode.

Opinion #4: Coda Hale is a humorous writer

Not everyone can write about techie stuff and make it humorous and informative.  Here is a quote from his blog post that made me chuckle:

"The Scala community needs another giant blog post about ways in which someone doesn’t like Scala like I need a hole in my head, and I’d rather suck a dog’s nose dry than lend a hand to the nerd slapfights on Hacker News."

I was pleasantly surprised to find that Hale is speaking at my company's annual technology conference Philly ETE.  That's one session I'll be attending.

And I might just ask him if Scala is better than Java :-)

Tuesday, December 28, 2010

Painless Java Desktop Application Development with Griffon, MigLayout, and IntelliJ

As Java consultants and developers, a very large percentage our projects are web applications.
We have become accustomed to depending on MVC web frameworks, and almost take for granted that at some level, we will be handling HTTPServletRequests, providing HTTPServletResponses, and deploying our applications to either a servlet container or a full J2EE server.

Every once in a while, however, the need arises to write an old-fashioned, desktop, thick-ui, non-web-centric application. If you have ever written a Java Swing application, you may have painful memories of dealing with things like BorderLayouts, GridBags, Event Listeners and Event Dispatching Threads, JFrames, and the like.

As a web developer, when you think about building your desktop application, you might start to think about how you will structure your application, and how you will build and package it. Will you have to re-package your code and all of your dependencies in one giant uber-jar?
Will you make it a web-start application? Fortunately, some folks have been thinking about, and working on these issues.

Griffon is a Groovy-based MVC framework for writing desktop applications. It is written by the Groovy Swing team, and is heavily influenced by Grails. If you have worked with Grails, then Griffon will be a very familiar environment. Griffon answers all of the above questions, and more.

Using the convention-over-configuration and command-line-driven scaffolding paradigms made popular by Ruby on Rails, Django, and Groovy on Grails, among others, Griffon generates the project structure, the model, view, and controller objects, and all of the configuration for you. You then customize these to produce your application.

Working in Groovy, of course, makes this a much more pleasurable experience, dispensing with the ceremony of Java development, and letting you write high-level, fairly terse, yet expressive code, and enabling you to accomplish quite a bit very quickly. The prime example of this in the Griffon framework is the SwingBuilder object - a Groovy Builder that provides a DSL for building Swing user interfaces. Views in Griffon are SwingBuilder scripts, and greatly simplify the task of creating Swing UIs.

Griffon uses the Gant build system (as does Grails), and by default, packages your application with all its dependencies as an uber-jar (with the correct manifest entries to make it 'executable'), as an applet, and as a java-webstart application.

When working with Swing, even with the SwingBuilder, one potentially still has to deal with the frustration of laying out the user interface by using a LayoutManager. GridBagLayout is flexible (too flexible?), but about as complicated as it can get. BorderLayout is fairly simple, but the simplicity comes at a cost - you typically wind up nesting lots of BorderLayouts in other BorderLayouts to get the desired behavior. Over the years, alternate layout managers have appeared, and some are quite useful, most notably the JGoodies Form Layout and RiverLayout. I tried MigLayout with my Griffon project, and found it to be very flexible and easy to use.

Here is a screenshot of the main UI of my application:
ServiceTester

As mentioned previously, the View is a Groovy SwingBuilder script. The first part of the script sets up an 'application' - the application's "MainFrame", which Griffon interprets according to the runtime environment - for a stand-alone application, it will build a JFrame, and for an applet it will build a JApplet:

package servicetester

import java.awt.FlowLayout
import net.miginfocom.swing.MigLayout

application(title: 'Service Tester', minimumSize: [297, 200], maximumSize: [297,200], location: [50, 50], pack: true, locationByPlatform: true) {
Note that minimumSize, maximumSize, location, etc. are set as properties here. In straight Java Swing code, these would have been at least one line of code each, and several would have involved building Dimension objects.

Next is setup of the actions and the menu bar that uses the actions, in this case, a simple 'File' menu with a 'Quit' menu item to exit the application.
Note the use of Groovy closures for associating the Action object with implementation of the action.

actions {

    action(id: 'exitAction',
            name: 'Quit',
            closure: { System.exit(1) },
            mnemonic: 'Q',
            accelerator: shortcut('Q'),
            shortDescription: 'Quit'
    )
  }

  menuBar(id: 'menuBar') {

    menu(text: 'File', mnemonic: 'F') {
      menuItem(exitAction)
    }

  }

I chose to use the MigLayout for the main panel of the view. There are lots of ways to initialize a MigLayout, I used the "fill" parameter to the constructor, which tells it to grow to occupy all available space, which makes sense here because it is the top-level component in the frame.
panel(border: emptyBorder(12), layout: new MigLayout('fill')) {

As you can see in the screenshot, there are three groupings of controls - the group of fields above the separator (the input fields), the group of fields below the separator (the output fields), and the "button bar" at the bottom (only one button). To achieve these groupings, I added two panels, each with MigLayout initialized with "fill", as with the main frame, and a third panel with a FlowLayout for the button bar.
These MigLayouts are given constraints of "growy" and "wrap":

panel(layout: new MigLayout('fill'), 
          constraints: "growy, wrap") {
"growy" means "grow vertically", and "wrap" means that the next component should be placed below, instead of to the right.
With this configuration, the MigLayout in the main frame gives the button bar it's preferred size, and resizes the other two panels as the main window is resized, giving equal weight to them.

The two MigLayout panels are very similar. Each has sets of label / input field pairs, which look like:
label(text: 'AMQ Broker URL:')
      textField(
              id: 'brokerUrl',
              columns: 40,
              text: bind(target: model, targetProperty: 'brokerUrl', value: model.brokerUrl),
              enabled: bind {model.enabled},
              constraints: 'wrap'
      )
The "text:bind" property binds the value entered in the textField to a property in the model - the value entered by the user is placed into the model.

One big "gotcha" here is that bi-directional binding is not supported. It is possible to bind in each direction, but not both directions at the same time. As one might expect, lots of people are asking for this capability, so I wouldn't be surprised to see it show up soon. I ran into this when trying to set the initial value of the field from the value in the model. The answer for this case is to use the "value" property as shown above - it gives the field it's initial value, and does not participate in the binding mechanism, so it doesn't present the same problem as bi-directional binding. If you do truly need bi-directional binding, at the moment, you'll have to roll your own.

Here is the full source code for this View.

Due to its highly-developed sense of structure and general lack of need for configuration, working on a Griffon application using the command line and a text editor is very straightforward. However, IDE support is always nice. Eclipse does not directly support Griffon, but does support Groovy. Both NetBeans and IntelliJ have direct support for Griffon projects. IntelliJ 9 and 10 have direct support for creating, importing, testing, running, and debugging Griffon applications, and I found these features to work quite well.

I found the combination of Groovy, Griffon, MigLayout, and IntelliJ to be an extremely productive environment for creating Swing applications, providing much of the application infrastructure, allowing me to dispense with ceremony and concentrate on writing the parts of the application that make it unique. Before embarking on a large or mission-critical Griffon project, however, you do need to be aware that Griffon is still in Beta (0.9.2-beta-3 was just released on Dec. 21st, 2010), and so is still in a bit of a state of flux.

If you are interested in reading about Griffon, there's documenatation at at the Griffon home page, and the Griffon In Action book is available from Manning.

Monday, January 4, 2010

Where is Java Headed?

Welcome to 2010!

As a way to start the new year, Joel Confino, one of our architects and future blog contributor, sat down for an interview to discuss his opinions on Java's future course. As you will hear in this interview, there are more options as Java is maturing.



We will be exploring some of these alternatives through 2010 on our blog, on our podcasts (Chariot Techcasts), and at our events, especially ETE 2010 (www.phillyemergingtech.com).