fredag 28 september 2012

Maturing projects...

It has been a while since my last post, so I just want to drop a note to let any- and everyone know that I continue to use the Netbeans RPC. This last year, our product has matured and reached its first and second thousand user. And we continue to have a steady growth of our user base. This is very nice for our team and the notion of actually daring to call the project a success is lurking around the corner...


And also, very exciting to me: I was invited by friend and Oracle Principal Product Manager Geertjan Wielenga to join him at JavaOne in San Francisco to talk about our Netbeans based product and development. Very exciting!!

Sooo, yeah, I better start packing. I leave in 6 hours...

Ps: I have a couple of blog topics that I'll try to get around to as soon as possible. They include building Netbeans RCP projects in Jenkins (including installers) and much else...

torsdag 29 september 2011

Review of Java EE 6 Development with NetBeans 7



One could sometimes argue that a disadvantage of open source community projects is that relevant documentation is written and maintained by people who, while often possessing great technical skills, may or may not excel in writing. Often too, one finds that the documentation is written to cover the authors specific project which often leads to the documentation either trying to cover ALL ASPECTS OF PROGRAMMING or, perhaps even worse, paint sketchy images of what might be possible to produce but leaves the reader frustrated with lack of information.

In this regard, Java EE 6 Development with NetBeans 7 is a gem! In the beginning of the book it clearly states what it will cover and for whom it is written and then goes on to do just that!



”The book is aimed at three different types of developers:

  • Java developers (not necessarily familiar with NetBeans) wishing to become proficient in Java EE 6, and who wish to use NetBeans for Java EE development.
  • NetBeans users wishing to find out how to use their IDE of choice to develop Java EE 6 applications.
  • Experienced Java EE 6 developers wishing to find out how NetBeans can make their Java EE 6 development easier. ”



And after reading it you will know what NetBeans can do for you regarding most popular Java EE development needs. It's as simple as that.



You will not be lead astray on any strange off-topics but rather efficiently be guided, from chapter to chapter, through the various available options and their pros and cons. Since I think a lot in images, the books extensive use of screen shoots and code examples (yes, of course they are available online) helped fast understanding and even though I've been using NetBeans professionally since 3.6, I found myself wanting to “try it out” after reading every chapter. It was only the fact that I didn’t have access to my workstation during the time of reading that made it possible for me to finish the book in a reasonable time.



What you will not get however, is any in depth arguments of why you would choose a certain technical Java EE technique. In my opinion, the reader should know that this book concerns itself more about how a certain desired objective is met rather than why you would want this or that. If you want deeper knowledge of, for instance, why you would want to “Generate Session Beans from JPA entities” perhaps you should find a book about that first and then grab this one as soon as you are ready to get your hands dirty.



Oh, and by the way, any aspiring NetBeans user will benefit from reading the chapters about how the IDE works in general regarding everything from Code Completion and built-in Project and File wizards to the integrated Debugger and Profiler tools. Not convinced? Here is a sample chapter. So there you have my two cents, I'm clicking the New Project button now...


måndag 21 februari 2011

Pdf viewer as a JPanel

The project I'm working on is run by a book publishing company in Sweden. So for them to want to be able to provide integrated support for pdf viewing in the product seemed fairly obvious. After spending quite some time googling for different derivatives of "open source pdf jpanel swing" and such, I first found PdfRenderer, an original SwingLabs project that seemed to not be worked on at that time (but perhaps will be restarting soon? Keep an eye on that link if you're interested, I know I will).

There are multiple options if you're looking for commercially licensed products and, what seem like, good ones too. But I wanted open source. After a while I found http://www.icepdf.org and it seemed like their mozilla public licensed ICEpdf project offered what I was looking for.

What I wanted was fairly simple. I had a pdf file and wanted a viewer panel extending JPanel.

And voila, this is what my code ended up looking:


import org.icepdf.ri.common.SwingController;
import org.icepdf.ri.common.SwingViewBuilder;


InputStream inputStream = MY_PDF_AS_STREAM;
SwingController controller = new SwingController();
SwingViewBuilder factory = new SwingViewBuilder(controller);
JPanel viewerComponentPanel = factory.buildViewerPanel(); 
MY_MAIN_PANEL.add(viewerComponentPanel, BorderLayout.CENTER);
controller.openDocument(inputStream, "", "");


And this is what I got, in our Netbeans RCP application:


As you can see, all expected icons are there... The pdf functionality includes everything I expected (as search, text selections and links). All in all, I think ICEpdf is a pretty nice acquaintance.



Ohh, and if you read this far, you might like what I used for selecting what pdf to view. I found http://pwnt.be/lab/jflow/ I know that it has been done before, but in this case I actually think a Cover Flow selection component actually adds some value as well as some sexy. The excellent JFlow project required only very light tweaking to fit my needs, so you might want to check that out!

Yes, I only offered two books to choose from...

Until next time!

torsdag 17 februari 2011

Screen Shots

Here are some screen shots of our application and how the art director envisioned it using photoshop. The application is an accounting software. In escence a program for smal to medium sized organisations to keep their books.

















Another photoshop image:

















And these are actual screen shots of our application where its at today:

Info screen




 Overview


And using pretty much nimbus defaults (icons are the same which will have to be addressed)

It's this work that made me write this post about tweaking Nimbus



Tweaking Nimbus

I'm just back from JFokus in Stockholm and a very good meeting with people sharing my Nb RCP interest. We might have been the only people at that Steak House that particular evening trading netbeans advice...

******** EDIT: 2011-02-19 ***************
Thanks to Geertjan's article I figured out how to make a complete redraw in real time.
Actually setting the UI to a different laf and then straight back again actually redraws the entire UI like I wanted to below.

public static void updateUI() { 
  try { 
    UIManager.setLookAndFeel(
      "javax.swing.plaf.metal.MetalLookAndFeel");
    UIManager.setLookAndFeel( 
      "com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
  } catch (Exception ex) {}
  UIManager.put(nimbusBlueGrey, YOUR_NEW_COLOR);
  SwingUtilities.updateComponentTreeUI(jFrame); 
}
Strange, but it works.
************************************

At that dinner I had the opportunity to show and discus my current project's struggles with adopting the Nimbus Look&Feel to what our art director have created in photoshop.

The thing is that we want the user to be able to change his or her settings in real time so we want to change the nimbus drawing properties in real time programatically.

This can typically be done like this:

for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()){
  if ("Nimbus".equals(info.getName())) {
    UIManager.setLookAndFeel(info.getClassName());
    UIManager.put("nimbusBase", new Color(21, 21, 21));
    UIManager.put("nimbusBlueGrey", new Color(44, 44, 44));
    UIManager.put("control", new Color(40, 40, 40));
    UIManager.put("textText", Color.WHITE);
    jFrame = (JFrame) WindowManager.getDefault().getMainWindow();
    SwingUtilities.updateComponentTreeUI(jFrame);
    break;
  }
}

In my current project I run code similar like the one above in my module Installer class like this:

public void restored() {
  WindowManager.getDefault().invokeWhenUIReady(new GuiInitialiser());
}

The code above is what gets done in the GuiInitialiser.
However, and this is my main concern right now, this code behaves quite differently when I, instead of setting tha laf programatically like above, set it by adding

run.args.extra=--laf com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel

in my project's platform.properties file. The problem seem to have something to do with performing a COMPLETE and clean redrawing of the entire project UI when setting it programatically. To illustrate this I have created a small project that illustrates what I'm talking about. If you run it, you will find that the main menu "row" at the top of th screen is quite ugly.


This is because I set

  UIManager.put("nimbusBase", Color.MAGENTA);
  UIManager.put("nimbusBlueGrey", Color.CYAN);

before updating the UI the first time. However, changing the same properties after that does not redraw that part of the UI.

In this program there are some predefined settings that you can try but also a text field where you can try to change the color of any other nimbus property that you might like. However, due to the Nimbus logic of derived colors (which by the way is illustrated quite well in the application above - set nimbusBase to red and watch how red is implemented on for instance the comboBox) it is quite hard to find a color property that just instantly does what you want/expect it to.


So if your interested, you can download the project and play around with it. I hope that both the application and its source can benefit someone. Obviously this application should have been presented as a java web start, but that rendered even differently which someone also should feel very free to get back to me about...

So, if you like this, download it and experiment. Run it as is or with the laf-setting in the platform-file instead. Run it as JNLP.

Over and out!

tisdag 14 december 2010

Extbrowser in Netbeans platform jnlp / webstart app

The project I'm working on right now is using java web start to deploy. It is also using the extbrowser platform module that enables urls to open in the users default browser. Setting up the building of this project in netbeans, however, was not very straight forward. If it can be done more easily, I'm all ears, but this is how I did it. By the way, I'm in a Ubuntu 10.04, Netbeans 6.9.1, java 1.6.0.22 environment building for Win/OsX/Linux platforms. The below solution is verified on ubuntu 10.04/10.10, Win 7 Professional and Os X 10.5.8

1) After including External HTML Browser (found in project properties -> Libraries -> ide modules), building jnlp application does not work until modules/lib/extbrowser.dll, modules/lib/extbrowser64.dll is added to the verifyexcludes line in jnlp.xml found in the netbeans-6.9.1/harness directory. Now you can build the jnlp project again.

2) Extbrowser uses native code to access default browser, and to facilitate this in Windows, two files are included in your netbeans installation (netbeans-6.9.1/ide/modules/lib/extbrowser.dll & extbrowser64.dll). The main part of the rest of this solution is about including these two files in your project and making them available to your run-time when necessary.

2.1) I wrote the following class to handle the dll relating tasks. It basically takes the two dll's and saves them to the local filesystem to make them available to windows and then loads them (since I really only load the one corresponding to 32 or 64 bit os, I should only save one - good opportunity for refactoring...). At //http://nicklothian.com/blog/2008/11/19/modify-javalibrarypath-at-runtime/ I found an excellent piece of code to make this newly saved file (at least its containing directory) a part of the java.library.path at runtime. This has to be done to be able to load the dll. Depending on file system rights, the files can of course be saved to a directory that is already a part of java.library.path and in this case, this part should, of course be omitted...

public class WindowsDllLoader {

    public static void loadExtbrowserDlls(boolean os64Bit) {
        if (!new File(System.getProperty("user.home") + "extbrowser.dll").exists()
                || !new File(System.getProperty("user.home") + "extbrowser64.dll").exists()) {
            try {
                copyFromJar("extbrowser.dll");
                copyFromJar("extbrowser64.dll");
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        }
        loadDll(os64Bit?"extBrowser64.dll":"extBrowser.dll");
        try {
            addDir(System.getProperty("user.home"));
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    private static void copyFromJar(String file) {
        try {
            InputStream in = WindowsDllLoader.class.getClassLoader().getResourceAsStream(file);
            File fileOut = new File(System.getProperty("user.home") + file);
            OutputStream out = new FileOutputStream(fileOut);
            while (true) {
                int data = in.read();
                if (data == -1) {
                    break;
                }
                out.write(data);
            }
            in.close();
            out.close();
        } catch (Exception e) {
            System.out.println(e.toString());
        }
    }

    private static void loadDll(String file) {
        System.load(System.getProperty("user.home") + file);
    }

    public static void addDir(String s) throws IOException {
        try {
            Field field = ClassLoader.class.getDeclaredField("usr_paths");
            field.setAccessible(true);
            String[] paths = (String[]) field.get(null);
            for (int i = 0; i < paths.length; i++) {
                if (s.equals(paths[i])) {
                    return;
                }
            }
            String[] tmp = new String[paths.length + 1];
            System.arraycopy(paths, 0, tmp, 0, paths.length);
            tmp[paths.length] = s;
            field.set(null, tmp);
            System.setProperty("java.library.path", System.getProperty("java.library.path") + File.pathSeparator + s);
        } catch (IllegalAccessException e) {
            throw new IOException("Failed to get permissions to set library path");
        } catch (NoSuchFieldException e) {
            throw new IOException("Failed to get field handle to set library path");
        }
    }
}

2.2) The WindowsDllLoader class was put in a separate project that also included the two dll's. The pre-build jar from this project was included in the platform app to be a part of the jnlp build signing process. When it was signed in this shape, it was called ext-windows.jar ( not a deliberate choice by me - maybe because of where I placed the included jar...). Anyways, this jar file had to be found by my jnlp-files when loading the app from the web server, so I modified the org-netbeans-modules-extbrowser.jnlp file in my build:

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE jnlp PUBLIC "-//Sun Microsystems, Inc//DTD JNLP Descriptor 6.0//EN" "http://java.sun.com/dtd/JNLP-6.0.dtd">
<jnlp spec='1.0+' codebase='http://***my_app***/netbeans/'>
  <information>
   <title>External HTML Browser</title>
   <vendor>NetBeans</vendor>
   <description kind='one-line'>Enables integration of external web browsers with the IDE.</description>
   <description kind='short'>The External Browser module enables the integration of external web browsers with the IDE for viewing Javadoc documentation and testing applets and web (JSP and servlet) applications. It provides an alternative to the built-in HTML Browser.</description>
  </information>
<security><all-permissions/></security>
  <resources>
    <jar href='org-netbeans-modules-extbrowser/org-netbeans-modules-extbrowser.jar'/>
    <nativelib href="http://***my_app***/app/org-jdesktop-swingx/ext-windows.jar" />
  </resources>
  <component-desc/>
</jnlp>

2.3) I then save a copy of this and included it in the project and added the following line to the project build.xml:
 <copy file="org-netbeans-modules-extbrowser.jnlp" overwrite="true" todir="${release.dir}/netbeans"/>
Where this file overwrites the vanilla version of the jnlp file that does not include the nativelib reference or directs the download to the signed version of the dll-containing ext-windows jar file.


2.4) I use the following code to call the above methods (and this code, in turn, is run in the application's module installer at start up):
public class ExtBrowser {

    public static void load() {
        if (Utilities.isWindows()) {
            WindowsDllLoader.loadExtbrowserDlls(is64BitWindows());
        }
    }

    private static boolean is64BitWindows() {
        // should be 32 or 64 bit, but it may not be present on some jdks
        String sunDataModel = System.getProperty("sun.arch.data.model"); //NOI8N
        if (sunDataModel != null) {
            return "64".equals(sunDataModel);
        } else {
            String javaVMName = System.getProperty("java.vm.name"); //NOI8N
            return (javaVMName != null) && (javaVMName.indexOf("64") > -1);
        }
}

I think this was pretty much it. It requires some more testing on more 32/64 bit cases, but I think this is how its done. Th solution can most probably also be used to include other native codes/dlls and such.



Oh and here is another nifty piece of code that I came across (at http://forums.netbeans.org/post-71323.html) that is very useful if you just want to open the system default browser programatically. This has nothing to do with the ExtBrowser logic and can, as far as I know, be used in any kind of project:
    static final String[] browsers = {"firefox", "opera", "konqueror", "epiphany","seamonkey", "galeon", "kazehakase", "mozilla", "netscape", "chrome"};
    public static void openURL(String url) {
        String osName = System.getProperty("os.name");
        try {
            if (osName.startsWith("Mac OS")) {
                Class<?> fileMgr = Class.forName("com.apple.eio.FileManager");
                Method openURL = fileMgr.getDeclaredMethod("openURL",
                        new Class[]{String.class});
                openURL.invoke(null, new Object[]{url});
            } else if (osName.startsWith("Windows")) {
                Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
            } else { //assume Unix or Linux
                boolean found = false;
                for (String browser : browsers) {
                    if (!found) {
                        found = Runtime.getRuntime().exec(new String[]{"which", browser}).waitFor() == 0;
                        if (found) {
                            Runtime.getRuntime().exec(new String[]{browser, url});
                        }
                    }
                }
                if (!found) {
                    throw new Exception(Arrays.toString(browsers));
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("Error attempting to launch web browser", e);
        }
    }