Archive for the ‘NetBeans’ Category

NB-managed JDBC connections

Friday, July 16th, 2010

If you are developing a NetBeans-based module or application and need a database connection, you can use “Database Explorer API” to manage them for you. Geertjan has the recipe, but there are two catchs:

  1. DatabaseConnection.getJDBCConnection(true) must NOT be called on EDT;
  2. ConnectionManager.showConnectionDialog MUST be called on EDT.

So, if you want to do something like this:

DatabaseConnection dbc = ConnectionManager
   .getDefault()
   .getConnection(databaseConnectionName);
ConnectionManager.getDefault()
   .showConnectionDialog(dbc);
Connection conn = dbc.getJDBCConnection(true);

You must call line 4 on EDT and line 6 on a RequestProcessor or similar. I had to deal with these scenarios:

  1. Populate a JComboBox on a button’s click event: I created a RP with the sixth line plus statements and result sets - first and forth line are called by Swing on EDT;
  2. Populate an asynchronous ChildFactory: my code is called on CF’s refresh thread. I had to encapsulate showConnectionFactory on EDT using SwingUtilities.invokeAndWait, without using RP for line 6.

It’s strange at first, but works nice (way better than developing my own database connection management).

BTW, if you call DatabaseConnection.getJDBCConnection on EDT you receive an useful warning, but you will suffer if you forget to call ConnectionManager.showConnectionDialog on EDT (no cool warnings to help you). I filled a request for adding this warning in bug 188741.

Using NetBeans Parser API with Navigator API

Sunday, May 30th, 2010

So, you created a new language on you NBP-based application and associated it with a new file type. Now, you need to show your parse result on your Navigator panel. I haven’t found a standard way to do so, but the best shot so far was adding the parser result to the DataObject’s CookieSet. This way you can lookup it in your NavigationPanel’s panelActivated. Easy and fast.

Using NewType on a NetBeans Node

Friday, May 7th, 2010

Given any Node on a NetBeans Platform Application, you can create a “new action” by adding “SystemAction.get(NewAction.class)” to your node’s actions (overriding Node.getAction). You must also override the Node.getNewTypes.

Everything is simple and obvious - except one detail: be careful when overriding the node’s lookup. This works:

public class MyNode extends AbstractNode {
  public MyNode() {
    super(Children.LEAF);
  }
}

But this don’t (you will only get a grayed “Add” menu):

public class MyNode extends AbstractNode {
  public MyNode(MyElement a) {
    super(Children.LEAF, Lookups.fixed(a));
  }
}

The answer is not obvious (I took hours to find out) - you must add “this” to the node lookup, using the old InstanceContent recipe:

public class MyNode extends AbstractNode {
  public MyNode(MyElement a) {
    this(a, new InstanceContent());
  }
  public MyNode(MyElement a, InstanceContent ic) {
    super(Children.LEAF, new AbstractLookup(ic));
    ic.add(this);
    ic.add(a);
  }
}

File attributes on layer.xml

Tuesday, December 15th, 2009

When registering files on NBM’s layer file, you can define attributes, too:

<file name="my-pkg-MyClass.instance">
  <attr name="mystr" stringvalue="value"/>
  <attr name="myint" intvalue="1"/>
  <attr name="mymethod"
    methodvalue="my.pkg.MyClass.myMethod"/>
</file>

They are avaliable as attributes of FileObject (through getAttribute). You can also use special attributes “instanceClass” to define a different class to instantiate or “instanceCreate” to define a factory method. This factory method can receive a map of attributes. I’m using it to customize a generic DataLoader:

public class MyLoader extends MultiDataLoader {
  private MyDataLoader(String secondaryExt) {
    super("MyObject");
    this.secondaryExt = secondaryExt;
  }
  public static build(Map<String, Object> attrs) {
    return new MyLoader(attrs.get("ext").toString());
  }
  ...
}

NetBeans 6.7

Tuesday, June 30th, 2009

Two days ago, NetBeans 6.7 was released. These are the new features I tested:

  • Maven integration was improved with autocomplete and dependency graph;
  • Profiler’s HeapWalker supports OQL - very useful if you do memory tunning;
  • Hudson server management and a status bar icon with build status.

See more at release page!

NetBeans Platform + JPA + Derby embedded

Friday, March 27th, 2009

If you want to use NetBeans Platform and JPA together, there’s a great tutorial on NB’s site. Unfortunatelly, it explains how to do it with an external database and using an external JAR for your entities.

If you want to have a entity module with your classes (instead of an external JAR and a library wrapper), no big deal - it works! You can follow GJ’s tutorial, but you will create a module project instead and you will not have NB’s wizards to help creating entity classes.

I also created a module install on my Derby wrapper, with this line:

FileUtil configRoot = FileUtil.getConfigRoot();
System.setProperty("derby.system.home",
    FileUtil.toFile(configRoot).getCanonicalPath());

This defines where Derby will create database files (user’s dir, in this case).

BTW, if a “no suitable driver found” is thrown, you forgot to add a dependency between JPA wrapper (EclipseLink, TopLink, OpenJPA or Hibernate) and JDBC wrapper.

NetBeans 6.5

Wednesday, November 19th, 2008

Finally, NB 6.5 was released! Grab your copy!

Unfortunatelly the download link is blocked here… I hate proxies…

Adding special chars to NetBeans action name

Tuesday, June 10th, 2008

This one I posted in NetBeans mailing lists. If you want to show a special char (like a slash - “/”) in the action display name (i.e. in the way your user will see it), the right way is to pass it to the AbstractAction constructor (just like the templates of NetBeans):

public class SpecialCopyPasteAction extends AbstractAction {

  public SpecialCopyPasteAction() {
    super(NbBundle.getMessage(SpecialCopyPasteAction.class,
        "CTL_ SpecialCopyPasteAction"));
  }

  public void actionPerformed(ActionEvent evt) {
    // TODO: Implement it
  }
}

And, in Bundle.properties:

CTL_SpecialCopyPasteAction=Special Copy/Paste

Remember that the action name in layer.xml is mapped to a system file name. You cannot put a “/” on file names, even on Windows. This is one of the reasons NB filesystem has the localization feature.

Custom project file structure in NetBeans - new menu item in projects

Monday, May 12th, 2008

In a previous post, I’ve told that I want to use NetBeans and share projects with developers that still use Eclipse. In another post, I added a custom folder to the project view.

Now, I will add a custom menu item to run an ant task outside NB’s build scripts:

  1. Assuming you have a NB module project ready, you just need to add a new entry in your layer.xml:
    <folder name="Projects">
      <folder name="Actions">
        <file name="org-mycompany-MyMenuAction.instance"/>
      </folder>
    </folder>
  2. Create a new class with the same name (org.mycompany.MyMenuAction):
    public final class TransitMenuAction
      extends AbstractAction
      implements Presenter.Popup, ContextAwareAction {
    
      public final static String DISPLAY_NAME =
        NbBundle.getMessage(TransitMenuAction.class,
          "CTL_TransitMenuAction");
    
      private Lookup context;
    
      public TransitMenuAction() {
        this(null);
      }
      public TransitMenuAction(Lookup context) {
        super(DISPLAY_NAME);
        this.context = context;
      }
    
      public void actionPerformed(ActionEvent e) {
        // I'm a menu container... I do nothing
      }
    
      public Action createContextAwareInstance(
        Lookup context) {
        return new MyMenuAction(context);
      }
      public JMenuItem getPopupPresenter() {
        return new PopupPresenter();
      }
    
      /** Dynamic menu factory */
      class PopupPresenter extends JMenuItem
        implements DynamicMenuContent {
    
        public JComponent[] getMenuPresenters() {
          Project proj = context.lookup(Project.class);
          // MyUtils comes from last post
          if (!MyUtils.isMyProject(proj)) {
            // hides this menu
            return new JComponent[0];
          }
    
          JMenu mnu = new JMenu(DISPLAY_NAME);
          mnu.add(new PackageAction(context));
          return new JComponent[] { mnu };
        }
        public JComponent[] synchMenuPresenters(
          JComponent[] items) {
          return items;
        }
      }
    }
  3. And, the PackageAction is:
    public class PackageAction extends AbstractAction {
      private Lookup context;
      public PackageAction(Lookup context) {
        super(NbBundle.getMessage(PackageAction.class,
          "CTL_PackageAction"));
        this.context = context;
      }
    
      /* This action will copy a file and run an Ant task */
      public void actionPerformed(ActionEvent e) {
        try {
          /* Both the file and the build scripts are under
           * the "ant" folder in project directory
           */
          Project proj = context.lookup(Project.class);
          FileObject antFolder = proj.getProjectDirectory().
            getFileObject("ant");
    
          // Read the original file
          FileObject def = antFolder.
            getFileObject("compile-default.properties");
          Properties prop = new Properties();
          prop.load(def.getInputStream());
    
          // Change a little bit
          // TODO: Add this to a property window
          prop.setProperty("compile.lib.ext",
            "C:\\etc\\apache-tomcat-6.0.14\\lib");
    
          // Write to the copy file
          FileObject cpy = antFolder
            .getFileObject("compile.properties");
          if (cpy == null) {
            cpy = antFolder.createData("compile.properties");
          }
          FileLock lock = cpy.lock();
          try {
            prop.store(cpy.getOutputStream(lock),
              "any cool comment");
          } finally {
            lock.releaseLock();
          }
    
          // Now, we will run Ant - grab the script...
          FileObject buildXML = antFolder
            .getFileObject("build.xml");
    
          // ... and run the "package" target
          ActionUtils.runTarget(buildXML,
             new String[]{ "package" }, null);
        } catch (IOException ex) {
          Exceptions.printStackTrace(ex);
        } catch (IllegalArgumentException ex) {
          Exceptions.printStackTrace(ex);
        }
      }
    }
  4. Last, but not least, the project dependencies:
    1. Ant (ActionUtils)
    2. Filesystem API (FileObject and FileLock);
    3. Project API (Project);
    4. UI Utilities API (DynamicMenuContent); and
    5. Utilities API (Lookup)

And, your new project menu item (that calls an Ant task) is ready.

Custom project file structure in NetBeans - adding folder

Monday, May 12th, 2008

In a previous post, I’ve told that I want to use NetBeans and share projects with developers that still use Eclipse. Since we have a custom requirements, I plan to customize the way NB handle these projects.

As you will notice, every NB project customization will go into the layer file, under “Projects” folder. You will be surprised on how easy it goes.

In this post, my task is to make a “config” folder visible under “Projects” tab of NetBeans. Assuming you already have a NetBeans module project ready, steps are:

  1. Create an utility class with this method:
    public static boolean isMyProject(Project proj) {
      // check if it is a nb project
      if (proj.getProjectDirectory().getFileObject("nbproject") ==
        null) {
        return false;
      }
      // check if our custom folder exists
      return (proj.getProjectDirectory().getFileObject("config") !=
        null);
    }
  2. In your layer.xml, create an entry named “Projects / type-of-your-desired-project / Nodes”. You can look into “Important Files / XML Layer / Projects / org-netbeans-modules-xxx-project / Nodes” and right click on it to add a new file - NB will add the “folder” tags automatically;
  3. Inside “Nodes”, add a file named “org-mycompany-MyCompanyNodeFactory.instance” (or rename the file if you created it on step 1);
  4. Now, create a class named “org.mycompany.MyCompanyNodeFactory” that implements NodeFactory. This is a pretty small one:
    public class MyNodeFactoryImpl implements NodeFactory {
      public NodeList createNodes(Project proj) {
        if (MyUtils.isMyProject(project) {
          try {
            MyFilesNode nd = new MyFilesNode(proj);
            return NodeFactorySupport.fixedNodeList(nd);
          } catch (DataObjectNotFoundException ex) {
            Exceptions.printStackTrace(ex);
          }
        }
        // if we can't use it, return an empty list
        return NodeFactorySupport.fixedNodeList();
      }
    }
  5. Create the MyFilesNode. It must be a child of Node. You can do anything you want here (just Google it and you will see what I mean). Mine is:
    public class MyFilesNode extends AbstractNode {
      private static Image smallImage =
              Utilities.loadImage("/path-to/icon-8x8.gif"); // NOI18N
    
      public MyFilesNode(Project proj) {
        super(Children.create(new MyChildFactory(proj), true);
      }
      public String getDisplayName() {
        return "My Files";
      }
      // A bonus: this snippet will merge NB folder icon and
      // your 8x8 icon
      @Override
      public Image getIcon(int type) {
        DataFolder root = DataFolder.findFolder(
          Repository.getDefault().getDefaultFileSystem().getRoot());
        Image original = root.getNodeDelegate().getIcon(type);
        return Utilities.mergeImages(original, smallImage, 7, 7);
      }
      @Override
      public Image getOpenedIcon(int type) {
        DataFolder root = DataFolder.findFolder(
          Repository.getDefault().getDefaultFileSystem().getRoot());
        Image original = root.getNodeDelegate().getIcon(type);
        return Utilities.mergeImages(original, smallImage, 7, 7);
      }
    }

    And, of course, the ChildFactory is:

    public class MyChildFactory extends ChildFactory<String> {
      private Project proj;
    
      public TransitFilesChildren(Project proj) {
        this.proj = proj;
      }
    
      /* Since a node is the visual representation of
       * something else, this method builds the list
       * of "something else"s we want to show. If this
       * was a business application, the "key" could be
       * a value object. In this module, the key will
       * be the name of the custom folder.
       */
      @Override
      protected boolean createKeys(List<String> keys) {
        keys.add("ant");    // another folder - why not?
        keys.add("config");
        return true;
      }
    
      @Override
      protected Node createNodeForKey(String key) {
        // 1. Find the FileObject for the folder
        FileObject fo = proj.getProjectDirectory().getFileObject(key);
        // 2. Find the DataFolder for the FileObject
        DataFolder df = DataFolder.findFolder(fo);
        // 3. Create a custom filter
        DataFilter filter = new MyDataFilter();
        // 4. Get DataFolder children (with filter)
        Children children = df.createNodeChildren(filter);
        // 5. Create a proxy lookup
        Lookup lookup = new ProxyLookup(new Lookup[]{
          df.getNodeDelegate().getLookup(), proj.getLookup()
        });
        // 6. Create a filter node
        return new FilterNode(df.getNodeDelegate(), children, lookup);
      }
    
      /**
       * Filter DataObjects that must not be shown (like .svn folders)
       */
      static final class MyDataFilter implements ChangeListener,
        ChangeableDataFilter {
    
        private final ChangeSupport changeSupport =
          new ChangeSupport(this);
    
        public MyDataFilter() {
          VisibilityQuery.getDefault().addChangeListener(this);
        }
        public boolean acceptDataObject(DataObject obj) {
          FileObject fo = obj.getPrimaryFile();
          return VisibilityQuery.getDefault().isVisible(fo);
        }
        public void stateChanged(ChangeEvent e) {
          changeSupport.fireChange();
        }
        public void addChangeListener(ChangeListener listener) {
          changeSupport.addChangeListener(listener);
        }
        public void removeChangeListener(ChangeListener listener) {
          changeSupport.removeChangeListener(listener);
        }
      }
    }
  6. Now, go toproject properties and add the following modules (you can also search for the needed classes):
    1. Datasystems API (ChangeableDataFilter, DataFilter, DataFolder and DataObject);
    2. Filesystem API (FileObject);
    3. General Queries API (VisibilityQuery);
    4. Nodes API (ChildFactory, Children, FilterNode and Node);
    5. Project API (Project);
    6. Utilities API (ChangeSupport, Lookup and ProxyLookup);

Now, if you run the project, you will notice the new folder under Projects view.