The Source for Java Technology Collaboration
User: Password:



Michael Nascimento Santos's Blog

Swing Archives


Making your components work nicer inside Matisse

Posted by mister__m on February 20, 2008 at 07:20 AM | Permalink | Comments (4)

A co-worker had been developing some nice-looking custom components for a customer project. It was tightly integrated with the backend logic, though, so he tried to use it with Matisse, there were several issues, from class loading errors to slowness, since the component was trying to do its "real task" inside the designer.

So, when he told me that, I immediately recalled a trick I came to know way back in 1999, while I was struggling with Java and Swing for the first time. The java.beans package comes with a class named Beans that comes with a bunch of static utility methods. One of them, isDesignTime(), let your component find out if it's being used in preview mode.

He changed the component constructor to check for design time and skip the "black magic" section. It worked like a charm and he said it was the best tip I gave him last year. So now I've finally had the time to blog again, I thought it would be an interesting tip to share :-)



Desktop development made easier with genesis

Posted by mister__m on June 21, 2007 at 02:43 PM | Permalink | Comments (4)

It's been quite a while since the last time I mentioned genesis here. One of the reasons is I've been working on it a lot and there isn't much time left to blog about it. Hopefully I will be able to do so more often.

We have just released genesis 3.0 after almost two years and a half of development. genesis is all about making the development of enterprise desktop applications easier. To accomplish that, genesis provides UI-related features and also a neat way of building and integrating with the back-end of your application. For now, let's focus on the desktop itself.

genesis 3.0 comes with full support for Swing, SWT and Thinlet. It supports binding, validation, actions and conditions. Let us say that for the desktop it tries to address nearly the same problem space as JSR-295 (Beans Binding), JSR-296 (Swing Application Framework) @Actions and JSR-303 (Bean Validation), but there are enough conceptual differences between them (besides supporting SWT and Thinlet out of the box, of course). I've already explained the basic UI binding functionality last year, so here I will try to show why the genesis approach is better.

First, genesis works with an UI model, instead of plain binding. This means your JavaBean represents the UI data plus the presentation logic behind it. It makes your presentation logic UI toolkit-independent, testable and self-contained. It also doesn't require PropertyChangeListeners at all. Consider the most basic binding example in genesis docs:

@Form
public class LoginForm {
   private String user;
   private String password;

   public String getUser() {
      return user;
   }

   public void setUser(String user) {
      this.user = user;
   }

   public String getPassword() {
      return password;
   }

   public void setPassword(String password) {
      this.password = password;
   }

   @Action
   public void login() {
      System.out.println(getUser());
      System.out.println(getPassword());
   }

   @Action
   public void clear() {
      setUser(null);
      setPassword(null);
   }
}

If you were to do the same with JSRs 295 and 296, you would have to create a JavaBean with PropertyChangeListener support, fire property changes on setters and also create two @Action methods in your view that access your bean. As the number of properties, beans and actions involved in a view increases, your code with genesis would still be self-contained and testable, while JSR-295 would require individual bindings to be produced and your actions would be tightly coupled with Swing, manipulating your beans as value objects.

genesis also comes with some unique features of its own, such as declarative conditions. Let's analyze the classic dependent comboboxes issue: whenever the selected country changes, the state list should be reloaded. Here is what it takes to implement this with genesis:

@Form
public class StateSelectionForm {
   private Country country;
   private State state;

   @DataProvider(widgetName="countryTable", objectField="country")
   public List populateCountries() {
      return // business logic to retrieve countries
   }

   public Country getCountry() {
      return country;
   }

   public void setCountry(Country country) {
      this.country = country;
   }

   @DataProvider(widgetName="stateTable", objectField="state")
   @CallWhen("genesis.hasChanged('form:country')")
   public List populateStates() {
      return // business logic to retrieve states
      // Notice getCountry() will return the selected country
   }

   public State getState() {
      return state;
   }

   public void setState(State state) {
      this.state = state;
   }
}

So genesis will automatically invoke the stateTable @DataProvider whenever the selected country changes.

There are many other features and things to explore and I hope I can show them here in the next few days. The Brazilian Portuguese users list is quite active these days and there is of course an English users list. Check out genesis and let me know what you think.

A public pledge to NetBeans

Posted by mister__m on May 07, 2007 at 04:55 PM | Permalink | Comments (8)

I could not be more disappointed after attending the Swing GUI Building With Matisse: Chapter II presented at NetBeans. It's not a problem with the Swing Application Framework or the NetBeans tooling; it's a problem with freedom of choice, vendor lock-in and a close-minded approach, not community-like friendly by the NetBeans guys.

I hate to make such issues public, but I've been trying to solve this in a civilized way for a year. Almost two years ago I've filed an issue about making Matisse extensible. It was ignored for a long time, but over time, I become hopeful again since the NetBeans roadmap indicated that Matisse would support binding for NB 6.

Over six months ago, I've emailed Tomas Pavek, the NetBeans Matisse lead developer (as far as I know) and Scott Violet, who was the spec lead for JSR-295, Beans Binding, to make sure Matisse implemented it as an abstraction, so it was possible to use Matisse with other binding technologies, such as JGoodies Binding or genesis. This email resulted in a thread in which I explained to Tomas what was needed in the API in order to support other frameworks (basically, abstracting how you interact with the binding "metadata"/API and providing extension points to generate the specific binding API code).

One thing that limited my analysis before was the fact there was no publicly accessible code for JSR-295 or the NetBeans support and I was told that a public preview would be available in January. Well, as most of you know, it has only been made available a couple of days ago.

Today, during the session, I mentioned that while I actually found the tooling fantastic, many folks (just for an example, read this) have all kind of issues with Beans Binding and whether NetBeans would allow its users to work with other binding frameworks, by providing a API that is extensible. While Shannon Hickey, the new JSR-295 spec lead, understood it's not like I'm bashing his work, the NetBeans guys simply said they just want to support the standard. What does it mean to you?

  • If you don't believe that beans binding as a concept is the correct way to make GUI development easier (genesis does what we call "UI binding", which is different, much simpler and straightforward), you're lost
  • If you are working with Java 1.4, sorry; NetBeans will never have a decent solution for you since JSR-295 is targeted at Java 5
  • If you want to use a solution that already works with other UI technologies, such as SWT, sorry, no donut for you
  • If you want to keep the binding logic apart from the UI logic, there is no way
  • If you have an existing project or someone pushed another binding solution in your project, you will actually have to branch Matisse and override it to support it

Obviously, the first answer would be: "hey, but about maintainance?" but no one is asking for a JGoodies or a genesis binding module to become part of NetBeans; I am just asking for the possibility of doing so if needed without branching Matisse. And, heck, I've read Matisse's code: the kind of support I'm asking for would require just a few days to extract the dependencies and create an abstraction based on interfaces and going through the API review process, that would allow people with enough knowledge about other binding solutions to validate it. That is it.

So, to sum up, what I'm complaining about here:

  • Sun has always promised us we would have freedom of choice, but NetBeans will get us locked in beansbinding (vendor lock-in)
  • NetBeans should listen to the community and work with the community. For years, there weren't a standard approach for binding and there are tons of folks working with other frameworks right now that could benefit from having a plugin that allows them to work with their current binding solution.
  • If NetBeans wants to become supported by the community, it shouldn't bite the hand of those that helped to evangelize it. I have been actively promoting NetBeans in Brazil for a few years now, both as a consultant and as a SouJava organization member. I just want to help NetBeans to help its users, but apparently NetBeans developers want to push Sun's solution down our throats.

Here is my request: show me I am wrong. Show you can listen to the community. Show me freedom of choice is not just some marketing rubbish. Please.



Swing made easy with genesis

Posted by mister__m on August 25, 2006 at 10:43 AM | Permalink | Comments (13)

Swing was always known as a powerful, highly configurable UI toolkit. However, not much longer after it was born, it was also regarded as a slow, hard to learn, confusing, hard to program toolkit. Sun first started working on performance and Swing became faster and lighter - if you only knew how to code make a GUI with it. Designing some interfaces could take hours (or days) and since there are many ways of accomplishing (almost) the same thing in Swing, developers would usually get confused or pick the wrong road. Visual designers such as VEP and later Matisse came and made it simple to design the GUI. However, working with Swing still required understanding models, writing listeners and dealing with the many choices offered by the API.

An easier programming model was needed and then binding frameworks started to appear. genesis was born two years ago and it has been supporting GUI-toolkit independent binding for more than a year and a half now. At first, only Thinlet was supported and we were always asked about when it would support Swing. Well, since the beginning of the year a Swing binding has been implemented and now it has finally been released.

What makes the binding implemented by genesis unique is that it doesn't require you to use any "proprietary" components and it doesn't require you to code listeners (neither in the interface nor the JavaBean). So you can design your interface using Matisse, write your JavaBean class and just use a couple of annotations to bring it to life. Let's see how it works in practice. Let's say we would like to implement a login use case. We could code the UI handling JavaBean, called a form, like this :


@Form
public class LoginForm {
   private String user;
   private String password;

   public String getUser() {
      return user;
   }

   public void setUser(String user) {
      this.user = user;
   }

   public String getPassword() {
      return password;
   }

   public void setPassword(String password) {
      this.password = password;
   }

   @Action
   public void login() {
      System.out.println(user);
      System.out.println(password);
   }

   @Action
   public void clear() {
      setUser(null);
      setPassword(null);
   }
}

And bind it to a Swing UI like this:


@ViewHandler
public class LoginSwingView extends JDialog {
   public LoginSwingView() {
      super(new JFrame(), "Login");
      initComponents();

      SwingBinder binder = new SwingBinder(this, new LoginForm());
      binder.bind();
   }
   
   private void initComponents() {
      getContentPane().setLayout(new GridLayout(2, 1));

      JPanel dataPanel = new JPanel();
      dataPanel.setLayout(new GridLayout(2, 2, 5, 5));

      JLabel labelUser = new JLabel();
      labelUser.setText("User");
      dataPanel.add(labelUser);

      JTextField user = new JTextField();
      user.setName("user");
      dataPanel.add(user);

      JLabel labelPassword = new JLabel();
      labelPassword.setText("Password");
      dataPanel.add(labelPassword);

      JPasswordField password = new JPasswordField();
      password.setName("password");
      dataPanel.add(password);

      getContentPane().add(dataPanel);

      JPanel buttonPanel = new JPanel();

      JButton login = new JButton();
      login.setText("Login");
      login.setName("login");
      buttonPanel.add(login);

      JButton clear = new JButton();
      clear.setText("Clear");
      clear.setName("clear");
      buttonPanel.add(clear);

      getContentPane().add(buttonPanel);

      pack();

      setLocationRelativeTo(null);

      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent e) {
            System.exit(0);
         }
      });
   }

   public static void main(String args[]) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            new LoginSwingView().setVisible(true);
         }
      });
   }
}

So, as this example shows, genesis binds JavaBeans properties to widgets such as JLabels, JTextFields and others based on their names. This is just the default behaviour; it is possible to determine which component to bind to a property using any other technique, as well as to ignore a property. Methods annotated with @Action can be bound to JButtons and other widgets following the same logic used for properties. You can find out more about how the binding works by reading the docs.

Besides that basic binding features, genesis also makes it possible to enable/disable components based on conditions (using @EnabledWhen), making them visible/hide them (using @VisibleWhen), populate tables, combos, lists etc. with a java.util.List or an array (using @DataProvider) and much more. It is also fully compatible with Java 1.4 and has many other non-UI related features, such as transparent remoting (which I blogged almost two years ago).

To finish the big announcement day, a SWT binding is now in HEAD and should be released in the next few days. So if you are developing a desktop application that uses either Swing, SWT or Thinlet, take a look at genesis.



A tricky issue with java.awt.Font

Posted by mister__m on March 21, 2005 at 11:40 AM | Permalink | Comments (30)

If your code or code you use relies on loading fonts by name, you may face severe limitations when trying to use your application in a different environment than the one you performed your tests. Although many of us are aware of the fact specific fonts may not be installed on a machine, trying to work around this problem may prove to be more difficult as it seems at first.

Most code that loads fonts by name use either the getFont(String name) or the getFont(String name, Font defaultFont) method from the java.awt.Font class. What happens when there is no font registered in the OS whose name is name? In the first case, null is returned; for the second method, the defaultFont parameter is returned. This will probably lead to unexpected results. Even when the font exists in the OS, it may not be the same font you were expecting. Yes, that is right: there are different font files for "common" fonts. So, how can you load a specific font and make sure the one you expect will be used to render text?

The Font API allows you to programatically load a font from a java.io.InputStream using the createFont(int fontFormat, InputStream fontStream) method. Although the only font format supported is Font.TRUETYPE_FONT, this should be enough for most situations, since if you are not using a TrueType font, there is no guarantee it will render the same in different platforms and/or devices. Unfortunately, there is a problem that prevents this solution from working most of the times: there is no way to associate a loaded font with a name. This is somewhat surprising, since the loaded instance even returns the correct name when its getName() method is called.

So, how to solve this problem? If you wrote the code or can modify its source, you simply need to rewrite the parts that load a font by name so that they delegate to a method that looks up the instance in a "registry", such as a Map. The problem gets worse, though, when you are working with third-party software. If you can modify the binaries in runtime, using AOP to work around this problem is a possible solution. However, sometimes the third-party license prohibits this kind of modification and then there isn't much the developer can do besides asking the vendor to rewrite the API.

The long term fix for this issue would be to add a register(String name, Font font) method to Font in Mustang. Should I propose an enhancement about this? I would like to know what you think.



Announcing genesis

Posted by mister__m on December 13, 2004 at 10:27 AM | Permalink | Comments (1)

A few weeks ago, we've silently released the first public beta version of genesis. But what is genesis about?

genesis is open-source software (LGPL) and its main objective is to allow you to build powerful, scalable applications in a simple, productive and testable way. Although its long term goals are much more ambitious, right now it focuses on two main areas:

  • UI programming: your form is just an annotated POJO and... that is it; no further requirements. Annotations will allow you to automatically populate comboboxes and tables, to enable/disable widgets, make them visible or not, clear fields based on conditions etc., in a declarative way. Programming a complex UI becomes a very simple task, which is one of the main reasons some people still avoid using Java on the desktop. The current implementation uses Thinlet as the view technology, but other APIs, such as Swing, will be supported soon.
  • Business components: many "modern" frameworks support a POJO model for business components, but there are still several limitations - a business object cannot be directly instantiated, but rather injected or looked up using a factory, for example, or you have to expose your POJO using an interface or otherwise you won't be able to take advantage of most facilities offered by these frameworks. genesis takes a different road: you can use new to instantiate your components and they don't have to implement or be exposed by any interface in order to take advantage of genesis' facilities. In runtime, depending on your configuration, genesis may use EJB technology to execute your POJOs as if they were Stateless Session Beans or you can work in local mode (which is cool for some desktop applications). You don't have to change a single line of code to switch execution modes, but just use a different target to build your application. Current genesis features include transparent remoting, transactional support and DI (dependency injection) for Hibernate. General DI will be supported soon.

genesis does not try to reinvent the wheel, but rather builds on top of several other open-source projects to deliver its functionalities. Besides Thinlet, this release relies heavily on AspectWerkz and AOP to implement a flexible core so that new ways to do remoting or to configure a form - using xml, for example - are easy to write and don't require any changes to existent genesis code. So, if you are looking for practical ways of using AOP, check out genesis sources.

genesis is already running on production environments and, in one of them, the server-side application is capable of handling more than 1.125 million transactions per day with a single box. You can access genesis docs and download it at https://genesis.dev.java.net

UPDATE: genesis was the 2nd largest java.net project by commits last month according to this report, so it is really worth a quick look. ;-)





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds