under construction
Table of Contents |
---|
Introduction
...
Plugins are software programs that can be dynamically added to existing programs to add / enhance functionality.
...
They are also called Add-ons or Bundles (the OSGi term).
...
In IGB, plug-ins are also called "Apps." You may have seen plugins in the software that you use,
...
such as internet browsers, text/graphics editors, games, etc.
...
Examples include
...
Chrome extensions and countless Photoshop filters.
...
Users can search for plugins and choose ones that address their needs.
...
Users can add
...
or remove plugins at will without having to re-install the base application. Plugins can also be updated dynamically when the author(s) make changes.
IGB uses OSGi
...
to implement plug-able Apps
OSGi is specification and framework for building modular applications in java. In addition to the framework specification, it also contains a specification for dynamically installing and running plug-ins. The OSGi term for a Plugin is Bundle
...
. They mean the same thing.
An OSGi bundle is a nothing but a jar file
...
with some OSGi-specific headers included in the its MANIFEST.MF file. The OSGi framework uses these headers to manage how the bundle interacts with other bundles within the same framework. The OSGi
...
framework also use the headers to manage transitions between lifecycle states for a bundle - installed, resolved, starting, active, stopping, and uninstalled.
An OSGi bundle can (usually will) have a special java class called an Activator. This class must extend org.osgi.framework.BundleActivator
, and implement the start()
and stop()
methods. The activator class is specified in the Bundle-Activator MANIFEST.MF header (see below). These methods are called when the bundle is started and stopped.
When a program is running with OSGi, the OSGi framework dynamically handles installing / uninstalling,
...
activating (starting), deactivating (stopping), and upgrading bundles.
OSGi caches bundles, saving them to a directory called the "bundle cache" on the local hard disk, so that they don't need to be downloaded every time. So you can work offline after you have loaded IGB. IGB stores the App bundles alongside core IGB platform bundles in a folder called "bundles" in an IGB
...
-specific directory called ".igb." The bundles directory contains subdirectories for specific IGB versions.
There are several implementations of OSGi, including:
- Apache Felix (http://felix.apache.org/)
- Eclipse Equinox (http://www.eclipse.org/equinox/)
- Knoplerfish (http://www.knopflerfish.org/)the
The implementations are (for the most part) interchangeable and are free
...
. The implementations allow the user to run OSGi from a command line tool or to start it within a program.
IGB
...
uses the
...
Apache Felix implementation from within the com.affymetrix.igb.main.OSGiHandler class
...
.
There are several tutorials for OSGi, including:
- How to get Started with OSGi (http://www.osgi.org/About/HowOSGi)
- Apache Felix OSGi Tutorial (httphttps://felix.apache.org/site/documentation/tutorials-examples-and-presentations/apache-felix-osgi-tutorial.html)
- OSGi for Beginners (http://www.theserverside.com/news/1363825/OSGi-for-Beginners)
- OSGi with Eclipse Equinox - Tutorial (http://www.vogella.de/articles/OSGi/article.html)
IGB uses OBR (http://felix.apache.org/site/apache-felix-osgi-bundle-repository.html) to find and load bundles from web servers (repositories). The web servers need to use OBR to create the repository.xml file listing all the available bundles.
Users can add / remove bundle repositories by choosing File>Preferences
...
, Plugin Repositories tab.
IGB uses OSGi Services to allow bundles to add new implementations of existing IGB elements. The new implementations will show up as soon as the bundle is started. Examples include IGB tabs and graph manipulation functions.
Extension
...
Points
IGB has several "extension points" designed specifically for pluginsApps. These are interfaces or abstract classes that
are implemented or extended by plugin versionsApp classes. Usually the Activator for the plugin App will register the plugin App as a
service in the its start() method:
Code Block |
---|
bundleContext.registerService(<extension point>.class.getName(), <plugin implementation>, new Properties()); |
...
|
Some of the available extension points areinclude:
- com.affymetrix.genometryImpl.operator.transform.FloatTransformer - transforms a graph track from the GraphAdjuster tabcom.affymetrix.genometryImpl.operator.graph.GraphOperator Operator - operates on a selection of graph tracks from the GraphAdjuster tab com.affymetrix.genometryImpl.operator.annotation.AnnotationOperator - operates on a selection of annotation tracks from the SeqMap popupor popup menu
- com.affymetrix.genometryImpl.parsers.FileTypeHandler - process a file type (by the file extension)
- com.affymetrix.igb.shared.TrackClickListener - called when the user clicks on the label portion of a track
- com.affymetrix.igb.shared.GlyphProcessor - called when IGB creates a glyphcom.affymetrix.genometryImpl.event.TierMaintenanceListener - called when tracks are added or removed
- com.affymetrix.igb.shared.ExtendedMapViewGlyphFactoryI MapViewGlyphFactoryI - a new way to display a track
- com.affymetrix.igb.search.mode.ISearchMode - a new search method option on the Search View tab
- com.affymetrix.igb.osgi.service.serviceIGBTabPanel IGBTabPanel - a tab in the JTabbedPane at the bottom and sides of IGB
Developing IGB bundles
IGB bundles are OSGi compatible, so they should have an Activator and they must have a MANIFEST.MF file that is found in the META-INF directory. These are compiled and jarred with all required classes, jars, and resources. The resulting jar is the bundle. This can then be uploaded to the bundle repository. At that point, any one can use the bundle with IGB. IGB uses the Apache Felix OSGi implementation, but this could change, so no Felix specific code should be used. In the Activator class, there is a start() and stop() method. This is where you want to put all the code to start and stop the bundle.
In the MANIFEST.MF file, there are several headers that need to be specified - fill in parenthesis below:
Panel |
---|
Manifest-Version: 1.0 |
If you look at the Plug-ins tab, you will see the Bundle-SymbolicName under the Name column, the Bundle-Description under the Description column, and the Bundle-Version under the Version column. If the Bundle-DocURL is there, the Name cell will have a blue info icon that links to this URL.
- Import-Package and Require-Bundle don't overlap, you can specify a requirement in one or the other, but Import-Package is recommended over Require-Bundle. To have a multi line header, start the continuation line after a blank in column 1.
- Put a blank line at the end of the manifest.mf file - due to a bug in felix.
- The igb, genometry and genoviz projects can by accessed as bundles. If a class/method is needed from igb/genometry/genoviz, it must be public, and the package must be exported in the MANIFEST.MF Export-Package list of the bundle that contains the class (igb, genometry or genoviz).
- If you want to add a tab panel as a bundle, there is a helper abstract class, com.affymetrix.igb.window.service.WindowActivator, that you can extend.
- The order that OSGi starts the bundles is not specified, so at any given time, one bundle cannot assume that another bundle has been started. So, if your bundle needs access to other bundles or services (like IGBService), you will not be sure when the bundle is available. For Services, like IGBService and WindowService, you can use a ServiceTracker to be notified when the required service is available - see WindowActivator for an example.
- In the IGB source code repository, in the plugins/ directory, you will find several embedded bundles that IGB uses. You can look through these to help you understand bundles.
Sample plug-in
- to create a plugin, you will create (at least) two files, the MANIFEST.MF file and the Activator
- we will create a bundle that adds a new graph function
- create the following MANIFEST.MF file:
Panel |
---|
Manifest-Version: 1.0 |
(note - there are spaces at the beginning of the extension lines, and a blank line at the end)
- Now, create the Activator,
mypackage.MyActivator.java
Code Block | ||||
---|---|---|---|---|
| ||||
package mypackage;
import java.util.Properties;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import com.affymetrix.genometryImpl.util.FloatTransformer;
public class MyActivator implements BundleActivator {
private BundleContext bundleContext;
private ServiceRegistration serviceRegistration;
protected BundleContext getContext() {
return bundleContext;
}
public void start(BundleContext _bundleContext) throws Exception {
bundleContext = _bundleContext;
serviceRegistration =
bundleContext.registerService(FloatTransformer.class.getName(),
new MyTransformer(), new Properties());
}
public void stop(BundleContext _bundleContext) throws Exception {
if (serviceRegistration != null) {
serviceRegistration.unregister();
}
bundleContext = null;
}
}
|
- And create the Transformer,
mypackage.MyTransformer.java
Code Block | ||||
---|---|---|---|---|
| ||||
package mypackage;
import com.affymetrix.genometryImpl.util.FloatTransformer;
public class MyTransformer implements FloatTransformer {
final String paramPrompt;
final String name;
public MyTransformer() {
super();
paramPrompt = null;
name = "MyTransformer";
}
public String getParamPrompt() { return null; }
public String getName() {
return name;
}
public String getDisplay() {
return name;
}
public float transform(float x) {
return x; // boring, put your own math function here, return 1.0/x; or return 3*x*x*x - 2*x*x +8*x - 6;
}
public boolean setParameter(String s) {
return true;
}
}
|
- Now, compile and jar these files into MyTransformer.jar (the jar command has a -m option for the manifest file).
- At this point, you want to put your jar into a web server using OBR (see above).
- To add the plugin to a web server without OBR, in the plugins directory, from the command line run
java -cp IGB_HOME/igb_server/lib/bindex.jar -jar bindex.jar -n IGB -q -r repository.xml .
- where IGB_HOME is your checked-out copy of the genoviz-trunk repository from Sourceforge.net.
- this will create a repository.xml file listing all the bundles and their requirements.
- make sure the repository.xml file and the bundles directory are accessible to the web server.
- in IGB, open the File>Preferences page, Plugin Repositories tab
- click the Add ... button at the bottom
- enter the web server URL for the directory that has the repositories.xml file that you created - you can use whatever you want for the name.
- Click the "Add Server" button
- close the Preferences page.
- Open the Plugins tab
- you should see your bundle in the table, click on the install checkbox.
- Once you have installed your bundle, select a graph track on the main view, and open the Graph Adjuster tab. Now if you click on the Transformation drop down, you will see
MyTransformer
in the list. Select it and click theGo
button, and you will see a new track using your bundle.
To create plug-ins using eclipse - a Quick-Start Guide
Eclipse (http://www.eclipse.org) makes it a lot easier to develop bundles. You can create a new project
as a plug in project, and it will give you wizards, etc. for development. see:http://www.vogella.de/articles/OSGi/article.html
(using eclipse Helios)
- Check out IGB from https://genoviz.svn.sourceforge.net/svnroot/genoviz/trunk into a new directory - we will refer to the project directory below as IGB_HOME.
- open a command prompt in IGB_HOME, run "svn co https://genoviz.svn.sourceforge.net/svnroot/genoviz/trunk"
- in eclipse, project explorer, "File>Import"
- in the Import popup, under "General", select "Existing Projects into Workspace"
- "Next >" button
- make sure "Select root directory:" is selected, and fill in the IGB_HOME directory
- click the "Finish" button
- Open a command prompt in IGB_HOME, and do an "ant clean" and "ant jar"
- Make sure Eclipse is displaying the Project Explorer Tab. (If not, click View>Show View>Project Explorer.)
- In Eclipse, Project Explorer, right click the IGB project and select "refresh."
- In Eclipse, Project Explorer, right click on open space, and select "Import..." > "Plug-ins and Fragments" under "Plug-in Development", click the "Next >" button
- under "Import From", select "Directory:"
- under "Plug-ins and Fragments to Import" select "Select from all plug-ins and Fragments"
- under "Import As" select "Binary Projects"
- for "Directory:", click the "Browse..." button and select the IGB_HOME/bundles, click the "OK" button
- click the "Next >" button
- under "Plug-ins and Fragments Found:", select affx_fusion, colt, freehep, image4j, jlfgr, picard, and sam
- click the "Add -->" button to add these all to "Plug-ins and Fragments to Import:", click the "Finish" button
- Ann's note: The user must have already installed the Eclipse Plug-in Development Environment, under "General" in the Help>Install New Software window.
- Do the following for the directories under IGB_HOME, genometryImpl, common, genoviz_sdk, igb, plugins/igb_service, plugins/window_service
- in eclipse, project explorer, right click on open space, and select "Import..."
- Select "Existing Projects into Workspace" under "General", click the "Next >" button
- click "Select root directory:" and click the "Browse..." button on the right
- select the project folder under IGB_HOME, and click "OK"
- click "Finish"
- note - you will find it easier to debug in eclipse if you use the Debug project. You can add the Activator for your bundle (whatever one you are developing) in com.affymetrix.igb.debug.Debug, in the static statement at the top. Choose Run>Debug>Debug As Then on Debug, you can "Debug As" "Java Application", and
this will run IGB using a dummy OSGi implementation, so that you can easily step through code, set breakpoints, etc. Please explain in more detail.** import the "debug" project** The benefit of this is that Eclipse will be able to compile as you go.- right-click Debug Configure Build Path ,c change it to always put every resource path
Sample plug-in using eclipse
- In the Project Explorer - blank area, right-click
- New > Project ... > Plug-in Project > "Next >" button
- Project name: "HelloWorld"
- "Next >" button
- Version: "1.0.0"
- check "Generate an activator ..."
- Activator
helloworld.Activator
- Rich Client Application click "No"
- "Next >" button
- Uncheck "Create a plug-in using one of the templates"
- "Finish" button
- Under the new HelloWorld project, find the META-INF directory, and open the MANIFEST.MF file by double-clicking on it.
- Click on the "Dependencies" tab,
- in the "Required Plug-ins" box, highlight org.eclipse.core.runtime, and click the "Remove" button on the right.
- next to the "Imported Packages" box, click the "Add..." button, in the "Package Selection" popup in the text box, enter
com.affymetrix.genoviz.util
and click the "OK" button. - Do the same for
org.osgi.framework
(there may be a version number in parentheses after the name).
- Right click on the blank area at the top, next to "Dependencies" and
- a context menu comes up, click "Save".
- Open the Activator.java file under src/helloworld
- add the import statement
import com.affymetrix.genoviz.util.ErrorHandler;
- at the top, and add the line
ErrorHandler.errorPanel("Hello World");
- at the end of the start() method, and
ErrorHandler.errorPanel("Goodbye World");
- at the end of the stop method
- Save the file
- In the Project Explorer
- Right click the HelloWorld project
- > "Export ..."
- "Deployable plug-ins and fragments"
- "Next >" button
- Under "Available Plug-ins and Fragments:", check only Hello World
- "Directory:" use any directory you like
- click the "Finish" button.
- The HelloWorld_1.0.0.jar file will be in the plugins directory under the directory the you specified. This is your bundle
- In the plugins directory, from the command line run
java -cp IGB_HOME/igb_server/lib/bindex.jar -jar bindex.jar -n IGB -q -r repository.xml .
- where IGB_HOME is your IGB directory.
- Now, if you can access your plugins directory from a local web server, you can open up the Plug ins tab in IGB
- (http://wiki.transvar.org/confluence/display/igbman/IGB+Tabbed+Panels#IGBTabbedPanels-PluginsPanel)
- click on the "Repositories..." button on the lower right
- Click the "Add..." button
- put the URL for your local plugin directory, fill in the name with whatever you want.
- Click the "Add Server" button
- close the Preferences popup
- You should now see your Hello World plugin.
- If you install the plugin you will see an Error popup "Hello World"
- When you uninstall the plugin, you will see an Error popup "Goodbye World"
- Congratulations!
To create plug-ins using NetBeans - a Quick-Start Guide
...
- com.affymetrix.genometryImpl.util.ServerTypeI - a server type
- com.affymetrix.igb.osgi.service.IStopRoutine - a routine to be run when IGB terminates
- com.affymetrix.igb.prefs.IPrefEditorComponent - a Preferences window to let users enter preferences
IGB-specific MANIFEST.MF requirements
To install an App into IGB, the MANIFEST.MF file needs to include the standard OSGi headers, which you should not have to manually add. You should use the BND tool to create these. Of these, two are specifically required by IGB to install and manage Apps. These are:
- Bundle-SymbolicName - used by IGB to uniquely identify an App and its various versions, e.g., "super-simple-igb-app." Users typcally do not see this name; however, App Store uses this to form URLs to App-specific pages, e.g., https://apps.bioviz.org/apps/org.lorainelab.igb.protannot
- Bundle-Version - used by IGB to distinguish different versions, called "releases", of the same App, e.g., "0.0.1." An App's version should always look like x.y.z, where x, y, and z are non-negative integers. It is important to not include suffix tags so that IGB can distinguish higher from lower versions of the same App.
IGB also recognizes and uses two optional MANIFEST.MF headers. These are:
- Bundle-Name - a user-facing, display name for an App, e.g., "Super Simple IGB App." This appears in App Manager and also on IGB App Store pages.
- Bundle-Description - base64-encoded markdown text describing the App. IGB App Manager and IGB App Store pages display the decoded, rendered Markdown to users. We provide a maven plugin (bundle-markdown-encoder) for developers to include this header in their bundle's MANIFEST.MF file.