Electronic Design

Write Less Code And Deliver Java Apps Faster With Eclipse 3.0

Base your next application on the Eclipse RCP and you can start thinking about reusing code—and get better cross-platform user interfaces.

DESIGN VIEW is the summary of the complete DESIGN SOLUTION contributed article, which begins on Page 2.

Full article begins on Page 2

Writing desktop applications has always been about compromises. If you want your application to be truly native, you need to select the platform and use its frameworks, tool kits, and low-level application programming interfaces (APIs) to build your application, using whatever programming language(s) it supports. While you'll certainly give up cross-platform portability, you will be able to provide users with the exact look and feel they expect.

But what if you want your application to run on multiple platforms? The most common solution has been to write Java applications that use a Swing user interface. With this approach, you'll gain support for multiple platforms. But you'll lose the use of native user-interface (UI) widgets and dialogs, which will jeopardize the native look and feel that's critical for end-user acceptance. If Java is to be a viable language for desktop applications, we need to be able to write cross-platform Java applications that use a native UI.

This is where Eclipse steps in. While most Java developers are familiar with Eclipse, they tend to think of it simply as a Java integrated development environment (IDE). However, when viewed at that level, Eclipse is unique because it looks like a native application upon whatever platform it is run. If you dig a little deeper, you'll see that the IDE is simply a specific type of application built on top of the Eclipse Rich Client Platform (RCP), which is part of Eclipse 3.0

The RCP is a platform for building applications. And because the IDE is built on top of the RCP, we have a rather compelling body of evidence that it's both extensible and flexible enough to support the construction of world-class applications with a native UI.

The article presents a step-by-step procedure for building an image viewer on top of an Eclipse RCP framework, including the appropriate code.

HIGHLIGHTS:
Building An Image-Viewer Application To solve the problem of having scattered photo directories, this article explores building a cross-platform image viewer using Eclipse 3.0.
Getting Started When building the application, you first must install the appropriate software development kit (SDK), which in this case is one of the Eclipse distributions. Because it will be a Rich Client Platform (RCP) application, the full Eclipse SDK is required (from the Eclipse Web site).
Key Steps The fundamental steps involved in creating the image viewer are: building the image viewer's core functionality; converting the image viewer to an RCP application; and packaging the image viewer for distribution.
Sidebar: The Case For Eclipse This sidebar runs down a host of reasons why Eclipse may be the right solution for your application.
Completed Projects View Figure 7 offers a view of completed development projects, showing all plug-in and feature projects.
Directory Structure Figure 8 depicts the directory structure, which shows the completed standalone application ready for distribution.

Full article begins on Page 2

Writing desktop applications has always been about compromises. If you want your application to be truly native, you need to select the platform and use its frameworks, toolkits, and low-level APIs to build your application, using whatever programming language(s) it supports. While you'll certainly give up cross-platform portability, you will be able to provide users with the exact look and feel they expect.

But what if you want your application to run on multiple platforms? Well, that's one of the principle selling points of Java. However, if you really want to be cross-platform, you won't be able to use any platform-specific frameworks, even if they have Java APIs. That's because the frameworks themselves aren't portable.

The most common solution to this problem has been to write Java applications that use a Swing user interface. With this approach, you'll gain support for multiple platforms. But you'll lose the use of native user-interface (UI) widgets and dialogs, which will jeopardize the native look and feel that's critical for end-user acceptance. If Java is to be a viable language for desktop applications, we really can't afford to compromise any longer. We need to be able to write cross-platform Java applications that use a native UI.

Enter Eclipse
While most Java developers are familiar with Eclipse, they tend to think of it simply as a Java IDE. However, even when viewed at that level, Eclipse is unique because it looks like a native application upon whatever platform it is run. That's due to its use of native widgets and dialogs wherever possible. If you dig a little deeper and look at how Eclipse is built, you'll see something unexpected: the IDE is simply a specific type of application built on top of the Eclipse Rich Client Platform (RCP), which is part of Eclipse 3.0.

The RCP isn't a platform for building IDEs. Instead, it's a platform for building applications. And because the IDE is built on top of the RCP, we have a rather compelling body of evidence that it's both extensible and flexible enough to support the construction of world-class applications with a native UI. Besides addressing our main concerns of portability and look and feel, Eclipse offers a number of other advantages (see DRILL DEEPER 8777, "The Case For Eclipse"). Now that we realize the Eclipse RCP is the framework we've been looking for, let's go in depth and actually build something on top of it.

Cross-Platform Image-Viewer Requirements
Many people have an ever-growing collection of digital photographs, which is often poorly organized. Maybe some are categorized by person, and some by occasion. Some are even saved with their default names that tell you nothing of the content. In any event, the result is many directories full of photos in locations that made sense at one time, but now just seem scattered.

To solve these problems, we would normally just search for the software we need and buy it. But for the purposes of providing an example Eclipse application for this article, we'll build an image viewer with the following core set of abilities:

  1. Remember the location of multiple photograph directories.
  2. Display the hierarchical layout of the directories.
  3. Select an image and display it.
  4. Drag and drop images to/from the file system or e-mail.

Getting Started
The first step in building our application is to install the appropriate software-development kit (SDK), which in this case is one of the Eclipse distributions. Unfortunately, this can be a bit confusing. On the Eclipse Web site are several distributions of Eclipse. One of them is clearly labeled as the RCP SDK, and another simply as the Eclipse SDK.

Because we're building an RCP application, you'd imagine we'd need the RCP SDK. However, this distribution contains only the binary RCP runtime plus its source. As a result, it's really more of a "runtime kit" than a "development kit," because it's impossible to actually develop anything with it.

The full Eclipse SDK is really the SDK required to build an RCP application. While this might seem strange at first, it makes sense once you realize that you need the same plug-in construction capabilities, whether your final target is the Eclipse platform, the IDE, or the RCP. Consequently, building an RCP-based product really consists of three disjoint pieces.

  1. Building your product's functionality as a set of Eclipse plug-ins.
  2. Building the additional infrastructure necessary to run as an application on the RCP.
  3. Packaging your product for distribution.

Several sources are available for general Eclipse plug-in development information, so we'll summarize the initial plug-in creation in step one. This will let us spend more time on the RCP-specific operations.

Step 1. Building the Image Viewer's Core Functionality:
Remember, one advantage of using Eclipse is that any part of it can be modified and redistributed with your application. In addition, there's a thriving community of plug-in authors that release their code under the CPL or compatible licenses. So before beginning any Eclipse development, smart developers always check to see what open-source components they can use to accelerate their delivery, which is precisely where we'll begin.

In Eclipse, a built-in view called the navigator enables the creation and viewing of hierarchical file directory structures to be created and viewed. In addition, the navigator can create a "linked" location that simply points to an existing directory, rather than creating a new one. So, simply deciding to reuse the navigator view in our application satisfies requirements 1, 2, and half of 3, which moves us well toward completion simply through reusing Eclipse code. Even better, we get the drag and drop functionality (requirement 4) for free because it's automatically supplied by the Eclipse framework.

All we lack now is the ability to actually display the image. As a starting point, Eclipse's standard widget toolkit (SWT) includes support for several image formats, including JPEG, PNG, GIF, and bitmap. So at least we won't have to handle the low-level file formats directly. However, for the application to be useful, we still need to build a view to contain the image and handle scaling and scrolling. Because the Eclipse UI doesn't require image manipulation, we had to look elsewhere for code to reuse.

With a bit more searching, we discovered a detailed article written recently by Chengdong Li* . It illustrates various image manipulation techniques in Eclipse—including the required scaling and scrolling functionality. Plus, because the source code for the plug-in described in the article was released under the CPL, we're free to reuse any portion of it for our application.

Now that we've found all the necessary basic functionality and installed the Eclipse SDK, it's time to start building our application. Here's a quick summary of how the initial application functionality was formed. After launching Eclipse, we created a development project for the image viewer plug-in by using the wizard on the File menu at File > New... > Project... > Other > Plug-in Project. In the wizard panels, we specified that the project name was com.genuitec.eclipse.imageviewer, that it was a Java Project, and that it didn't need a plug-in class created. Next, we simply imported Chendong's example code and merged his plugin.xml file with the one generated automatically.

Now we just needed to write some nice "glue" code to tie everything together. We created a custom perspective—to specify how we wanted our views to be sized and oriented—by implementing

:
org.eclipse.ui.IPerspectiveFactory.

And as selection changes in the navigator would update the image view, we implemented:

org.eclipse.ui.ISelectionListener.

At this point, our plug-in's Eclipse project looked like Figure 1.

Perhaps surprisingly, we're already feature-complete with respect to our image-viewer application requirements. All of our application functionality has been implemented through effective reuse of existing plug-ins and the creation of approximately one hundred lines of new code to tie everything together. To see what our application looked like thus far, we created and then ran a launch configuration that specified all of the plug-ins required by our plug-in, and by the main Eclipse workbench plug in, org.eclipse.ui.ide. Figure 2 shows the application capabilities in action. You should also notice that the application's look and feel automatically adapted itself to the current UI theme in use by the operating system, which in this case is XP Chrome, through Eclipse's use of native widgets.

Even though we can launch our application, it still contains lots of "IDE-isms" in the menu bars, tool bars, and preferences that need to be removed. Toward that end, we begin the next development phase, which is augmenting our application so that it can be run as an application on the RCP.

Step 2. Converting the Image Viewer to an RCP Application:
All RCP applications need to provide an implementation of the plug-in extension point named org.eclipse.core.runtime.applications. We could have added the implementation of the application extension point to our existing plug-in. However, we chose to separate all of our packaging code from the core application functionality by creating a new plug-in named com.genuitec.eclipse.imageviewer.rcp for this purpose.

As before, we used the new plug-in wizard and accepted all of the defaults, but declined the offer to create the plug-in class because it's not needed here. Once the plug-in was created, the plug-in development environment (PDE) brought up the plug-in manifest editor and we added our known dependencies, which are: org.eclipse.core.runtime, org.eclipse.core.resources, org.eclipse.ui, and org.eclipse.ui.ide.

Implementing the org.eclipse.core.runtime.applications extension point is quite simple when compared with most others. The only requirement is that we specify the name of the class that implements

org.eclipse.core.runtime.IplatformRunnable

which should be instantiated and called to start the application. The full specification is shown in the plugin.xml fragment below:

<extension
id="application"
point="org.eclipse.core.runtime.applications">
<application>
<run class="com.genuitec.eclipse.imageviewer.rcp.Application"/>
</application>
</extension>

Now that we'd told Eclipse what our application class is called, we needed to create it. We used the class wizard to generate the shell of our application class and the only method we needed to implement is run. Fortunately, the Eclipse javadocs provide an example implementation of the run, method that we modified slightly for our use, as shown below.

public Object run(Object args) throws Exception \{
Display display = PlatformUI.createDisplay();
try \{
WorkbenchAdvisor workbenchAdvisor =
new ImageViewerWorkbenchAdvisor();

return PlatformUI.createAndRunWorkbench(
display, workbenchAdvisor) == PlatformUI.RETURN_RESTART
? IPlatformRunnable.EXIT_RESTART
: IPlatformRunnable.EXIT_OK;
\} finally \{
display.dispose();
\}
\}

The major modification was adding our own implementation of:

org.eclipse.ui.application.WorkbenchAdvisor.

which is used to configure the presentation of the Eclipse workbench. Utilizing our advisor, we were able to specify the exact layout of the menu bars, tool bars, and other UI elements for our application. The WorkbenchAdvisor class specifies one abstract method, getInitialWindowPerspectiveId, that our ImageViewerWorkbenchAdvisor class must implement. After generating our advisor with the class wizard and implementing getInitialWindowPerspectiveId to return the ID of the image viewing perspective we created previously, we were ready for another test run.

To launch our plug-ins as an application, rather than as part of the workbench, we needed to first create a new launch configuration. To do this, we selected the com.genuitec.eclipse.imageviewer.rcp project, right clicked, and selected Run.... from the context menu. From the configurations list, we selected Runtime Workbench and clicked the New button. In the Program to Run section of the Arguments tab, we selected com.genuitec.eclipse.imageviewer.rcp.application. Next, we switched to the Plug-ins tab and selected "Choose plug-ins and fragments to launch from the list." We deselected the root of the External Plug-ins tree and clicked on the "Add Required Plug-ins" button to add back only the plug-ins that are required by our two plug-ins. After saving the configuration, we launched it to see what our application looks like at this point (Fig. 3).

As you can see from comparing Figure 3 with Figure 2—now that we've implemented our own application rather than using

org.eclipse.ui.ide.Workbench
—we've lost all of our menu bars and tool bars. However, our perspective was opened as the default, as we expected. But obviously, we've removed a bit too much at this point, so we'll augment our
ImageViewerWorkbenchAdvisor
class to address that next.

Because the view tool bars already contain all of the actions we need, we overrode the

preWindowOpen
method in our
ImageViewerWorkbenchAdvisor

class to remove the application tool bar. Furthermore, we set the initial window size and the application name that will be displayed in the title bar.

public void preWindowOpen(IWorkbenchWindowConfigurer configurer) \{
super.preWindowOpen(configurer);
configurer.setInitialSize(new Point(640, 480));
configurer.setShowCoolBar(false);
configurer.setShowStatusLine(true);
configurer.setTitle("Image Viewer");
\}

Just these small changes made a big difference in the presentation (Fig. 4). But the navigator view no longer worked, so things were not quite as good as they might have seemed.

Next, we needed to work on making sure the navigator view is initialized properly. Because this was previously done by the workbench's application advisor (which we replaced), we needed to make sure that our advisor does the same initialization. We located the code in:

com.eclipse.ui.internal.ide.IDEWorkbenchAdvisor

and copied the methods,

declareWorkbenchImages(), declareWorkbenchImage(Bundle, String, String, boolean), and initialize(IWorkbenchConfigurer),

into our workbench advisor class. With another quick launch to test our changes, we saw that the navigator had been repaired (Fig. 5).

Our final task was to create menu bars containing only the actions necessary for our application. Again we turned to the

IDEWorkbenchAdvisor
class, because we knew that it sets up the menu bar for the workbench application. We copied the
fillActionBars(IWorkbenchWindow, IActionBarConfigurer, int)

method into our

ImageViewerWorkbenchAdvisor

class and declared the same ACTION_BUILDER constant. At this point, we had exactly the same menu configuration as the workbench. But, because we need to customize both the menu bar and the actions, we simply copied

org.eclipse.ui.internal.ide.WorkbenchActionBuilder

to

com.genuitec.eclipse.imageviewer.rcp.ImageViewerActionBuilder.

Now we can simply remove the code that builds the menus or actions that we don't want in our final application. By comparing the final menu layout in Figure 6 to Figure 1, you can easily see that we removed everything that wasn't germane to viewing images.

Step 3. Packaging the Image Viewer for Distribution:
The Eclipse distribution and update model is based on the concept of a feature, which is a grouping of plug-ins that provides some function. So next, we quickly created a feature for the image-viewer application.

We used the feature project wizard and specified that our project would be called com.genuitec.eclipse.imageviewer-feature. Once the project creation was completed, we navigated to the Content tab of the feature manifest editor. Then we added our two plug-ins, and selected the Compute button in the Required Features/Plug-ins section so that Eclipse would determine the top-level plug-ins necessary for our feature to execute properly. This prevented our feature from installing any distribution without all of the prerequisites. At this point, our completed development projects looked like Figure 7.

Next, we had to determine the minimal set of plug-ins necessary to ship as part of the product distribution. So it was time to download the RCP binary distribution to use as our application's distribution foundation. The plug-ins within the RCP distribution form the minimum platform for any RCP-based application and include:

  • org.eclipse.core.expressions_3.0.0
  • org.eclipse.core.runtime_3.0.0
  • org.eclipse.help_3.0.0
  • org.eclipse.jface_3.0.0
  • org.eclipse.osgi.services_3.0.0
  • org.eclipse.osgi_3.0.0
  • org.eclipse.swt.win32_3.0.0
  • org.eclipse.swt_3.0.0
  • org.eclipse.ui.workbench_3.0.0
  • org.eclipse.ui_3.0.0

After unzipping the RCP, we renamed the top-level directory to ImageViewer so that we could begin adding our unique functionality into it.

Because we reused some functionality from the SDK, most notably the navigator view, we needed to do a bit of research to determine the full closure of the set of plug-ins required by the image viewer plug-ins—until we reach the RCP layer. Fortunately, we could use standard plug-in development techniques to find that list by focusing the Eclipse plug-in dependencies view on each of our plug-ins. Upon inspection, our plug-in dependency trees illustrated that the following plug-ins from the Eclipse SDK needed to be redistributed as part of our application:

  • org.apache.lucene_1.3.0
  • org.eclipse.core.resources_3.0.0
  • org.eclipse.core.resources.win32_3.0.0
  • org.eclipse.core.runtime.compatibility_3.0.0
  • org.eclipse.help.appserver_3.0.0
  • org.eclipse.ui.forms_3.0.0
  • org.eclipse.help.base_3.0.0
  • org.eclipse.ui.ide_3.0.0
  • org.eclipse.ui.win32_3.0.0
  • org.eclipse.ui.views_3.0.0
  • org.eclipse.update.configurator_3.0.0
  • org.eclipse.update.core_3.0.0
  • org.eclipse.update.core.win32_3.0.0
  • org.eclipse.update.ui_3.0.0

To complete the packaging of our application, we copied these additional plug-ins from our Eclipse SDK installation into our unzipped RCP binary distribution's plug-ins directory. We then exported our image-viewer feature from the workbench as a directory structure, which we pointed to the top-level ImageViewer directory. Finally, we created a new directory, called bin, and moved eclipse.exe into it. At the top level, we created a new shortcut named ImageViewer.exe and added the command-line argument

-application=com.genuitec.eclipse.imageviewer.rcp.application

to start our application. The completed application directory structure is shown in Figure 8. An application distribution could then be built by creating a zip file of the ImageViewer directory. As long as a Java VM of version 1.4.1 or later is installed, any recipient of the distribution can simply unzip it and double-click the ImageViewer.exe executable to launch the application on Windows.

For non-Windows platforms, a compatible distribution can be created by removing the four Windows-specific plug-ins (org.eclipse.core.resources.win32_3.0.0, org.eclipse.swt.win32_3.0.0, org.eclipse.ui.win32_3.0.0, org.eclipse.update.core.win32_3.0.0) and replacing them with the appropriate platform-specific version that's available from Eclipse.

How effective was this application development on the Eclipse RCP? The Image Viewer was completely built and ready for distribution in only a few hours. In our opinion, rapid delivery of end-user functionality is the definition of effective development. Of course, many will note that the primary reason for this rapid implementation was that we actually wrote very little new code. And, they'd be correct.

But the bigger question is "How did we manage to get away with writing so little code?" It's because we based our application on Eclipse technology. Between the Eclipse Foundation's projects and the hundreds of other open source Eclipse plug-in projects scattered throughout the Internet, there are literally millions of lines of code that are ready for use, reuse, integration, or modification.

Now you can base your next application on the Eclipse RCP and start thinking about reusing code rather than writing it. You'll likely deliver your next application much more effectively, just like we did.

* Li, Chendong, "A Basic Image Viewer," University of Kentucky, March, 15, 2004, http://www.eclipse.org/articles/Article-Image-Viewer/Image_viewer.html

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish