Allowing users to extend your Java code – Option 1 Custom listeners

When you have an application there is a delicate balance between allowing users to tweak the application to match their needs while not making the code unmaintainable. I thought that a serious of articles on how we are trying to do this in our JPedal PDF library might be of general interest.

The balance we have tried to strike is to essentially keep the user at the top level API – so we can alter the internals at will as the code develops – but provide several ways to configure or alter this functionality. This article explains how we provide custom listeners as a means to to do this.

By specifying an Interface we can define a ‘possible’ object which the user has instance and pass into our application, It can have clear, documented behaviour and allow the user to obtain information or effect the way the code works. Let work it through with an example which involves 3 steps.

Let’s say that the developer using our application wants to know whether the page is being drawn or not.

1. We need an interface

public interface RenderChangeListener {

/**Fired when rendering starts.*/public void renderingStarted(int pageNumber);

/**Fired when rendering ends.*/public void renderingEnded(int pageNumber);
}

The user can now create their own version of this Interface.

2. We need to pass in the user instance

There are several ways to do this. We could add a pair of listeners along the lines of

public void addRenderChangeListener(RenderChangeListener l);

public void removeRenderChangeListener(RenderChangeListener l);

In our case, the main class (PdfDecoder) has a lot of method already and we are trying to avoid clutter. We have also added a route to pass in instances of agreed Interfaces

public void addExternalHandler(Object newHandler, int type);

where type is a static int defined in the class org.jpedal.external.Options

pdfDecoder.addExternalHandler(myRenderListener, Options.RenderChangeListener);

3. Add some calls into our code

The last step is to add some calls into our code to all this instance if present. So we need to plumb in the methods into our code. As it is tracking the paint events, we add it to the paintComponent method, which calls our painting code in threadSafePaint(g2);

public void paintComponent(Graphics g) {

if(customRenderChangeListener!=null) //call custom class if present
customRenderChangeListener.renderingStarted(this.pageNumber);

if (SwingUtilities.isEventDispatchThread()) {

threadSafePaint(g);

if(customRenderChangeListener!=null) //call custom class if present
customRenderChangeListener.renderingEnded(this.pageNumber);

} else {
final Graphics g2 = g;
final int page=pageNumber;
final Runnable doPaintComponent = new Runnable() {
public void run() {

threadSafePaint(g2);

if(customRenderChangeListener!=null) //call custom class if present
customRenderChangeListener.renderingEnded(page);
}
};
SwingUtilities.invokeLater(doPaintComponent);
}
}

Finale

So now we have a way that the developer can interact with our code in a controlled way. We can even add methods which return values allowing the user to replace or control functionality. And if you want to use the new Interface in our code, it will be in the JPedal February release…

Related Posts:

The following two tabs change content below.

Mark Stephens

System Architect and Lead Developer at IDRSolutions
Mark Stephens has been working with Java and PDF since 1999 and has diversified into HTML5, SVG and JavaFX. He also enjoys speaking at conferences and has been a Speaker at user groups, Business of Software, Seybold and JavaOne conferences. He has a very dry sense of humor and an MA in Medieval History for which he has not yet found a practical use.
Markee174

About Mark Stephens

Mark Stephens has been working with Java and PDF since 1999 and has diversified into HTML5, SVG and JavaFX.

He also enjoys speaking at conferences and has been a Speaker at user groups, Business of Software, Seybold and JavaOne conferences. He has a very dry sense of humor and an MA in Medieval History for which he has not yet found a practical use.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>