 | Level: Intermediate Benoit Marchal (bmarchal@pineapplesoft.com), Consultant, Pineapplesoft
01 Nov 2002 The more author and columnist Benoît Marchal learns about Eclipse and the plug-in API, the more he likes what he sees. In this column, Benoît continues his ongoing project to integrate XM (a simple content management and publishing solution based on XML and XSLT) and Eclipse (an open-source project to define a next-generation Integrated Development Environment for Java developers). His effort pays off when XM launches from the IDE. As a bonus, Benoît finds a basic XML editor already hidden in the toolkit!
Eclipse is a remarkable Integrated Development Environment (IDE) because it promotes an extensible architecture. Eclipse recognizes that modern development requires many skills. It is no longer satisfactory to use only Java or HTML or C++. A modern IDE needs to support all these languages and then some. To address this need, Eclipse uses plug-ins. Plug-ins extend the IDE with support for new languages, compilers, and other development tools. There is no default language in Eclipse. Every language -- even the Java language -- is supported through plug-ins. Therefore, the number of languages that Eclipse can support has no limits -- provided you write the appropriate plug-in. As I'm discovering through this project, writing an Eclipse plug-in is not that difficult. I only had to write two classes to launch XM from Eclipse. That's great news for developers. It has been my experience that no matter what the vendor has included in the box -- whether it's home-grown utilities, a third-party tool, or software management -- I always need other tools. Any IDE can launch external tools, but this usually means I have to switch back and forth between the IDE and the external tool. With Eclipse I am confident that I can integrate the tool into the IDE and keep everything neatly organized. To learn how to write such a plug-in, read on. And hats off the Eclipse development team -- I'm starting to like your work very much. XM background
It's been a long time since I last discussed XM in this column. From the mail I have received, it appears that some readers have misunderstood what it is and what it is not. XM is a low-cost solution for publishing static Web sites with XML and XSLT. It does not compete with WebSphere Portal or Cocoon. I initiated XM because I was frustrated by the lack of simple solutions for XML publishing. Most publishing projects eventually grow to the point where they need an application server, but not every project starts with zillions of documents. XM is intended to help you in the early stages. For example, as I'm writing this column, I have just returned from a visit to a new XM user. That organization has about 400 simple XML documents that are updated infrequently. Their style sheet is less than 100 lines long. Does it make sense to acquire and install a full-blown application server just to publish these 400 documents? I don't think so, and neither do they. For the time being, it makes more sense to run XM in batch every night. Obviously, as they grow more familiar with XML, they will want to turn to more powerful solutions, but right now XM serves their needs, as it serves the needs of many small- to medium-sized Web sites.
Writing a plug-in for XM
When writing an Eclipse plug-in, the first step is to decide which element to extend. Plug-ins can extend the navigator or the outline, add menu entries, replace the editor, or draw new console and status windows. As Figure 1 illustrates, I decided to start by adding a new Run XM entry to the navigator's pop-up menu. I also added a new XM Console window to display status messages. With these two additions, it's possible to use XM without ever leaving the Eclipse window. Figure 1. Launching XM from Eclipse

One challenge here was determining when to display the Run XM menu. Very early in the development of XM, I chose to banish configuration files. Unfortunately, it left nothing to right-click on. Therefore, the first step was to define an .xmp (XM Project) file. The plug-in is configured to associate Run XM with .xmp files. For simplicity, an .xmp file is a Java property file with the following four properties corresponding to the command line parameters (see Listing 1 for an example):
source: Points to the source directory (default: src)
publish: Points to the publishing or target directory (default: publish)
rules: Points to the style sheet directory (default: rules)
build: Has a value of either true or false -- true forces a rebuild where XM processes every file (default: false)
Listing 1. Sample .xmp file
# this publishes the ananas.org site
source=src
publish=ananas-html
rules=style-sheet
build=false
|
XM console
The XM console is implemented in MessengerView. It is a part -- Eclipse jargon for a window in the workbench. Since last month's column included a plug-in as a part, the code should be familiar to you, so I will only reproduce excerpts (see Resources to download all the code). If you missed the previous column, which introduced the Eclipse plug-in architecture, I recommend you go back and read it now. The most striking difference with last month
is the use of an SWT table instead of a label. The table has two columns, the first of which contains the status of the message (error, warning, and so on), the second contains the message itself. A table is handy for a list of messages, since it allows the user to scroll up and down. In SWT, a table is an instance of org.eclipse.swt.widgets.Table. Use the TableColumn class to define each column width and its title, as shown in Listing 2. Listing 2. Creating an SWT table
table = new Table(parent,SWT.SINGLE);
TableColumn column = new TableColumn(table,SWT.NONE);
column.setText("Status");
column.setWidth(50);
column = new TableColumn(table,SWT.NONE);
column.setText("Message");
column.setWidth(500);
table.setHeaderVisible(true); |
Note that there are no addColumn() methods. Instead, you pass the table instance in the TableColumn constructor. TableItem represents a table line. To initialize it, pass an array of strings (one string per column) to its setText() method, as shown in Listing 3. Listing 3. Adding a new line
TableItem item = new TableItem(table,SWT.NONE);
String[] text = new String[2];
text[0] = type;
text[1] = t.getLocalizedMessage();
item.setText(text);
|
MessengerView also implements the Messenger interface. Long-time readers will remember that Messenger is the interface XM uses to print messages. MessengerView simply redirects the messages to the window.
Run XM menu
The pop-up menu is implemented in the XMRunner class. Eclipse uses the IObjectActionDelegate interface to notify the plug-in when the user selects the menu. XMRunner is available in Listing 4. Listing 4. Answering user clicks
package org.ananas.xm.eclipse.runner;
import java.io.*;
import java.util.*;
import org.ananas.xm.*;
import org.eclipse.ui.*;
import org.eclipse.ui.actions.*;
import org.eclipse.jface.action.*;
import org.eclipse.core.runtime.*;
import org.eclipse.jface.dialogs.*;
import org.eclipse.jface.viewers.*;
import org.eclipse.core.resources.*;
public class XMRunner
extends ActionDelegate
implements IObjectActionDelegate
{
public static final String PLUGIN_ID =
"org.ananas.xm.eclipse.runner";
public static final String CONSOLE_ID =
"org.ananas.xm.eclipse.runner.view.Console";
private IFile selectedFile;
private IWorkbenchPart part;
public void run(IAction action)
{
try
{
if(part != null)
{
saveDirtyEditors();
MessengerView messenger = showConsole();
if(messenger != null)
{
messenger.clean();
runXM(messenger);
}
else
throw new NullPointerException("Failed.");
}
}
catch(Exception x)
{
ErrorDialog.openError(part.getSite().getShell(),
"XM","Exception.",makeStatus(x));
}
}
public void selectionChanged(IAction action,
ISelection selection)
{
selectedFile = null;
if(selection instanceof IStructuredSelection)
{
IStructuredSelection structuredSelection =
(IStructuredSelection)selection;
if(structuredSelection.size() == 1)
{
Object selectedResource =
structuredSelection.getFirstElement();
if(selectedResource instanceof IFile)
selectedFile = (IFile)selectedResource;
}
}
}
public void setActivePart(IAction action,
IWorkbenchPart targetPart)
{
part = targetPart;
}
private MessengerView showConsole()
throws PartInitException
{
IWorkbenchPage page =
part.getSite().getWorkbenchWindow().getActivePage();
MessengerView messenger = null;
if(page != null)
messenger = (MessengerView)page.showView(CONSOLE_ID);
return messenger;
}
private void runXM(Messenger messenger)
throws CoreException, IOException, XMException
{
InputStream is = selectedFile.getContents();
Properties properties = new Properties();
properties.load(is);
String rulesPath =
properties.getProperty("rules","rules"),
sourcePath =
properties.getProperty("source","src"),
publishPath =
properties.getProperty("publish","publish"),
buildString =
properties.getProperty("build","false");
boolean build =
Boolean.valueOf(buildString).booleanValue();
IResource parent = selectedFile.getParent();
if(parent != null)
{
IPath parentPath = parent.getLocation();
rulesPath =
parentPath.append(rulesPath).toOSString();
sourcePath =
parentPath.append(sourcePath).toOSString();
publishPath =
parentPath.append(publishPath).toOSString();
}
DirectoryWalker walker =
new DirectoryWalker(messenger,rulesPath,build);
walker.walk(sourcePath,publishPath);
}
private IStatus makeStatus(Exception x)
{
Throwable t = MessengerView.popThrowables(x);
if(t instanceof CoreException)
return ((CoreException)t).getStatus();
else
return new Status(IStatus.ERROR,
PLUGIN_ID,
IStatus.ERROR,
x.getMessage(),
t);
}
private void saveDirtyEditors()
{
IWorkbenchWindow window =
part.getSite().getWorkbenchWindow();
IWorkbenchWindow[] windows =
window.getWorkbench().getWorkbenchWindows();
for(int i = 0;i < windows.length;i++)
{
IWorkbenchPage[] pages = windows[i].getPages();
for(int j = 0;j < pages.length;j++)
pages[j].saveAllEditors(false);
}
}
}
|
You should think of this class as an event listener. The Eclipse workbench uses it to forward the user's selection to the plug-in. The class inherits from ActionDelegate, which provides a default implement for most of IObjectActionDelegate. The Eclipse workbench calls selectionChanged() when the user selects a new file in the navigator. Most importantly, it calls the run() method when the user selects the menu. In run(), the plug-in saves the editor's content (if the user was editing a file, it is saved), brings the XM Console to the front, and launches XM. The major difference between runXM() and XM's own main() method is that main() takes its arguments from the command line, whereas runXM() reads them from a properties file.
Bonus time
That completes the integration work for this month. But wait, there's more! It turns out that Eclipse ships with a basic XML editor. You only have to compile it. Now you can edit XML documents with syntax highlighting, and publish them through XM. XML editor
Eclipse's project wizard can generate a basic XML editor. Here's the procedure:
- From the File menu, select New and then select Project.
- In the project wizard, select Plug-in Development and Plug-in Project. If you don't see the Plug-in Development option, download the Plug-in SDK from the Eclipse Web site.
- Click Next.
- Give your project a name, such as
org.ananas.eclipse.xml.editor and click Next.
- Click Next to accept the default values on the next screen, Plug-in Project Structure.
- Make sure Create a plug-in project using a code generation wizard is selected, and point to Plug-in with an editor (see Figure 2). The wizard automatically generates a basic XML editor with syntax highlighting.
- Click Next.
- Click Finish to accept the option on the next screen, Plug-in Content. Eclipse creates a new project and writes an XML editor.
- From the Project menu, select Rebuild All to build the project.
- Make a JAR archive called
editor.jar with the class files. To create a JAR from Eclipse, use the Export option in the File menu. This is important -- if you don't make the JAR file, Eclipse won't load your plug-in.
- Exit Eclipse and look for your new project under the
workspace directory.
- Copy the project directory from the
workspace to the plug-in directory, then restart Eclipse.
The XML editor automatically launches when you double-click an XML file. To associate more files (such as .xsl), select Window > Preferences > Workbench and then File Associations. Figure 2. Compiling the hidden XML editor

XM update
While I was working on XM, I fixed a few bugs and added an option to change file extensions. By default, XM gives HTML files the .html extension. XML files get the .xml extension. Some users may need to change the default to .htm, .shtml, .rss, .wml, or something else. If you need this capability, copy the code from Listing 5. Notice the new rules:extension attribute. Listing 5. Changing the file extension
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:rules="http://ananas.org/2001/XM/XSLT/Rules">
<xsl:output method="html"
rules:extension="htm"/>
<!-- style sheet comes here -->
</xsl:stylesheet> |
This feature is completely integrated with XM link management so all your links remain valid.
The work continues
It only takes two classes to launch XM from Eclipse, which is a testimony to the power of the plug-ins. In my next column, I plan to write a wizard plug-in to initialize a new XM project. In the meantime, you can download and test the XM runner plug-in. You'll see that it's a joy to edit and publish a Web site through Eclipse. A word of warning, however: The plug-in is currently available for JDK 1.4 only. If you need to run Eclipse under JDK 1.3.x, you have to install Xalan and adapt the plug-in accordingly.
Resources - Download the plug-in from the online repository.
- Explore Eclipse, an open-source effort to develop an IDE framework. It was initiated by IBM.
- Try WebSphere Portal, a great solution when publishing more sophisticated portals.
- Check out WebSphere Studio Site Developer, a commercial offering built on the open Eclipse framework. (WebSphere Studio Site Developer replaces WebSphere Studio.)
- Read the first "Working XML" column, "Using XSLT for content management," which also introduces the XM project (developerWorks, July 2001).
- Learn about "Working the Eclipse Platform" in another introduction to Eclipse (developerWorks, November 2001).
- Although I don't like using the Swing editor, you may have a different opinion... or you may want to wrap a Swing-based editor in an Eclipse plug-in. Turn to "Plug A Swing-based development tool into Eclipse" (developerWorks, October 2002).
- The Internet is global, so prepare your plug-in for international markets with Internationalizing your Eclipse plug-in (developerWorks, June 2002).
- Find more XML resources on the developerWorks XML technology zone.
- Get IBM WebSphere Studio Application Developer, an easy-to-use, integrated development environment for building, testing, and deploying J2EE applications, including generating XML documents from DTDs and schemas.
- Find out how you can become an IBM Certified Developer in XML and related technologies.
About the author  | 
|  | Benoît Marchal is a consultant and writer based in Namur, Belgium. He has just released the second edition of XML by Example, which covers the latest features of the ever-evolving XML standard, including coverage of the final XML Schemas recommendation and the latest developments of XSL. More details are available at marchal.com. You can contact Benoît at bmarchal@pineapplesoft.com. |
Rate this page
|  |