Pondělí VIII 29, 2005

Plug-in Development Support Improvements



This is very nice. Although it's a way how to make writing NetBeans plug-ins or application above NetBeans less exciting - less hacking is necessary, things will just work... etc. Wizards are not for 100% geeks, are they? :-)

Pondělí VIII 22, 2005

My New Toy Is... a Processor Simulator



I'd like to show you two screenshots. Take a look at them and compare them (click to open maximized):


Original application


Ported to NetBeans platform


What I did is that I took the original application for simulation of processor and rewrote the GUI part in 2 days to work above NetBeans. I'm not done with it yet but the basic functionality is there. My impression is that the application got a really great look and somehow became professional-looking. The strange thing is that I didn't rewrite any of the components - the processor components are the same. I just reused NetBeans' components like editor, menu, toolbar and output window.

That were the changes in it's UI, but the more interesting part is which features I got for free from NetBeans, which the original application didn't have:
  • Editor with search & replace, undo, redo, go to line, support for abbreviations and macros.
  • Setting of keyboard shortcuts.
  • Possibility to move windows, dock them, maximize editor and sliding windows.
  • Automatic management of options in userdir.
  • Customizable toolbars.
  • Printing of source code.
  • Output window with possibility of many windows opened, cleaning of output window, search, saving of output.
  • Improved modularity of system, possibility to add additional features as modules, their uninstalling, updating and management of versions and dependencies.
  • Availability of help window.
  • Splashscreen, support for various look & feels, drag&drop functionality...
Now I have a question - do you know about an easier way of adding so many features in 2 days?! :-)

Ok, this is getting quite a marketing mambo-jumbo, I should not forget that I am an engineer... there are also some drawbacks:
  • Increased start-up time (approx. 10 secs).
  • Increased application size (+approx. 12 MB).
  • Increased memory footprint (+approx. 10 MB).
However these drawbacks IMO are small compared to the set of functionality I gained for free. Programmer time is very expensive... and I wonder how many man-months it would take me to develop all the features I leveraged from NetBeans platform.

Neděle VIII 21, 2005

Hacking NetBeans #5 - Boundling a Library with Your Module



Unless you're doing something very standard there is a probability you'll need to include an additional library with your module. At first it's a good idea to take a look if the library is not included with the IDE (in module/ext subdirs of individual clusters). In that case you can just declare dependence on the module which boundles the library and you're done.

However you probably won't be so lucky and you'll need to provide the library yourself. As I was told it's better to provide your library as a single module - this way others can then depend on this module if they need the library. Some useful info about this is here. There is a new wizard in NetBeans Plug-in Modules | Library Wrapper Module Project which can do most of the job for you if you want to create a library wrapper module.

The other possibility is to add the library directly into your module which has an advantage that you provide single a nbm with everything. Create a subdirectory e.g. external and place the jar into it. It's a good idea to change the name of the jar, e.g. from mail.jar into mail-1.3.2.jar so that other people can find out what's its version easily.

In order to get the jar copied into your nbm, you need to add something like this to your build.xml:

    <target name="netbeans-extra" depends="init">
        <mkdir dir="${cluster}/modules/ext"/>
        <copy todir="${cluster}/modules/ext">
            <fileset dir="external">
                <include name="mail-1.3.2.jar"/>
            </fileset>
        </copy>        
    </target>

This way you create a modules/ext subdir in your nbm and copy necessary libraries into it. You may need to get the library on classpath, so that it's visible for other modules. The classpath extensions are specified in project.xml like this:

            <class-path-extension>
                <runtime-relative-path>ext/mail-1.3.2.jar</runtime-relative-path>
                <binary-origin>external/mail-1.3.2.jar</binary-origin>
            </class-path-extension>

To add more jars to classpath just use additional class-path-extension tags. You get the same effect if you add the Class-Path: property into your manifest, but this method is deprecated - use project.xml for this purpose.

You will also need the IDE to recognize the jar, so that you can use code completion, methods called from the library don't get underlined as errors, etc. This is done by adding a property into the project.properties file:

cp.extra=\\
    external/library1.jar:\\
    external/library2.jar

Similar properties are used for tests - test.unit.cp.extra and test.qa-functional.cp.extra, so if the IDE doesn't recognize the API provided by the library, you're probably missing them.

Note that there are special requirements on libraries hosted on www.netbeans.org, this document explains what needs to be done. The document explains everything necessary about scrambling and unscrambling, how to add a license to the nbm and few other legal topics (legal stuff is boring but you need to be correct).

I've been fighting quite a lot with adding a library to ant's classpath, this is a larger topic so it will deserve another post. Btw, if there's any area about module development you find fuzzy or underdocumented, let me know, I like finding out how things work. I can't guarantee that I will understand everything perfectly, but just writing about how things work for me might help other people out.

Čtvrtek VIII 18, 2005

Hacking NetBeans #4 - Create Your Own Options



One of the things you'll probably want to do if you're writing your plug-in is to have your own options. NetBeans can take care of their lifecycle - they are loaded on IDE startup and if changed they are saved to the userdir. You have probably noticed the small dialog which appears when you close IDE, saving options of all modules is one of the actions performed. So the IDE takes care about management of your options - you don't have to write quite a lot of code which is already a part of NetBeans.

How to add your options? As usual, you'll have to start with the layer.xml file:

    <folder name="Services">
    	<file name="org-netbeans-modules-mymodule-MyModuleSettings.settings" url="MyModuleSettings.xml" />
    </folder>

This way you add to the folder Services a settings file, which is the MyModuleSettings.xml file in the same directory as your layer.xml. The MyModuleSettings.xml specifies the class which will be used for options of your module:

<?xml version="1.0"?>
<!DOCTYPE settings PUBLIC "-//NetBeans//DTD Session settings 1.0//EN" "http://www.netbeans.org/dtds/sessionsettings-1_0.dtd">
<settings version="1.0">
    <module name="org.netbeans.modules.mymodule"/>
    <instanceof class="org.openide.util.SharedClassObject"/>
    <instanceof class="org.openide.options.SystemOption"/>
    <instanceof class="org.netbeans.modules.mymodule.MyModuleSettings"/>
    <instance class="org.netbeans.modules.mymodule.MyModuleSettings"/>
</settings>

The MyModuleSettings.xml files specifies the class which will be instanciated when your options are needed. This class extends the SystemOption class. As specified in xml file, it is located in org/netbeans/modules/mymodule/MyModuleSettings.java. The class has a special format:

public class MyModuleSettings extends SystemOption {
    // unique serial number for serialization
    static final long serialVersionUID = 34343534534534573L;    

    // the names of options you'll use
    public static final String PROP_HOSTNAME = "hostname";
    public static final String PROP_PORT = "port";

    // initialization method
    protected void initialize () {        
        // calles SystemOption's initialize
        super.initialize();
        
        // default values
        putProperty(PROP_HOSTNAME, "localhost", true);
        putProperty(PROP_PORT, "4976", true);
    }

    // this method serializes the options 
    public void writeExternal (ObjectOutput out) throws IOException {
        out.writeObject(getProperty(PROP_HOSTNAME));
        out.writeObject(getProperty(PROP_PORT));
    }

    // this method deserializes the options
    private void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        putProperty(PROP_HOSTNAME, in.readObject(), true);
        putProperty(PROP_PORT, in.readObject(), true);
    }

    // readable name of options
    public String displayName() {
        return "My Module's Options";
    }

    // help context
    public HelpCtx getHelpCtx () {
        return new HelpCtx(MyModuleSettings.class); 
    }
    
    // set Hostname Option
    public void setHostname(String hostname) {
        putProperty(PROP_HOSTNAME, hostname, true);
    }

    // get Hostname Option
    public String getHostname() {
        return (String) getProperty(PROP_HOSTNAME);
    }         

    // set Port Option
    public void setPort(Integer port) {
        putProperty(PROP_PORT, host, true);
    }

    // get Port Option
    public Integer getPort() {
        return (Integer) getProperty(PROP_PORT);
    }         
}

This is basicly all you need to make the options work - whatever you write using setXXX method will be saved to the userdir (all options need to be serializable objects). Your module can read the options by calling the getXXX methods and they're loaded. You can create this way as many options as you want (but keep in mind it's better to keep the number of options small - we know what we are talking about). As you know, things get always complex, one of the first things you'll need to support various versions. But that's mostly solved by just writing to the first position an Integer with version and add a switch to decide what to do according to this version.

So this code handles reading and writing of options, but you'll probably also want them to be displayed. To do that, you need at first to create a beaninfo class, which will add additional information to your options. The name of the beaninfo class is always name of your class + BeanInfo.

public class MyModuleSettingsBeanInfo extends SimpleBeanInfo {
    
    public PropertyDescriptor[] getPropertyDescriptors() {
        try {
            PropertyDescriptor hostname =
                    new PropertyDescriptor("hostname", MyModuleSettings.class);
            hostname.setDisplayName("Hostname");
            hostname.setShortDescription("This option sets the hostname.");
            PropertyDescriptor port =
                    new PropertyDescriptor("port", MyModuleSettings.class);
            port.setDisplayName("Port");
            port.setShortDescription("This option sets the port.");
            return new PropertyDescriptor[] {hostname, port};
        } catch (IntrospectionException ie) {
            System.err.println("Introspection exception thrown: "+ie);
            return null;
        }
    }

    public Image getIcon(int type) {
        if (type == BeanInfo.ICON_COLOR_16x16 || type == BeanInfo.ICON_MONO_16x16) {
            return Utilities.loadImage(
                    "/org/netbeans/modules/mymodule/MyModuleIcon16.gif");
        } else {
            return null;
        }
    }
}

This way you set the names of the options and their descriptions, displayed in Tools | Options. You also specify an icon which is displayed in the panel. The last missing piece is wiring these options into the UI. Again, we'll use our favourite layer.xml file:

    <folder name="UI">
        <folder name="Services">
          <folder name="IDEConfiguration">
            <folder name="ServerAndExternalToolSettings">
              <file name="org-netbeans-modules-mymodule-MyModuleSettings.shadow">
                <attr name="originalFile" stringvalue="Services/org-netbeans-modules-mymodule-MyModuleSettings.settings"/>
              </file>
            </folder>
          </folder>
        </folder>
      </folder>

Yes, the options in Tools | Options are created like this, simply via definitions in xml files. This part defines that your options will be displayed in IDE Configuration | Server And External Tools Settings. The .shadow is actually a link the original xml settings file which specifies the class implementing your Options.

And that's it - your plug-in is now plugged into Tools | Options.

Pátek VIII 12, 2005

Hacking NetBeans #3 - Registering Files in Default Filesystem



Last time I blogged about executing ant tasks from the IDE. It was done in an ugly way - by generating a temporary ant script for execution. A nicer way is to use an existing ant script, which will be placed somewhere in the module's jar.

But the question is: how to access a xml file which is located somewhere inside my module? Or any other arbitrary file?

Well, you need to register this file in NetBeans layered filesystem. To achieve this, add something like this to the layer.xml file:

    <folder name="Services">
        <folder name="MyModule">
            <file name="my_script.xml" url="resources/copy_script.xml"/>                
        </folder>
    </folder>

By this I am declaring, that the export_script.xml file is located in resources/export_script.xml inside my module. This path is relative to the path of the layer.xml files. So these files are:

org/netbeans/modules/mymodule/layer.xml
org/netbeans/modules/mymodule/resources/copy_script.xml


I also declare that my module can find the xml file in Services/MyModule/copy_script.xml on the default NetBeans filesystem. To access the file, I use:

        FileObject sfo = Repository.getDefault().getDefaultFileSystem().findResource("Services/MyModule/my_script.xml");

Now that I have a reference to the FileObject of the xml file I can do something useful with it. I can e.g. convert it to a regular file by using FileUtil.toFile(sfo). To execute the ant script you can use my previous example but you will need to copy the ant script to the disk at first and call RunTarget() method on a FileObject which resides on disk.

Confused about NetBeans filesystems and the layer.xml file? I was, too. A good and short reading is here from Tim. You can also use the sysfs plug-in to explore the default filesystem. It's full of surprises :-)

Sobota VIII 06, 2005

My Second NetBeans Plug-in: Project Packager



During my vacation next to lots of biking and having fun with friends I also worked on my second plug-in called Project Packager. Unlike my previous Googlefight plug-in this one is almost useful. It can be used to export projects as zip packages, send them by e-mail and to unpack them for opening. Here's a screenshot of the export dialog:


Exporting of projects and sending by e-mail

I've found out that exploring NetBeans APIs can be quite fun and will share some of the experiences during my next posts, especially all those tricky things. It happened to me several times that I was trying to solve some problem and then after few hours smashed my head when finding the answer, you know how it goes... exploring something new is both challenging and fun.

Here you can download the nbm of my plug-in and it's sources. If anyone thinks this plug-in is a good idea I may also commit the sources to CVS. The project needs to be opened in a recent 4.2 daily build. Note that the project contains 2 external libraries to be able to send MIME attachments. Comments are welcome and appreciated.

Update: I've commited the project into CVS, the latest version of sources will always be in contrib/projectpackager.

Why Is NetBeans Full of Cookies?



If you've ever studied NetBeans sources or wrote anything above NetBeans, you must have crossed the omnipresent Cookies. There is a nice explanation of how the term was created in the NetBeans: the Definitive Guide (which is btw a great resource if you want to really understand NetBeans, most concepts are still valid although the book is about NetBeans 3.x):

----------------------%<----------------------
Why Cookies?

According to Yarda Tulach, “The name ‘cookie’ was invented by us, and was meant as something special and sweet that is provided by an object, which you can ask for.” NetBeans’ cookies bear a fleeting resemblance to the concept as defined by web browsers, but only inasmuch as they are objects held by a provider object which is not expected to know much about what kinds of cookies it possesses. Do not conflate NetBeans’ concept of cookies with browser cookies— this will only create confusion. The name probably contributes a little bit to Cookies being a stumbling block for people getting involved in coding for NetBeans.
----------------------%<----------------------

If it helps you can visualize a Node offering various Cookies like this:

Úterý VII 26, 2005

Hacking NetBeans #2 - Executing Ant Tasks from the IDE



In my new plug-in I need to call an an task from the IDE. Thanks to Zajo for telling me the secret how to do that via NetBeans API. It's quite simple, the hardest thing is (as usual) to find the right classes and methods. Here is the source code:

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import org.apache.tools.ant.module.api.support.ActionUtils;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
...

       String script = "<?xml version=\\"1.0\\" encoding=\\"UTF-8\\"?>" +
                "<project name=\\"Packager\\" default=\\"copy\\" basedir=\\".\\">" +
                "<target name=\\"copy\\">" +
                "<copy todir=\\"e:\\\\test2\\">" +
                "<fileset dir=\\"e:\\\\test\\" includes=\\"\*\*/\*.txt\\"/>" +
                "</copy>" +
                "</target>" +
                "</project>";
        try {
           File zf = File.createTempFile("ant-copy", "xml");
           BufferedWriter out = new BufferedWriter(new FileWriter(zf.getAbsoluteFile()));
           out.write(script);
           out.close();
           FileObject zfo = FileUtil.toFileObject(FileUtil.normalizeFile(zf));
           ActionUtils.runTarget(zfo, new String[] {"copy"}, null);
           zf.deleteOnExit();
        } catch (IOException e) {
           System.out.println("IO error: "+e);
        }

What do I do in the code? At first I create a xml file which will serve as the ant script. I create it as a temporary file in default temp dir and write the xml into it. To execute the ant task I call the ActionUtils.runTarget method with parameters - a fileobject (created from a normalized File), 1 target to run and no properties. At the end I mark the file to be deleted when VM exits. Easy, isn't it?

To be able to compile this code you need to add to your plug-in two dependencies - Ant and Execution API. When you run this code, the output window opens and shows the results:



Now I can do with my plug-in anything what ant is capable of. Time to generate some powerful ant scripts.

Pondělí VII 25, 2005

Hacking NetBeans #1 - First Steps



I'm starting hereby a new series of posts called Hacking NetBeans. Through this I want to share my experiences with using NetBeans APIs when writing NetBeans plug-ins. I know almost nothing about NetBeans APIs at the moment so I'll meet many obstacles. My advantage is that I can ask anyone in the Prague building for an answer and by writing these answers down I hope to provide simple answers for other developers (you'll probably have similar issues as I do). The disadvantage is that not every information has to be correct but hopefully I'll learn by making mistakes.

So you decided to write your NetBeans plug-in. Good. For 4.1 you have to prepare the backbone of your plug-in manually but development version of 4.2 comes with a nice GUI you can use. Geertjan wrote a good tutorial which can help you with the first steps or you can take a look at my little Googlefight plug-in.

Once you'll capture the basics (how to create a hello world plug-in, how to customize it, how to add dependencies, etc.) you're biggest friend will be this page which describes the NetBeans API (or it's 4.1 brother). Here you can find the APIs of both NetBeans platform and NetBeans IDE you'll need to use in your plug-in. Your second friend is the openide mailing list archive which can provide answers to many questions.

As described in the tutorials here and here, usually the plug-in becomes a part of NetBeans by defining an action in the layer.xml file. Here you define the class which will be called once the users click on the menu item. So your the method performAction() of your class which extends CallableSystemAction and is defined in layer.xml is called and your code starts to be executed.

Inside the peformAction() method you can do basicly whatever you want - run your code and call various NetBeans APIs to do someting useful with your plug-in. Seems easy, but you'll probably have many questions soon. Like:

Question #1: How do I place my menu item to a different position in the menu?

Answer: Like this:

<filesystem>
   <folder name="Menu">
   <attr name="Tools/Games" boolvalue="true" />
   <attr name="Games/Window" boolvalue="true" />
      <folder name="Games">
         <file name="com-toy-anagrams-OpenAnagramAction.instance"/>
      </folder>
   </folder>
</filesystem> 

By this you are saying that the Games menu containing your menu item will be after the Tools menu and before the Window menu. This works for the top menu, you can do it similarly inside a menu:

<filesystem>
    <folder name="Menu">
        <folder name="File">
            <attr name="org-netbeans-modules-project-ui-SetMainProject.shadow/org-netbeans-modules-projectpackager-ZipProject.instance" boolvalue="true" />
            <attr name="org-netbeans-modules-projectpackager-ZipProject.instance/org-netbeans-modules-project-ui-CustomizeProject.shadow" boolvalue="true" />
            <file name="org-netbeans-modules-projectpackager-ZipProject.instance"/> 
        </folder>    
    </folder>
</filesystem>


Question #2: How do I dock my component into the IDE?

Answer: Explained in the tutorial here in section Embedding and Displaying the Anagram Plug-in. You need to create a class which extends the TopComponent class and do described steps to dock it and activate it.

Question #3: I've sent the sources of my plug-in to my friend but he can't the project in the IDE. What's wrong?

Answer: When you distribute your project you need to make sure not to include the private subfolder of the nbproject folder. This subfolder contains information about paths on your computer. Once you try to open a project with this subfolder somewhere else, the dialog fails to open the project folder without giving an error, so this is quite tricky. Thus don't forget to remove the private folder if you send the project anywhere.

Question #4: I can't find a method on an object although my intuition tells me it should be there. Where is it?

Answer: Some of the methods are not connected with the object you're using. For example the object Project has several methods like getProjectDirectory but if you want to find more information about the project you need to use a static method ProjectUtils.getInformation(Project). Similarly to get a list of opened projects you won't find through classes around the Project class but you need to find the OpenProjects class. My advice is to look around in the javadoc or search for usage of such classes in NetBeans sources.

Enough about hacking of NetBeans for today. Good night.

Čtvrtek VI 30, 2005

I Wrote My First Plugin for NetBeans



When I saw the newest tutorial from Geertjan I decided it's time for me to write my first NetBeans plug-in. I am a software quality engineer (fancy title for a tester), so I do not know much about development of NetBeans plug-ins. Anyway, after several hours of experimenting my first plug-in is alive :-) This is how it looks like:



Not very beautiful, I know, but that was not the goal. I wanted to experiment with writing plug-ins to find out how easy or hard it is. The development is a lot simplified by a wizard which helps you create the backbone of the plug-in. The rest needs a bit of investigation on the platform website and NetBeans APIs.

If you are really interested, the sources are here. Open the project in latest development build of NetBeans, you also need to get Open APIs support from Update Center, otherwise you won't be able to open it. This plug-in is extremely simple so you can use study it to see how to write NetBeans plug-ins. I don't guarantee that this is the best way how to do it, but it works for me. A lot of things could be improved, but I wanted to keep it as simple as possible if others would like to take a look at it.

If you still wonder what is the purpose of the plug-in, see this.

Update: This e-mail is what you need if you want to add external libraries into your nbm. If you want to try my extremely useful module, download it here (works only in dev builds).
About

Roman Strobl

Search

Archives
« duben 2014
PoÚtStČtSoNe
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    
       
Today