Wednesday, March 06, 2013

Gradle over Maven for Groovy/Java projects

* "The Ant" problems

For many years, I've been down with Maven and I still am. In many projects, I've seen some gigantic Ant scripts. Ant has often been a maintenance nightmare in huge applications, fix a script and notice that your eyes are bleeding.

As "most"corporate developers don't use Ivy to resolve dependencies with Ant, you typically end up with 3 scenarios:

  • The dependencies are stored in the version control server, that can get people fired at few places.
  • There's some kind of repository concept with jars at dedicated locations (Windows shares, NFS, etc.). The dependency resolution involves tons of scripting or some Ant tasks.
  • Dependencies are stored at a secret location(mapped drive to a Windows share that you'll know about one day) and you can only build after lots of configurations.

Some of the good things about Ant are flexibility, accessibility, documentation. Most developers know how to use Ant and writing custom tasks is not usually hard.

Maven 

My initial Master thesis subject was supposed to be about Apache Maven, but I bailed out... If I recall correctly Maven had only beta releases at that time with very little documentation.

After downloading Maven binaries, I didn't know what to do and how to use it: close to zero documentation and not really "accessible". When I was about to finish my MBA, I decided to give it another shot. It felt like "I looked at Maven and I got scared...".

The Jelly Issue


While many people didn't like Jelly, it provided some basic scripting capabilities inside the build file. If I recall correctly plugins also needed some Jelly files and many developers were annoyed by that constraint.


Sometime ago, I wrote an automation tool with Commons Jelly to setup Web hosting on Tomcat.
The tool was a launcher with few relatively small Jelly scripts to perform the following:

  • Read the list of users from an excel spreadsheet or XML file 
  • Setup FTP accounts 
  • Modify the Tomcat XML files to create application context paths for Web applications 
  • Populate the user information in a database(credentials, ftp and tomcat information)
  • Send email notifications with the account details.


Understanding Jelly made my life easier with Maven until it reached 2.x and Jelly was dropped at that time, if I recall correctly.

Maven evolution

With Maven 2.x things got cleaner and more stable, documentation got better and so on. Still many companies didn't give Maven a second chance. Then came Maven 3 and things haven't changed that much at the corporate level.

Most Java development companies still use Ant and few of them use Ivy to resolve dependencies. I do see Maven here and there, time to time, but not that often.

Maven adoption

From my experience, Maven has always been an issue in non Open-Source projects: too complicated, not worth the effort, too much impact on the typical IDE or build/release workflows.

While convention over configuration introduces a potential "lack of control" or flexibility, it also have its pros.

Gradle to the rescue

In the past, I've not really been a big fan of any kind of dynamic or scripting language, unless it's used for scripting or as an extension module. I would probably never have considered Gradle and similar tools in the past, because it would have made me feel "uncomfortable".

Nowadays, I'm willing to take advantage of Groovy, Jython, Clojure or whatever.

If I were doing some Clojure, I would try cake or lein as build tool, unless there's a feature that I need that is only available in Maven. Why? For just 3 main reasons :
- A tool dedicated to a programming language, often means more power and integration.
- There are not many attempts to do too many things or to be too generic, things are easier.
- Having scriptable builds and zero XML is cool.

Accessible to Java Developers

While a Groovy DSL to write a build script is no XML or Java, it's more expressive.
Learning some basic Groovy concepts is easy for Java developers.

Solid documentation

I was impressed when I saw the Gradle manual. It's very good and it's easy to read and understand.

Readable files

Last year, after migrating few personal projects from Maven to Gradle, I felt relieved. I didn't have some crazy POM files, but still too much XML to write (or copy/paste).
The Gradle build files were easier to read/understand with less lines of code.

Flexibility

Adding custom tasks with Groovy Code, without writing plugins unless you need too, this is really nice. If you need plugins there are couple of them out there.

While there are not as many plugins for Gradle as for Maven, I believe that Gradle is getting more  momentum. Many projects such as Hibernate already switched from Maven to Gradle.

Monday, December 17, 2012

VFSJFileChooser and XPontus are unmaintained

  Both VFSJFileChooser and XPontus XML Editor are currently "very inactive", I just needed to make it "very official"!

  Surprisingly, as soon as I stopped working on those projects, I started getting tons of emails related to features requests, bugs or general questions.

 It was a nice run...

 While I may still work on Open Source projects, they will always be small to medium. Nowadays, I focus pretty much all my energy on my Consulting company, Rimero Solutions.

 Below is the short story of XPontus and VFSJFilechooser.

XPontus
  XPontus is probably my most successful project in terms of downloads and interest. Sadly the code was written very late at night without "much love" from an API perspective.
   Beer was often around to ease the pain, that's the poor man's truth! I was awake and coding almost all the time...

Audience and expectations
  The first release introduced a basic text editor with syntax highlighting, as well as few features for validating and transforming XML. The program was fast and simple.
  I started getting feedback quickly as well as many requests about code completion and other missing features.

  Around the last release, I realized that editor was widely used. On Sourceforge there was about 30 000 downloads. I would estimate about the same number of combined downloads  from many other sites such as Freshmeat(now freecode it seems), Softpedia, etc.

  XPontus was then expected to reach quickly the same level of functionality than Altova XML Spy and others. One Open Source Developer against many dedicated Corporate developers...

Planning
  It took me a while to get comfortable with Sourceforge. Between research and related coding activities, I kinda rushed things.

  With too many users' requests, I felt like 24h wasn't enough, especially with a full-time on-site job.

  I was still learning some parts of Java Swing while writing the software and I never could perform a full cleanup. I just kept adding new features to please users at the expense of maintenance issues and additional bugs...

Failure to involve other developers
  Maven is often an issue among Java Developers. In real life, not that many people are comfortable with it, especially when it's a multi-project module.

  Because the API was a bit crappy and not very flexible, people got discouraged. I also had to answer tons of questions related to design and the various technologies that I was using.


Support, later on

 Initially, I generated all the installers manually, more control, but time consuming for testing on many platforms. Later on, ej-technologies gave me a free license of Install4j while I received a free copy of IntelliJ by JetBrainsApama designed the new XPontus logo.


Last effort
  I attempted a rewrite in late 2009 using OSGI but I had the impression that it would become a bit like Eclipse Framework, but for Swing.

  The new rewrite was better designed, higher quality but it was too time consuming. I thought about making the project commercial after ensuring that I was using only Apache Licensed libraries.

  I wish that I had the same programming abilities few years ago...

VFSJFileChooser 

Audience
  I always expected VFSJFileChooser to draw interest but not that much. It was a product by a developer and for developers. The library was meant to provide a file dialog component with out-of-the-box remote browsing features. The project was not much advertised.

  VFSJFileChooser was originally supposed to complement XPontus XML Editor by providing a filesystem abstraction layer. The component was then extracted to make it generic.

General planning
  Because it was my second Open Source project, I came prepared.

  I was both organized and I knew my entire Open Source workflow well. My growing Swing experience also helped a lot.

  The scope of the project allowed to be more focused in general. I was more disciplined in terms of programming habits and time allocation.

Community involvement
  As it was a relatively small project that used Ant (and not the evil Maven), I received lots of valuable contributions very quickly.

  Some new members joined the team and were were able to get along very quickly. I didn't have to provide any explanations, nor did I answer more than few technical questions.

  We did two releases together, if I recall correctly, and I was really happy about it. I also didn't feel "alone" anymore.

  My life was getting very unbalanced and I was also exploring new career opportunities, so that was pretty much the end of  it.

Other alternatives
  • otrosvfsbrowser is a File browser for Apache Commons VFS version 2. It was created as alternative to "VFS JFileChooser" and "Commons VFS - UI".
  • vfsjfilechooser2 is a mavenized fork of the dormant vfsjfilechooser project on sf.net.

Saturday, December 08, 2012

emacs-grails-mode enhancements

I'm now using Emacs again as my main Anything (Text Editing, Mail Reading, Blogging, Chat, etc.).

I still use vi and IDEs randomly but I will dedicate more time to master Emacs (don't quote me on that...) and a bit of Lisp.

I was looking for some Emacs packages for Grails Development and I found emacs-grails-mode.
It's nice, but it doesn't seem to provide few things such as running a random Grails command against a project.

During development, most of the time I'm jumping between files using find-file-in-project. Occasionally, I use few functions provided by emacs-grails-mode to switch between files associated to Grails domain classes.

Then, whenever I want to run a Grails command, I switch to a dedicated shell buffer. I read the output and I jump back to the file(s) that I'm working on...

I'm no Lisp coder which is a bit sad from my perspective. If I were fluent with it, I could write couple of Emacs packages or improve few things for my personal taste.


Below is my first attempt to improve emacs-grails-mode. I will soon create a basic project on my corporate GitHub account and push the code there. The license will be GPL v3.

I added the following extensions to emacs-grails-mode.
  • Few functions to run some Grails commands from the project folder.
  • There are are also 3 functions to browse API docs, Wiki docs as well as the latest reference guide.


;; ================================
;; Extensions for grails-mode
;; Author : Yves Zoundi
;; Created : 08-Dec-2012
;; 
;; License: GNU GPL v3 (http://www.gnu.org/licenses/gpl-3.0.txt)
;; Sypnosis:
;;    Run grails command on a project
;;
;; TODO:
;;   - Add some easy keybindings like in Rinari
;;   - Would be nice if auto-completion was there
;;     (including dynamic methods)
;;   - Anything else that would be useful for Grails development
;; ================================
(require 'grails-mode)

;; --------------------------------
;; Main functions
;; --------------------------------
(defun grails/command (str)
  "Run a Grails command (Non interactive)"
  
  (project-ensure-current)
  (setq shell-command-initial-directory 
        (project-default-directory (project-current)))
  ;; store old directory
  (setq old-dir default-directory)

  (setq command-initial-directory 
        (expand-file-name shell-command-initial-directory))
  (cd command-initial-directory)
  
  ;; runs the grails command from the project directory
  (async-shell-command (concat "grails " str) nil nil)

  ;; restore previous directory
  (cd old-dir)
  )

 (defun grails/read-param-and-run (input-hint grails-command)
  "Read an input parameter and invoke a given Grails command"
  (setq grails-command-argument 
        (read-from-minibuffer input-hint))  

  (grails/command 
        (concat grails-command " " grails-command-argument))
 )

;; --------------------------------
;; General functions
;; --------------------------------
(defun grails/icommand ()
  "Enter a Grails command (Interactive)"
  (interactive)  
  (grails/read-param-and-run "Goal:" "")
  )

(defun grails/create-domain ()
  "Create a Grails Domain Class"
  (interactive)
  (grails/read-param-and-run 
   "Domain class:" "create-domain-class")
  )

(defun grails/create-controller ()
  "Create a Grails Controller"
  (interactive)
  (grails/read-param-and-run 
   "Controller Domain class:" "create-controller")
  )

(defun grails/create-service ()
  "Create a Grails Service"
  (interactive)
  (grails/read-param-and-run 
   "Service Domain class:" "create-service")
  )

(defun grails/create-taglib ()
  "Create a Grails Taglib"
  (interactive)
  (grails/read-param-and-run 
   "TagLib Name:" "create-tag-lib")
  )

;; --------------------------------
;; Plugin functions
;; --------------------------------
(defun grails/install-plugin ()
  "Install a Grails plugin"
  (interactive)
  (grails/read-param-and-run 
   "name optionalversion:" "install-plugin")
  )

(defun grails/uninstall-plugin ()
  "Uninstall a Grails plugin"
  (interactive)
  (grails/read-param-and-run 
   "Plugin Name:" "uninstall-plugin")
  )

(defun grails/package-plugin ()
  "Package a Grails plugin"
  (interactive)
  (grails/command "package-plugin")
  )

(defun grails/refresh-dependencies ()
  "Refresh Grails Dependencies"
  (interactive)
  (grails/command "refresh-dependencies")
  )

;; --------------------------------
;; Browse docs (api, wiki, guide)
;; --------------------------------
(defun grails/browse-wiki-docs ()
   "Browse the Wiki Documentation"
   (interactive)
   (browse-url "http://grails.org/Documentation")
)

(defun grails/browse-api-docs ()
   "Browse the API Documentation"
   (interactive)
   (browse-url "http://grails.org/doc/latest/api/")
)

(defun grails/browse-latest-guide ()
   "Browse the official Grails Guide"
   (interactive)
   (browse-url "http://grails.org/doc/latest/guide/single.html")
)

(provide 'emacs-grails-mode-ext)

Saturday, October 27, 2012

A quick look at Apache Karaf




Today, I decided to take a brief look at Apache Karaf. Apache Karaf is an OSGI runtime that embeds Apache Felix as OSGI container. It ships with couple of OSGI bundles and provides a nice shell interface.

I am not really covering some technical details in this post. I haven't deployed an application under Karaf yet, as I would want to do a bit more than the typical "Hello World".

My previous OSGI experience in context
Few years ago, I was trying to understand OSGI and I created an OSGI based IRC bot, called Jerkbot. Prior to that experience, OSGI looked inaccessible, unpopular with a low return value. Even after reading couple of OSGI articles, I still couldn't figure out 100% what to do with it.

Building Jerkbot
Within a day, I was able to deal with most of the bot features. I went quickly over Apache Felix tutorials and I was ready to write some code.

  • As soon I introduced a persistence layer, I hit minor issues with JPA(classloading). After struggling with OpenJPA and Hibernate, I settled with EclipseLink.
  • I used mainly Declarative Services with annotations to save time, instead of writing couple of BundleActivators and/or XML descriptors.
  • SpringSource had a Maven repository with OSGIfied jars, so I used it instead of wrapping every single non OSGI jar

My build process involved the following :

  • Run the Maven install goal at the root of the multi-module project
  • Copy all the files with rsync from  my desktop to a laptop running FreeBSD (/)
  • Setup the Apache Felix configuration to specify the startup order of each bundle depending on what I was testing
  • Start Apache Felix from the command line with my configuration

Alternatively, I could update the bundles within the Apache Felix console without restarting the program sometimes.

Packaging
When the time came to actually shipping the application, I was annoyed. I had to write a Maven assembly configuration to package the application as a zip file. After playing with couple of deployment options, I had generated few temporary folders and it became difficult to identify what was still needed.

I ended up only shipping source code, as the PAX Maven plugin could run the application from the project tree.

If I recall correctly Karaf already existed, as mentioned in a blog post about Apache Karaf history. I believe that I did look at it, but not in details. I probably couldn't figure out quickly whether or not it would be worth it (read 2 paragraphs and give a yes/no/maybe answer).

Application Frond-end
As the bot itself had a JMX interface, I could just use JConsole to hook into the bot admin interface. I wasn't really planning on hooking up some Apache Felix commands.
With Karaf, the shell extension process seems similar to the one provided by Apache Felix.


Discovering Karaf

I was really impressed by the product's features and usability. Below are some of the topics that got my interest.

Installation
Installing Karaf on my iMac was pretty straightforward. The only problem that I run into was launching the webconsole as soon as I installed it. I got a NullPointerException and I just restarted Karaf. I guess that some initialization logic is probably missing in the bundle activation or something related.

Documentation
I usually don't read much technical documentation. The Karaf folks did a decent job with the user guide. I found myself scrolling and jumping only to topics of interest.

Console
The console is really neat with tab completion. The contextual help for each command is brief and easy to grasp.

Deployment
In additional to standard OSGI bundles, Karaf also includes support for wrapping non OSGI jar files. I guess that they achieved that with some integration with the BND tool. What really surprised me was the Maven integration in addition to standard deployment options : http://stackoverflow.com/questions/9414375/karaf-development

It is also possible to configure the available Maven repositories: https://cwiki.apache.org/KARAF/66-installing-additional-features.html

Remoting
Apache Karaf exposes couple of JMX MBeans, it has a nice Web Console and SSH access is also supported.

Security
Karaf developers really made a decent effort here. The JAAS integration seems rather smooth in addition to basic encryption services. I found a blog with a JAAS module example and I didn't expect it to be that easy http://iocanel.blogspot.ca/2010/09/karafs-jaas-modules-in-action.html

Opened questions (running foreground)
If I recall correctly, it is possible to start Apache Felix in the foreground by specifying a config property (embedded or something). I didn't figure out how to do it with Karaf, but I didn't search a lot.


Conclusion
I easily see myself using Apache Karaf on side projects in the near future. It provides many  features out of the box that would require the following :

  • Adding couple of bundles to the OSGI container manually
  • Writing few modules to support administration services with additional options. Especially with you can grab few OSGI bundles here and there that I only provide basic functionalities(Felix config/admin bundles, PAX bundles, etc.).

I still don't seem much OSGI adoption even though there are more articles and tutorials available. I believe that Apache Karaf makes OSGI somehow more "accessible". However, the OSGI adoption in the corporate world is very slow from what I see. OSGI needs more simplicity, more mediatization, more real life examples to reach many companies' doors.


References


Saturday, March 24, 2012

GWT in general and the CellTree widget

    I've been toying a bit with GWT and it's quite nice.

     In the past, I've used many frameworks to some extent, depending on what was needed : raw JSP with or without JSTL, Struts, VRaptor 1.x,  Cocoon, JSF, Velocity, Wicket, etc.
     I had more fun using Wicket than something like Struts 1.x for example...
     I never liked Struts but it seemed good enough compared to other frameworks at the time(Early ASP.NET versions were already more usable for medium size apps). If I were compelled to use something similar to Struts today, with the luxury of choice, I would likely select Stripes or another technology close to it (simple but flexible).
     Overall, I've always preferred component based Web Frameworks to the Actions based ones.

A) GWT and its UI frameworks (more than UI usually)
   GXT and others such as SmartGWT seem to be much slower than raw GWT for rendering but it is expected as the default look and feel looks good without any effort. The default look and feel is cool.

   I stumbled upon GXT buttons rendering. While the buttons are VERY beautiful, the amount of nested elements required for rendering is almost incredible.


1) Normal HTML
<input type="button" value="OK" class='dummyClass' />

2) GXT (dummy example to illustrate the problem)
<table cellspacing="0" role="presentation"
class="x-btn x-component x-btn-noicon" id="x-auto-175"
style="margin-right: 5px;">
  <tbody class="x-btn-small x-btn-icon-small-left">
    <tr>
      <td class="x-btn-tl">
        <i> </i>
      </td>
      <td class="x-btn-tc"></td>
      <td class="x-btn-tr">
        <i> </i>
      </td>
    </tr>
    <tr>
      <td class="x-btn-ml">
        <i> </i>
      </td>
      <td class="x-btn-mc">
        <em unselectable="on" class="">
          <button style="position: relative; width: 69px;"
          type="button" class="x-btn-text" tabindex="0">
          Save</button>
        </em>
      </td>
      <td class="x-btn-mr">
        <i> </i>
      </td>
    </tr>
    <tr>
      <td class="x-btn-bl">
        <i> </i>
      </td>
      <td class="x-btn-bc"></td>
      <td class="x-btn-br">
        <i> </i>
      </td>
    </tr>
  </tbody>
</table>

The above example was taken from http://my.opera.com/lienngoc/blog/2010/08/10/how-to-improve-performance-in-a-gwt-gxt-application and is realistic enough.

B) GWT CellTree
The GWT CellTree is a very nice widget. I managed to use it with RPC calls within a small amount of time and minimal reading.

1) Rendering
I was expecting to be able to provide easily a custom renderer and that was the case. In the GWT showcase, for each level of the tree, the same kind of node is rendered which means a single renderer at each level of the tree. I was like well, it is just an 'example'. In real life, you usually have objects of different kinds at the same level of a tree and they need to be rendered differently.

I ended up using a single renderer and calling instanceof to select the appropriate styling for each node of the tree. I couldn't find another solution within a short amount of time.
public void render(Context context, MyObject value, SafeHtmlBuilder sb) {
 if (value != null) {
  sb.appendHtmlConstant((value instanceof MyBaseObject) ? baseImgHTML : advancedImgHTML).appendEscaped(" ");
  sb.appendHtmlConstant("");
  sb.appendEscaped(value.toString());
  sb.appendHtmlConstant("");
 }
}

2) Can I haz vertical scrollbars???
After testing a simple application, I noticed that Google Chrome was displaying a vertical scrollbar while I was expanding the CellTree nodes. Firefox didn't render any scrollbars.... I came accross a stackoverflow link, and for a second I thought 'this is it!', I guess it would've been too easy...
http://stackoverflow.com/questions/7854086/horizontal-scrollpanel-not-displaying-with-celltree

After 'googling' a bit and Firebug's help, I found a solution.
1. Add few CSS style attributes to the CellTree and its parent container
The celltree has the following CSS style : width:100%;height:100%;overflow:auto. It is enclosed within an HTMLPanel with the same CSS style. There's no need to combine a ScrollPanel with let's say a VerticalPanel. Same problem and additional widgets used.


2. Ensure that the first element has a visible overflow and a dummy size in pixels
I didn't test it with a small tree, but I figure that as long as you supply a dummy size that will always be lower than the height of a non empty tree, it'll be fine. I used 100px.
Style firstCellTreeChildStyle = cellTree.getElement().getFirstChildElement().getStyle();
firstCellTreeChildStyle.setOverflow(Overflow.VISIBLE);
firstCellTreeChildStyle.setHeight(100, Unit.PX);   

C) Overall opinion about pure GWT vs GWT frameworks

It looks like building a professional looking pure GWT application requires a serious commitment in terms of styling widgets. For medium-small applications, I think that using a framework will give good looking results with less time without many performance issues.

Sunday, June 19, 2011

Playing with Apache Cocoon 3.0 - Part 2

As couple of people requested a complete example, I uploaded the full source file, as well as some sample input files, everything but the jar files here.


About the upload

  • The uploaded file is called dist.zip (MD5: d930916c8506bdc563925385e7aa1162) and contains 2 folders : src and www
  • The src folder contains the Java source file 
  • The www folders contains all the files needed to generate a simple website. I didn't provide the exact files that I used for privacy reasons.

Dependencies


You can grab the required Jar dependencies from Maven repositories(click on the hyperlinks below) :



Running the application
  • Create an Java project with the IDE of your choice and add the source file and the required dependencies(jar files).
  • In the main method of the class StaticSiteGenerator, change the variable wwwRoot to point the the folder www folder extracted from the dist.zip archive.
  • Run the class and the output will be generated in the www/out folder in HTML format.

Saturday, April 09, 2011

Playing with Apache Cocoon 3.0

This morning, I needed to generate quickly a simple website without learning some template engine or recalling it.

I was thinking about couple of years ago when I was working a lot with Apache Cocoon and XML processing in general.

I downloaded the latest build of Cocoon 3.0 alpha 2 and gave it a shot. Below are my settings for the site generation :

  • Input folder containg XML files (root element and html content)
  • Toc file for site navigation (xml + html markup)
  • Resources to include 
  • An XSL stylesheet for the website generation (dynamic include of the TOC)
The site was ready after few minutes and minor adjustments. It looks like there's a bug in the XIncludeTransformer so I couldn't use it with the alpha 2 version.

Below is some quick and dirty code(all in one class, without other files used) written after downloading it. There are some errors(generics and invalid content) in the code below because of the code formatter that I'm using on this blog.
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.transform.OutputKeys;

import org.apache.cocoon.pipeline.NonCachingPipeline;
import org.apache.cocoon.pipeline.Pipeline;
import org.apache.cocoon.sax.SAXPipelineComponent;
import org.apache.cocoon.sax.component.XMLGenerator;
import org.apache.cocoon.sax.component.XMLSerializer;
import org.apache.cocoon.sax.component.XSLTTransformer;

// Simple website generation demo with Apache Cocoon 3.0 alpha2
public final class StaticSiteGenerator {

    // Generator configuration parameters
    private static enum SiteConfiguration {
        INPUT_FOLDER_LOCATION,
        OUTPUT_FOLDER_LOCATION,
        WEBSITE_XSL_STYLESHEET_LOCATION,
        RESOURCES_FOLDER_LOCATION,
        TOC_FILE_LOCATION,
        INPUT_FILES_EXTENSION
    }
    
    // Error thrown when the configuration appears to be invalid
    private static class InvalidConfiguration extends RuntimeException {
        private static final long serialVersionUID = -8661543651471092797L;

        public InvalidConfiguration(String message) {
            super(message);
        }
    }

    // Parameter passed to the XSL stylesheet to include a navigation bar
    private static final String TOC_FILE_XSLT_PARAMETER_NAME = "tocURL";

    // Current link in the TOC
    private static final String TOC_FILE_CURRENT_LINK = "currentLink";    

    // Copy a file to a folder
    static void copyToFolder(File in, File outputFolder) throws IOException {
        File out = new File(outputFolder, in.getName());
        
        FileChannel inChannel = new FileInputStream(in).getChannel();
        FileChannel outChannel = new FileOutputStream(out).getChannel();
        
        try {
            inChannel.transferTo(0, inChannel.size(), outChannel);            
        } 
        catch (IOException e) {
            throw e;
        }
        finally {
            if (inChannel != null)  inChannel.close();
            if (outChannel != null) outChannel.close();
        }
    }

    // Generator configuration parameters
    private EnumMap configuration;
    
    
    // Creates a new StaticSiteGenerator with a given configuration
    StaticSiteGenerator(EnumMap configuration) {
        validateConfiguration(configuration);
        this.configuration = configuration;
    }
    
    // Simple configuration validation
    private void validateConfiguration(EnumMap configuration) throws InvalidConfiguration {
        if (configuration == null) {
            throw new IllegalArgumentException("You must provide a valid configuration");
        }
        
        Collection allConfigurations = new ArrayList(Arrays.asList(SiteConfiguration.values()));
        Collection providedConfigurations = new ArrayList(configuration.keySet());
        allConfigurations.removeAll(providedConfigurations);
        
        if (!allConfigurations.isEmpty()) {
            StringBuilder sb = new StringBuilder(allConfigurations.size() * 3);
            sb.append("Some configuration parameters were not found:");
            SiteConfiguration[] missingConfigurations = allConfigurations.toArray(new SiteConfiguration[allConfigurations.size()]);
            sb.append(missingConfigurations[0]);
            for (int i = 1; i < missingConfigurations.length; i++) {
                sb.append(", ").append(missingConfigurations[i]);
            }
            throw new InvalidConfiguration(sb.toString());
        }
    }

    private void clean(File outputFolder) {
        for (File outputFile : outputFolder.listFiles()) {
            outputFile.delete();
        }
        
    }
    // Generates the static web site
    private void execute() throws Exception {        
        final File inputFilesFolder = new File(configuration.get(SiteConfiguration.INPUT_FOLDER_LOCATION).toString());
        final File outputFolder = new File(configuration.get(SiteConfiguration.OUTPUT_FOLDER_LOCATION).toString());
        final File resourceFolder = new File(configuration.get(SiteConfiguration.RESOURCES_FOLDER_LOCATION).toString());
        final File xslStylesheet = new File(configuration.get(SiteConfiguration.WEBSITE_XSL_STYLESHEET_LOCATION).toString());
        final String inputFilesExtension = configuration.get(SiteConfiguration.INPUT_FILES_EXTENSION).toString();
        final File tocFile = new File(configuration.get(SiteConfiguration.TOC_FILE_LOCATION).toString()); 
        
        clean(outputFolder);
        
        final String tocURL = tocFile.getAbsolutePath();
        
        // Get the list of XML files for each page of the website
        File[] inputFiles = inputFilesFolder.listFiles(new FileFilter() {
            public boolean accept(File pathname) {
                return pathname.getName().endsWith(inputFilesExtension) && pathname.isFile();
            }
        });
        
        File[] resources = resourceFolder.listFiles();

        // Copy resources to the output folder
        for (File resource : resources) {
            copyToFolder(resource, outputFolder);
        }

        // Loop through the input files
        for (File inputFile : inputFiles) {
            OutputStream outputFileStream = null;

            String msg = "Transforming '%s' to '%s'";

            try {
                String outputFilename = inputFile.getName().replaceAll(inputFilesExtension, ".html");

                File outputFile = new File(outputFolder, outputFilename);

                Logger.getAnonymousLogger().log(Level.INFO, String.format(msg, inputFile.getAbsolutePath(), outputFile.getAbsolutePath()));
                
                outputFileStream = new FileOutputStream(outputFile);
                
                Pipeline pipeline = new NonCachingPipeline();
                
                pipeline.addComponent(new XMLGenerator(inputFile.toURI().toURL()));
                pipeline.addComponent(newXSLTransformer(xslStylesheet, tocURL, outputFilename));
                pipeline.addComponent(newXMLSerializer());

                pipeline.setup(outputFileStream);
                pipeline.execute();
            }
            finally {
                if (outputFileStream != null)  outputFileStream.close();
            }           
        }        

    }
    
    private XMLSerializer newXMLSerializer() {
        Properties format = new Properties();
        
        format.put(OutputKeys.METHOD, "xml");
        format.put(OutputKeys.INDENT, "yes");
        
        return new XMLSerializer(format);
        
    }
    
    private XSLTTransformer newXSLTransformer(File stylesheet, String tocFileURL, String currentLink) throws MalformedURLException {
        Map parameters = new HashMap(1);
        parameters.put(StaticSiteGenerator.TOC_FILE_XSLT_PARAMETER_NAME, tocFileURL);
        parameters.put(StaticSiteGenerator.TOC_FILE_CURRENT_LINK, currentLink);

        XSLTTransformer transformer = new XSLTTransformer(stylesheet.toURI().toURL());
        transformer.setParameters(parameters);

        return transformer;
    }

   public static void main(String[] args) throws Exception {
        String wwwRoot = "/Users/yves/Desktop/www"; 
        
        EnumMap configuration;
        configuration = new EnumMap(StaticSiteGenerator.SiteConfiguration.class);

        configuration.put(SiteConfiguration.INPUT_FOLDER_LOCATION, wwwRoot + "/in");
        configuration.put(SiteConfiguration.INPUT_FILES_EXTENSION, ".xml");
        configuration.put(SiteConfiguration.TOC_FILE_LOCATION, wwwRoot + "/toc/toc.xml");
        configuration.put(SiteConfiguration.OUTPUT_FOLDER_LOCATION, wwwRoot + "/out");
        configuration.put(SiteConfiguration.WEBSITE_XSL_STYLESHEET_LOCATION, wwwRoot + "/resources/xsl/site.xsl");
        configuration.put(SiteConfiguration.RESOURCES_FOLDER_LOCATION, wwwRoot + "/resources/files");
        
        StaticSiteGenerator generator = new StaticSiteGenerator(configuration);
        generator.execute();
    }

}






Thursday, September 30, 2010

Flex annoyances...Can I grabz the initialz focuz?

I've been playing a bit with ActionScript and the like lately. ActionScript and few other languages never really caught my interest. Sure, I can read and I could manage, but why? ;-).

In fact, I've been avoiding Perl, ActionScript and few others professionally for years:
Q:"So you know Java, Quartz, JMX, Unix/Linux, etc. what about Perl?"
A:"Oh my, to be honest, I'm afraid of it...".
Sometimes, you just have to suck it up and do the damn thing anyway.

It's surprising to me that Flex is used a lot for "intensive" tasks, that would/should require threads to poll data at specific intervals. What else would you use if you need a decent toolkit with advanced drawing capabilities on the browser? A beloved Java Applet? Silverlight? HTML5? Javascript?? ActiveX???

Flex has some interesting things despite being single threaded(not the flash engine itself) : function pointers, easy binding, easy drag and drop, etc. However, some simple to more complex things can become annoying, while with more code, but still simple code, the same thing would be trivial with a Desktop UI toolkit(that you're familiar with).
  • Can I be sure that a return inside a switch statement will just return, at once?
  • Can I get the initial focus on a component once the flash movie is loaded, without keyboard interaction from my part? without random errors once in a while? without upgrading to bleeding edge?
  • Can I get a simple even bare layout manager interface without plugging stuff directly inside some updateDisplayList method? Or without managing myself couple of  "drawing delegates"?
  • etc...

It looks like Flex has couple of issues with its FocusManager. All I wanted was setting the focus on a component at startup with a blinking cursor... Trivial right?

I was looking for an answer on Google as many seem to have that issue. I found a suggestion that seem to work :
ExternalInterfaceIfAvailable.call(SomeJavascript.focusFlashComponent).
callLater(mycomponent.grabTheFocus)

Lots of people seem to be ok with the fact that the Javascript that they write might not work on most browsers. Well, good enough if only a defined set of browsers will be supported by the application.
But hey man, this is 2010, most web applications are past the time where a notice would be displayed "Only supported in Internet Explorer". Unless this is the web interface of your online banking accounts, you switch to an alternative immediately."Sorry man, I do not use Windows, so how could I use I.E??".

I just grabbed the latest version of prototypejs and I'm confident that the code compatible with most modern browsers(and even maybe text based browsers that support a little subset of Javascript). Why not write it myself? Well I don't do tons of Javascript, and looking at browsers specs and compatibility with Javascript versions might not be worth the time. From prototypejs to JQuery and the like, people already did the dirty work.

Let's say that the Javascript code is working, now you're able to set the initial focus on a component once the flash movie is loaded. In my case, the component that need the focus is added/removed at runtime.

When going back to the initial screen, Flex 3.4 throws randomly errors in the FocusManager. It looks like that FocusManager bug got fixed(defaultButton issue), but my solution was to deactivate/activate the FocusManager myself :
  • Initial screen
onCreationComplete -> javascript call to set the focus -> Specific flex component requests then the focus
  • Leaving the initial screen(Avoid some null errors on the defaultButton if you use that)
component.focusmanager.deactivate()
  • Going back to the initial screen(Avoid some null errors on the defaultButton if you use that)
mycodeToGrabTheFocus
component.focusmanager.activate()

Tuesday, December 29, 2009

Default right-click in all text components of an application

First of all, Merry Christmas and Happy New Year everybody!

Many people are still surprised not to see a default right-click popup in all text components of a Java application. One way to do it, is to push a new EventQueue to the default one. One concern could be applets in general (unsigned applets, global EventQueue).
Toolkit.getDefaultToolkit().getSystemEventQueue().push(new PopupEventQueue());
Below is the code for the event queue.

import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.EventQueue;

import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;

import javax.swing.AbstractAction;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;

import javax.swing.text.JTextComponent;

public class PopupEventQueue extends EventQueue {

private final JPopupMenu popup = new JPopupMenu();
private final TextAction[] popupActions = new TextAction[4];

public PopupEventQueue() {
popupActions[0] = new TextAction("Cut") {
private static final long serialVersionUID = -3844049016540352208L;

public void actionPerformed(ActionEvent ae) {
textComponent.cut();
}

@Override
protected void postTextComponentInitialize() {
setEnabled(textComponent.isEditable() && isTextSelected());
}
};
popupActions[1] = new TextAction("Copy") {
private static final long serialVersionUID = -3844049016540352208L;

public void actionPerformed(ActionEvent ae) {
textComponent.copy();
}

@Override
protected void postTextComponentInitialize() {
setEnabled(isTextSelected());
}
};
popupActions[2] = new TextAction("Paste") {
private static final long serialVersionUID = -3844049016540352208L;

public void actionPerformed(ActionEvent ae) {
textComponent.paste();
}

@Override
protected void postTextComponentInitialize() {
setEnabled(textComponent.isEditable());
}
};
popupActions[3] = new TextAction("Select all") {
private static final long serialVersionUID = -3844049016540352208L;

public void actionPerformed(ActionEvent ae) {
textComponent.selectAll();
}

@Override
protected void postTextComponentInitialize() {
setEnabled(!textComponent.getText().trim().equals(""));
}
};

for (TextAction action : popupActions) {
popup.add(action);
}
}

@Override
protected void dispatchEvent(AWTEvent event) {
if (event.getID() == MouseEvent.MOUSE_RELEASED) {
MouseEvent e = (MouseEvent) event;

Component c = getSource(e);

if (c instanceof JTextComponent) {
if (SwingUtilities.isRightMouseButton(e)) {
final JTextComponent txtComp = (JTextComponent) c;
for (TextAction action : popupActions) {
action.setTextComponent(txtComp);
}
popup.show(e.getComponent(), e.getX(), e.getY());
}
}
}
super.dispatchEvent(event);
}

private Component getSource(MouseEvent e) {
return SwingUtilities.getDeepestComponentAt(
e.getComponent(),
e.getX(),
e.getY());
}

private static abstract class TextAction extends AbstractAction {

private static final long serialVersionUID = -7708937505251885197L;
protected JTextComponent textComponent;

public TextAction(String name) {
super(name);
}

public void setTextComponent(JTextComponent textComponent) {
this.textComponent = textComponent;
postTextComponentInitialize();
}

protected boolean isTextSelected() {
return (textComponent.getSelectionStart() != textComponent.getSelectionEnd());
}

protected abstract void postTextComponentInitialize();
}
}

Monday, August 24, 2009

The making of an OSGI based IRC Bot

I've released the source code of JerkBot, an IRC bot based on Jerklib and OSGI. JerkBot source code is a multi-module maven project. It's not some OSGI/Java blueprints as I'm no OSGI expert, plus JerkBot is basically a 24 hours effort without bug fixes.

The library itself is distributed under BSD license, but the bot provides plugins such as SVN for example using SVNKit , which is subject to other licensing terms.

The bot uses Declarative Services for OSGI and Java technologies such as :

  • Apache Lucene for Javadoc Search
  • Quartz for job scheduling(session tracking, pending registrations, etc.)
  • Javamail to send emails for user registration
  • EclipseLink for persistence
  • A subset of JAAS for security
  • JMX for administrative commands (accessible through JConsole or through the bot jmx command)
  • The usual Jakarta libraries and couple of other libraries

I didn't provide scripting languages to the bot for security reasons, there's only JMX. I would prefer to offer scripting languages in a secure way with a custom SecurityManager to prevent drama from happening :-).
Let's say scripting is enabled, and someone accidently tries one or all the following instructions in a scripting language.

  • File("/somepath").delete()
  • System.exit(0)
  • Download("http://website/hugefile.iso").saveToDisk();

JerkBot was roughly a one day effort with 4 rewrites(1 full day each), and of course bug fixes time to time:
  • The first version was using traditional OSGI, well not so traditional :-), with BundleActivators service trackers and listeners, etc.
  • The second rewrite used Declarative Services with manually written XML descriptors
  • The third rewrite was based on Felix IPOJO. I would have preferred to use IPOJO, but found some annoyances(abstract based classes, JMX flexibility, etc.).
  • The last rewrite is using Felix SCR but with annotations to generate XML descriptors for components. Simplify code, remove unnecessary abstractions, consistent logic, etc.

I'll be providing the binary distribution soon. The binary distribution contains everything necessary to run the bot : OSGI configuration files, Bot configuration, jars, a user/developer guide, etc.

What the bot doesn't provide is a logging mechanism, for IRC channel logs. To do it right, I think it would be better to create a new project. In my opinion, when a bot logs channel it should have, if possible, the following features:

  • Configurable tasks to schedule flexible timed delivery of existing logs(local filesystem, ftp, samba share, ssh, http put, etc.)
  • Configurable log format(HTML, CSV, TXT) with optional generation of html logs or text logs.
  • Database storage or any other persistence mechanism(Apache Lucene Index vs flat text files, database logs, etc.)
  • Ability to stop/start logging for every channel
  • Optional Web front-end to publish logs(Could be a static HTML pages with Javascript search, JSF, Wicket, GWT, Rest interface + Apache Lucene or a DB for search).
Providing a quick and dirty plugin for channel logs would be trivial but not flexible :-). I started implementing it, but I decided to stop there.

The first draft of the bot manual can be found in the svn repository. Please bear with me for grammar and spelling mistakes, it was a written very quickly at early AM :-)

In JerkBot plugins are provided by OSGI bundles. I learned a lot from my previous experience with JPF when writing XPontus.

For now the bot is sitting on irc.freenode.net in the ##swing channel, running on an old laptop(FreeBSD-CURRENT).


Wednesday, July 22, 2009

Which one is the best?

You've probably heard it many times, in various circumstances. What was your answer?

That trivial question may(or not) have been subject to a long answer with hopefully credible reasons. "The problem is ... Many tools provide XXX ... This tool is the best, in this case, as ...".
But you're the expert! The reasons motivating your choices should be obvious to anybody else without having a long discussion!

There is no best, there are needs and there are constraints to reach particular goals(short, medium or long term). All those variables usually fits in to a vision.

If there were a best, anyone who could afford it, would have it.

No, no why would I want to use crap seriously?? So many tools already suck and people keep providing more crap. Maybe I should suggest that they stop already, as there are many similar great tools out there.

A typical conversation about choosing a Linux distribution
Q: "What's the best Linux distribution? Ubuntu?"
A: "Wut??? Hell no, it's Debian and all the rest is crap including the derivatives."
Q: "Why?"
A: "Because Ubuntu doesn't ... and because I say so :-)"
Q: "But Ubuntu is easy and Debian is not user friendly!"
A: "Really? Not really .... Ah, I guess I just like things when they're complicated, must be the geek feeling."

A tool can meet needs but not all the constraints and vice-versa and what you'll probably be looking for is a balance.

  • Memory usage vs tons of features
  • Usability vs complexity/too much flexibility
  • Easily understood vs require 5 books + certification + hiring a consultant
  • Commercial support vs community support
  • Proven stability and acceptance vs the unknown
  • etc.

There are simple ways to decide :

  • You have a problem to address within constraints(time, budget, etc.)
  • You try looking for tools which are particularly good at solving your specific problem and that integrate perfectly in your custom infrastructure. However, no such tools seem to exist or there's that little thing that you dislike.
  • You then look for compromises and ways to solve the issues that won't get magically fixed by the tools.
  • You don't have time to look at all the existing tools and evaluate them. You'll be selecting few tools and trying them out. Hopefully software vendors will cover the tiniest details which are relevant to your business needs, in their documentation.
  • You decide and you live with it, maybe reevaluate your decision and revise your goals, but you move forward unless you really believe that you're wrong.

It's not easy to decide in the IT world. You might get it right or wrong but you may have the power to correct your mistakes. Whatever the choice, the rational move is trying to select wisely, going forward and take responsibility.

Tuesday, July 14, 2009

Playing with OSGI and JPA

I've been writing an IRC bot for fun, but it's far from being done. My goal is to experiment with JPA in an OSGI environment.

At work, I am using Hibernate, XDoclet to generate the XML and JPA isn't coming soon :-). My last JPA application was a pastebin application, with Wicket, hibernate search, Lucene, etc.

I'm using a friend's library Jerklib with Dynamic JPA. I'm still having some minor issues when deploying but I'll probably find a solution soon.

About the application
Environment
I have a multi-project with Maven and I'm using the maven bundle plugin and I'm developing in Eclipse 3.5 on Debian testing.
I'm using openjpa , dynamic jpa and couple of other dependencies

Design overview
a) Commands implemented as plugins : The bot has a set of factoids(learn, forget, etc...). Each command gets created using a factory.

// message listener //String operation = getOperation();
CommandFactory factory = ServiceFromOSGI.getCommandFactory(operation);

// if the factory is not null, redundancy for the operation parameter
// as a factory can have multiple commands
Command command = factory.createCommand(operation);
ircChannel.say(command.render(ircMessageContext));

b) The command service listens for removal or installation of commands and updates itself.

After writing couple of "users' commands", I would need to implement some administration commands(load/unload plugins, irc specific tasks, etc.).

Problems
The dynamic discovery is failing for now, the persistence provider class is not resolved, it might just be a bundling problem for openjpa. I wrapped it myself.

Conclusion
It's strange that many open source projects still don't provide an OSGI manifest. The maven-bundle plugin is very trivial to use for maven enabled project and there's still bnd.
While OSGI is an interesting technology, I personally don't know anybody using it in the enterprise unless they are an Eclipse shop. I think that it's mostly due to
  • the lack of "OSGI enabled jars"
  • the fear that OSGI might introduce unnecessary complexity
  • the lack of step by step complete examples(if possible with screencasts)

There's plenty of documentation about OSGI and lots of successful applications(Web, Desktop) using OSGI. Hopefully my bot will be one those applications :-)

Saturday, March 21, 2009

Thoughts about JavaFX

JavaFX has the potential to become something interesting. While there are many articles written about JavaFX, I have yet to see a non trivial JavaFX application. When browsing Dzone articles, it almost looks like JavaFX is popular, while it's not.


There's no amazing UI control and I am not sure that I could code an entire JavaFX application without writing few java classes. I am not fond of applets and I haven't written any for years. JavaFX doesn't solve the "applet problem".


I don't know about any cellular phone which is officially supporting JavaFX. I am also not aware of any software vendor distributing desktop applications written with that technology.


In my opinion, JavaFX is not ready for production use. What motivated the release of JavaFX? Maybe they've been advertising it for too long and they had to release something. I'll give JavaFX probably one more year before attempting to use it.



Sunday, January 04, 2009

serialVersionUID in Netbeans

Most of the time, I use Netbeans when I have the choice. What I don't like about Eclipse and is getting me worried time to time is when the IDE freezes for a long time when you're not really doing anything.

Last week, I needed to build a simple Java project (Maven based build), about 50 000 lines of code(from sloccount). The project contains one core project and few sub projects. Eclipse took about 30 minutes to import the project and set up the classpath. That performance was achieved under a Quad Core, 2 GB of RAM, which is amazing. I had only Eclipse, Firefox and a terminal opened. I tried with both q4e and m2eclipse plugins, same results, I could hear my CPU making lots of noise. I had time to start cooking, boil water for coffee and do some other things, before the IDE was ready to use.

One of many missing features in Netbeans is the ability to generate the serial version ID for a serializable class. With Eclipse, you get the warning all the time, and you can choose between:
  • ignoring it
  • adding the annotation @SuppressWarning("serial")
  • generating the serial version id.

Available plugins for Netbeans
There are two plugins available for Netbeans, that I am aware of : UUIDGenerator and serialVersionUID generator. I only have success with UUIDGenerator (most of the time, I am running the latest development build under Linux and Windows and lately Mac OS Leopard).

Enabling Serialization warnings
Under Tools->Options->Editor->Hints->Standard Javac warnings, select Serialization. That way, you'll see a marker notifying you that you're missing the declaration of a serialVersionUID field.




Generate the serial version ID
You can generate the serialVersionUID using the shortcut Control-ALT-Z. You can copy the generated contents to the clipboard and paste it inside your class.

Tuesday, December 23, 2008

Merry Christmas

XPontus and VFSJFileChooser were released this evening. I wanted to release before Christmas! Still the same stress and anxiety before and after publishing a new version. Now, I need to start advertising in forums, etc.

The last few days, well more nights than days, have been mostly about testing XPontus installers, fixing bugs, reviewing as much code as I could.

Merry Christmas everybody and happy new year!

Tuesday, November 25, 2008

Handling database changes without complete migration

The ORM market
ORM tools are great. Products like Hibernate, JDO, JPA, IBATIS, Torque, and others made life easier for developing database enabled applications.
Using JDBC when your application is database intensive with lots of table can be lots of work especially if lots of your existing code base doesn't provide some DAO classes.
Usually in ORM tools, you map a set of fields to some columns, using XML or annotations, and you're done.

Most J2EE and core Java developers have faced database changes and migration issues at least once. The problem is pretty crucial when your database model is shared by other applications which can't be upgraded(for many reasons).

The concern
  • How to handle database changes which keep happening?
  • Should/Could you stop providing backward compatibility?
  • Is upgrading the database model your only solution?
The application history
  • You have an existing application with a model which has been designed carefully and everything is going well.
  • You have a server side application with a database model and client applications with the same database model as the "main server".
  • A month or a year later, you need to make lots of changes in couple of tables, replace some primary keys, introduce some non null foreign keys, etc.
  • You were using raw JDBC mostly and plain SQL. Now, you would like to use that brand new bleeding edge technology(Hibernate, JPA, Ibatis, name it).
  • Here and there, you might have been using a very old ORM tool which was convenient at the time and is still getting the job done.
Constraints
  • You need to be able to support simultaneously clients(applications) running older and newer versions of the database schema.
  • You cannot force the customer to upgrade for many reasons(hardware dependencies, partner application compatibility, the customer doesn't want to, etc.)
  • You need to keep adding new features which might involve altering again the existing schema
  • Your table contents are now messed up, invalid or irrelevant values here and there because the database column has a "NOT NULL" property.
Possible solutions
  • One might be tempted to maintain different versions of the same database, but let's say I have 100 versions since 1994.
  • Ok, let's use JCR to provide another abstraction level, maybe checking a node property before deciding which class to map, overkill in most cases?
  • "Dear customer, please, upgrade and buy the new pack to be able to use that version which also provide bug fixes and new features"
  • Hum... last resort "Dear customer, you should upgrade because that X, Y, W feature fixes lots of serious security holes which will affect your network"
  • Ok, from now on, every table will be like a key-pair, probably not very wise most of the time, especially if it will involve rewriting most parts of a huge application.
The problem is here, a fix must be delivered!
What I would probably do is :
  • Stick with raw SQL and migrate ORM mappings to JDBC, as needed(if the ORM tools cannot ommit fields), to ignore some properties depending on the client database version. I can insert some dummy values when I have no choice when dealing with "old software clients".
  • Use native SQL queries and JDBC only
  • Use named SQL queries with binding and anything tool that supports it Hibernate, Ibatis, a resultset handler from JdbcTemplate or DbUtils, etc. Some dummy data will need to be inserted when not available(new non null columns).
  • If the problem gets out of hand, way too many changes, I'll probably want to use a non relational database and handle relationships myself(An object or XML database might do, but might not scale)
  • Another solution, would be JCR. I messed with JackRabbit once, and the pain was brought. Performance, concurrency and the API probably improved since then.
I would definintely try to avoid running multiple database versions at the same time. You can easily go from 1, 2 versions and then reach 100.

What would you developers do in such a situation?

Sunday, November 16, 2008

JDK7 changes

I had a surprise this morning while playing with System.getProperties. I am using JDK7 and displaying the system properties in a Swing JTable.
When I don't specify the number of columns I get a null pointer exception. It happens when populating the array contents, not at initialization.
This will throw a NPE:

data = new Object[NB_ROWS][];

This will not

data = new Object[NB_ROWS][NB_COLUMNS];

Code excerpt

public JavaEnvironmentModel()
{
Properties envProperties = System.getProperties();
NB_ROWS = envProperties.size();
data = new Object[NB_ROWS][NB_COLUMNS];

// for google blogger parser, no generics(Entry)
Iterator it = envProperties.entrySet().iterator();

for (int i = 0; it.hasNext(); i++)
{
Entry entry = (Entry)it.next();
data[i][PROPERTY_COLUMN] = entry.getKey();
data[i][VALUE_COLUMN] = entry.getValue();
}
}

Monday, September 01, 2008

Drinking a huge cup of Java

I am working again on XPontus XML Editor. I believe that I'm ready to go for another round.

I am happy to see that there are so many people using the software. Since the intial XPontus release, there are about 10 000 official downloads from Sourceforge and probably 12 000 from other sites grabbing the files directly by HTTP. I didn't expect such a thing at all and it gives me energy to work again on the application.

Here are the main things that I learned since the XPontus inception:
  • Provide a project roadmap even if it's ambiguous or you won't do anything listed :-)
  • Don't use build tools such as Maven if you expect lots of people to contribute to a project, use a simpler but powerful tool such as Ant.
  • If the component is has a public API, finish writing all the documentation before releasing: 20 bugs+good documentation is better than 5 bugs + no documentation, there will be bugs anyway.
  • Advertise a project enough but not too much, people will expect the application to be at the best commercial level even if there are only 2 or 3 people working on the project.
  • Don't assume you know what you're writing because you do code similar things often, read books or articles about the subject whenever you can.
  • Don't try to make a big application without taking time to do it well, or as well as possible
  • Try to think more like a user or a client of an API, rather than a programmer. The application is for users at the moment you distribute it.
  • Make it look good and then make it work : you love what you see and then you love what's inside.
  • Don't necessarily provide many features, few working features are better than a zillion of unstable features.
My goals are simple for the next release:
  • The application will probably be lighter and faster (java reflection abuses, bad programming, race conditions, etc.)
  • Partial rewrite and more use of design patterns(without trying to recognize them everywhere though! ).
  • Fix the current bugs, usability/stability issues. No new features except maybe XPath2, XQuery. VFSJFileChooser will be introduced in the release as an optional plugin.
  • New, simple but powerful API : If you explain the components relationships and the logic of the application, it should almost sounds like common sense, not magic or absurd.
  • One should be able to reassemble, disassemble or extend XPontus without much effort. It looks difficult though, how can someone integrate very easily an application that uses a plugin system?? Last time I integrated parts of JEdit into an application I won't say it was too tough but it wasn't that simple even if I believe that JEdit modules are well written most of the time.

Friday, August 15, 2008

VFSJFileChooser 0.0.3 released

VFSJFileChooser was released today after some additional tests. Nothing much to say, I guess I'll wait for comments.

Tuesday, August 12, 2008

About VFSJFileChooser 0.0.3

I guess I'm ready for the third release of VFSJFileChooser thanks to Stephan Schuster. He helped a lot for that release(bug reports, patches, suggestions). I'll probably release tomorrow after few tests.

Here is the changelog:
  • One noticeable feature is speed :-). VFSJFileChooser was too slow I.M.H.O.
  • Navigation icons are "always" visible now. They didn't show when the java look and feel set, didn't derive from MetalLookAndFeel. I borrowed some icons from Tango and famfamfam which are now the default icons used.
  • Bug fixes for directory selection among other things
  • The VFSUtils class supports the methods setFileSystemManager and setFileSystemOptions. You can set those values at anytime. when the VFSJFileChooser class is instanciated, it checks if VFSUtils has a filesystemmanager set, if not it creates one. Files are always resolved with the FileSystemOptions object in VFSUtils.
  • Upgrade to webdavclient4j (http://webdavclient4j.sf.net) as Jakarta Slide is dead.
  • Cleaner but incompatible API (Enums instead of int fields) : I started to refractor code here and there. I am making full use of JDK5 as VFSJFileChooser is not compatible with jdk14 and older releases. Enums are introduced for few classes which breaks the API. The method setFileselectionMode of VFSJFileChooser now accepts an Enum as parameter. The methods "showOpenDialog" and "showSaveDialog" return an Enum too.
  • Sorting support : The details table has now sorting support again in the jdk5 branch. The jdk5 branch is the most up to date(patches, general improvements, etc.). The jdk5 will become the default branch. I now develop on jdk5 to ensure code compatibility.
The Windows look is still not supported. I'll look at it and see what can be done. For now, I only have my old laptop running Linux so I can't really work on it. Any help in that regard would be appreciated.