After some months of not having contributed substential stuff to e4 and in anticipation of the EclipseCon tutorial “e4 – Anatomy of an e4-Application”. I worked yesterday on some demo stuff which we’ll probably use show the audience:
- The new development process when writing plugins using Mock-Objects
- Extending an existing e4-Application
The idea of the extension is to add Flickr-Support (Searching and importing images and adding Images to flickr) to our e4-Photo-Demo using the Service-API provided by flickr.com.
One of the strengths of writing and extending e4-Applications is that you don’t need to bring up whole workbench but can develop all components standalone at first (you can even simply start with a none-OSGi-Application).
Here’s the process I followed
- Writing the UI as an ordinary SWT-Application (No OSGi, No e4). The initial code looked something like this:
public class Flickr { public Flickr(Composite comp) { // Create my UI } private void handleSearch(String apiKey, String tags) { // TODO implement search logic } public static void main(String[] args) { Display display = new Display(); Shell shell = new Shell(); Flickr flickr = new Flickr(shell); shell.open(); while( ! shell.isDisposed() ) { if( ! display.readAndDispatch() ) { display.sleep(); } } display.dispose(); } }
After some codeing (well I thought writing something like a PagedTableViewer might me a nice thing) the UI looked like something like this:
. The code is in a Bundle-Project “org.eclipse.e4.demo.e4photo.flickr” which depends on SWT-only for now (I let PDE manage even my standard Java-Project because this frees me from all the ClassPath-Configuration).
- The next thing was to look at the Flickr-API and what a JavaService to talk with it could look like. The Service-Interface I came up with looked like this:
public interface IFlickrService { public FlickrSearch createTagSearch(String apiKey, String tags) throws RemoteException; public List<FlickrPhoto> getPhotos(FlickrSearch search, int page) throws RemoteException; public InputStream getPhoto(FlickrPhoto photo) throws RemoteException; }
The service definition is also part of a separate bundle named ‘org.eclipse.e4.demo.e4photo.flickr.service’.
- Next step was to create a bundle with a concrete implementation named ‘org.eclipse.e4.demo.e4photo.flickr.service.rest’. Which uses the REST-API provided. The service is self is also a simply POJO and so I was able to test it quite easily by simply adding a main-method.
public class RestFlickrService implements IFlickrService { // Implementation of methods public static void main(String[] args) { try { RestFlickrService s = new RestFlickrService(); FlickrSearch search = s.createTagSearch("46d3d5269fe6513602b3f0f06d9e2b2e", "eclipsecon"); for( int page = 1; page <= search.getPages(); page++ ) { System.err.println("--------------------------------"); System.err.println("Page " + page); System.err.println("--------------------------------"); List<FlickrPhoto> photos = s.getPhotos(search, page); for( FlickrPhoto p : photos ) { System.err.println(" * " + p); } } } catch (Exception e) { e.printStackTrace(); } } }
Please don’t blame me that I haven’t written this by creating JUnit-Test but you should get the point that writeing application like this makes your whole application very easy to test in a real world application.
- Now we need to wire the stuff together so until now we didn’t even used OSGi but now we can’t get away without it. First step is to use DS to register our service in the OSGi-Registry:
<?xml version="1.0" encoding="UTF-8"?> <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="org.eclipse.e4.demo.e4photo.flickr.service.rest"> <implementation class="org.eclipse.e4.demo.e4photo.flickr.service.rest.RestFlickrService"/> <service> <provide interface="org.eclipse.e4.demo.e4photo.flickr.service.IFlickrService"/> </service> </scr:component>
- And we need to make our UI-Bundle use the IFlickrService. To prepare the stuff for DI we need to add the javax.inject-Bundle which holds the annotation of JSR-330.
public class Flickr { @Inject private IFlickrService flickrService; private void handleSearch(String apiKey, String tags) { try { FlickrSearch search = flickrService.createTagSearch(apiKey, tags); table.setInput(new SearchInput(search)); } catch (RemoteException e1) { Status s = new Status(IStatus.ERROR, "org.eclipse.e4.demo.e4photo.flickr", e1.getMessage(), e1); ErrorDialog.openError(table.getShell(), "Searchfailure", "Failure while executing search", s); } } }
- Now we make an mock-application bundle ‘org.eclipse.e4.demo.e4photo.flickr.mock’ which holds an Equinox-Application which bootstraps the Dependency Framework coming with e4 and creates an instance of our view.
public class Application implements IApplication { public Object start(IApplicationContext context) throws Exception { Display display = new Display(); Shell shell = new Shell(); Bundle bundle = FrameworkUtil.getBundle(Application.class); BundleContext bundleContext = bundle.getBundleContext(); IEclipseContext eclipseCtx = EclipseContextFactory.getServiceContext(bundleContext); eclipseCtx.set(Composite.class.getName(), shell); ContextInjectionFactory.make(Flickr.class, eclipseCtx); shell.open(); while( ! shell.isDisposed() ) { if( ! display.readAndDispatch() ) { display.sleep(); } } display.dispose(); return IApplication.EXIT_OK; } public void stop() { // nothing to do } }
- Last step is to integrate the “Flickr-Application” as a view in the existing Photo-Demo. This is done using an the ‘org.eclipse.e4.workbench.model’ extension point which allows us to contribute ModelElements to the e4 Workbench-Model.
Pointing to an XMI-File like this.
- Add the bundles beside the one of the mock-Application to the Launch-Config of the e4-Photo-Demo and you now have support for searching Fotos from Flickr like shown at the start of the post.
Running this Application with all the bundles we create above plus the core OSGi-Bundles needed like (equinox.ds) will bring up the same application we’ve had after the first step. With the difference that now executing the search will really do something. We now have a running OSGi-Application which allows us to query Flickr for Photos.
I think one of the strengths of e4 is that it makes it easy to develop code in a way that you can run it standalone while developing. You can do this also to some extend in 3.x but at the moment you reach out to workbench-services you’ll get into trouble.
Writing a general mock framework for 3.x is quite hard because you need to rebuild the complete Workbench-Structure because your component is reaching out to them. For e4 the only thing needed is to provide mock implementation of the 20 things (=core workbench-services) and you are ready to work without bringing up an eclipse-instance to test your contribution.
Thanks Tom, a very nice example. It would be great if you could post the full code of your examples.
The full code is available from e4-CVS
Cool.Thank you.