Page tree

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Let's review the Web Links module's POM (project object model) file. As with other maven projects, the POM file lists modules defines the overall structure of the project and its requirements, including:

  • project identifiers - name, artifactId and groupId that facilitate importing the project into IGB
  • dependencies - other modules and libraries the project depends on

...

  • called "dependencies." It assigns a unique identifier for the Web Links module jar file (called an artifact) and includes instructions for building and releasing the module

...

The entire POM file appears here, followed by explanations for individual sections.

...

  • parent - specifies the parent POM; in this case, the igb project POM, which defines dependencies used by Web Links and other IGB modules
  • build and plug-in tags - these tell maven how to build and install the project, as well as which tools to use

The entire POM file appears here, followed by explanations for individual sections.

Code Block
languagexml
titleWeb Links pom.xml
collapsetrue
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.affymetrix</groupId>
        <artifactId>igb-project</artifactId>
        <version>8.6.0</version>
        <relativePath>../../pom.xml</relativePath>
    </parent>
    <groupId>com.lorainelab</groupId>
    <artifactId>weblinks</artifactId>
    <packaging>bundle</packaging>

    <name>WebLinks</name>
    
    <dependencies>
        <dependency>
            <groupId>biz.aQute.bnd</groupId>
            <artifactId>bndlib</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.affymetrix</groupId>
            <artifactId>genometry</artifactId>
            <scope>provided</scope>
        </dependency>  
        <dependency>
            <groupId>com.affymetrix</groupId>
            <artifactId>igb-services</artifactId>
            <scope>provided</scope>
        </dependency>      
        <dependency>
            <groupId>com.affymetrix</groupId>
            <artifactId>igbSwingExt</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <scope>provided</scope>
        </dependency>
        <!--Start of logging dependencies-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <!--End of logging dependencies-->
        <dependency>
            <groupId>com.lorainelab</groupId>
            <artifactId>igb-preferences</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.jidesoft</groupId>
            <artifactId>jide-ultimate</artifactId>
            <scope>provided</scope>            
        </dependency>
        <dependency>
            <groupId>com.affymetrix</groupId>
            <artifactId>affymetrix-common</artifactId>
            <scope>provided</scope>           
        </dependency>
        <dependency>
            <groupId>com.lorainelab</groupId>
            <artifactId>igb-genoviz-extensions</artifactId>
            <scope>provided</scope>     
        </dependency>
        <dependency>
            <groupId>com.lorainelab</groupId>
            <artifactId>synonym-lookup</artifactId>
            <scope>provided</scope>
        </dependency>
         <dependency>
            <groupId>com.lorainelab</groupId>
            <artifactId>context-menu-api</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-clean-plugin</artifactId>
                <configuration>
                    <filesets>
                        <fileset>
                            <directory>${project.parent.basedir}/bundles/dynamic</directory>
                            <includes>
                                <include>${project.build.finalName}.jar</include>
                            </includes>
                            <followSymlinks>false</followSymlinks>
                        </fileset>
                    </filesets>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy</id>
                        <phase>install</phase>
                        <goals>
                            <goal>copy</goal>
                        </goals>
                        <configuration>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>${project.groupId}</groupId>
                                    <artifactId>${project.artifactId}</artifactId>
                                    <version>${project.version}</version>
                                </artifactItem>
                            </artifactItems>
                            <outputDirectory>${project.parent.basedir}/bundles/dynamic</outputDirectory>
                            <overWriteReleases>true</overWriteReleases>
                            <overWriteSnapshots>true</overWriteSnapshots>
                        </configuration>
                    </execution>
                </executions>
            </plugin>          
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
                        <Import-Package>*</Import-Package>
                        <Export-Package/>
                        <Service-Component>*</Service-Component>
                        <Bundle-Description>${bundleDescription}</Bundle-Description>
                    </instructions>
                </configuration>
            </plugin>  
            <plugin>
                <groupId>com.lorainelab</groupId>
                <artifactId>bundle-markdown-encoder</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>encodeMarkdown</goal>
                        </goals>
                    </execution> 
                </executions>
                <configuration>
                    <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
                </configuration>
            </plugin>       
        </plugins>
    </build>
</project>

...

Code Block
languagexml
<parent>
   <groupId>com.affymetrix</groupId>
   <artifactId>igb-project</artifactId>
   <version>8.6.0</version>
   <relativePath>../../pom.xml</relativePath>
</parent>

The parent tag references the parent tag in indicates that the POM for the IGB code base and also indicates that the module is compatible , defined using a relative path, is the parent for the Web Links POM. This means that the Web Links POM can reference dependencies defined in the parent POM. The parent tag also indicates that the  is the par the IGB code base using a relative path. By inheriting from the parent POM, the Web Links module's POM can reference dependencies defined in the parent. Dependencies defined in the parent POM is compatible with IGB version 8.6.0 and higher. See https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html for more details on how the inheritance mechanism in Maven works.  The benefits will be obvious when we discuss the dependency tag below.

Packaging Tag  

...

Packaging Tag  

Code Block
languagexml
<packaging>bundle</packaging>

...

We are using packaging type of "bundle" to take advantage of the pluggable plug-able architecture of Maven itself and the custom packaging type which is defined in the Apache Felix Maven Bundle Plugin.  Throughout the IGB project we use this maven plugin to generate all of the OSGi meta-data which makes the jar file into a module that can be managed in the IGB OSGi runtime.  

Dependencies

...

tag

Dependencies are externally provided artifacts (typically jar files) the Web Links module needs to compile and/or run. This section will highlight some of the important dependencies used in this module.  The dependencies tag contains one or more dependency child tags, described in the following section.

Note that each dependency tag includes a scope tag with value of "provided." This is because the parent POM defines these dependencies in its dependencyManagement tag. All we need to do here is indicate artifacts groupId and artifactId. 

biz.aQute.bnd:bndlib

The bndlib library manages the Declarative Services annotations used throughout the IGB project.

Code Block
languagexml
<dependency>
   <groupId>biz.aQute.bnd</groupId>
   <artifactId>bndlib</artifactId>
   <scope>provided</scope>
</dependency>

...

 

com.affymetrix:genometry

Contains data models and utility methods used throughout the IGB project and its use will be highlighted further in a discussion of the services provided by the Weblinks bundle. Note the lack of a version tag, and the inclusion of the scope tag with the value provided (note compile would also be an appropriate scope in this instance, so either/or).  The reason the version tag is not included is because this is a library with a version already declared in the dependencyManagement tag of the parent pom, so it is inherited.  

 

...

. When the user right-clicks a item to select it, the Web Links module obtains a reference to the selected data model and uses it to create linkouts to external resources. These genometry module defines these data models and their interfaces. 

Code Block
languagexml
<dependency>
       <groupId>com.affymetrix</groupId>
       <artifactId>genometry</artifactId>
       <scope>provided</scope>
</dependency>  

com.affymetrix:igb-services

This dependency is a an API module which contains many utility classes and much of the core data model content of the IGB project.  In the case of the Weblinks module, much of the logic which drives the dynamic content of the context menu is derived from the selections a user makes. These selections are represented by the data model object types defined in the genometry module.  

com.affymetrix:igb-services

most of the service interfaces which allow hooks into the IGB platform, including the interface (IgbMenuItemProvider) the Web Links module uses to add a menu item to IGB's Tools menu. The following sections will discuss this in more detail.

Code Block
languagexml
<dependency>
       <groupId>com.affymetrix</groupId>
       <artifactId>igb-services</artifactId>
       <scope>provided</scope>
</dependency> 

This module is an API module which contains most of the service interfaces which allow hooks into the IGB platform, including the interface (IgbMenuItemProvider)the Web Links moduel uses to add a menu item to IGB's Tools menu. The following sections will discuss this in more detail. 

com.affymetrix:igbSwingExt

...

A very useful utility module which is used extensively in our project.  We highly recommend you take a look at the content of this project (see https://code.google.com/p/guava-libraries/), and occasionally even demmand in our interfaces you leverage some of the collection data structures from Guava (e.g. Multimap). You will thank us later!

org.slf4j:slf4j-api

...

languagexml

...

This module (Simple Logging Facade for Java) provides a logging API developers should use to report status information to the console, useful for debugging. Never write directly to stdout or stderr. Use SLFJ instead. 

See http://www.slf4j.org/manual.html.

Code Block
languagexml
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <scope>provided</scope>
</dependency>

 

com.lorainelab:context-menu-api

This API module is the logging API we expect all module developers to use to hook into our logging system.  For more information see http://www.slf4j.org/manual.html

...

defines the interfaces and methods Web Links must implement in order to add items to the annotation context (right-click) menu.

Code Block
languagexml
<dependency>
   <groupId>com.lorainelab</groupId>
   <artifactId>context-menu-api</artifactId>
   <scope>provided</scope>
</dependency>

This module defines the interfaces and methods Web Links must implement in order to add items to the annotation context (right-click) menu. 

 

Build and plugins section

...

The utility of this plugin in our pom is largely context specific.  In our case, we are simply  hooking into the clean phase of the maven lifecycle to delete a copy of the compiled module we make using the maven-dependency-plugin.  

maven-dependency-plugin

Again, the utility The utility of this plugin is specific to the context of our build, but this plugin is used to hook into the install phase of the maven lifecycle and make a copy of the module in a location where we will later have OSGi install it during development runs.  

...

This is a special tag which instructs the felix bundle plugin to scan our modules classes for the Declarative Service annotations and generate the required service descriptor meta-data for OSGi.  For more information on this topic see http://wiki.osgi.org/wiki/Service-Component and http://www.aqute.biz/Bnd/Components

...

How the Web Links module uses IGB API extension points

IGB Services (com.affymetrix:igb-services)

Igb IGB Services is a collection of useful utilities provided by IGB to modules for reference.  In the case of the Web Links module, this service is used to register itself as a "ContextualPopupListener" and for little else.  Its worth pointing out that this would not be necessary if the ContextualPopupListener instances were tracked using the OSGi service registry, but since we cannot migrate the entire project to OSGi services all at once this more manual listener registration mechanism is still in use at this location. 

How IgbService is accessed

Weblinks accesses the IgbService implementation from the service registry. There are several ways this could be done; however, our preferred way to access services is with the use of the Declarative Service @Component annotation.the IGB framework provides for external modules - IGB Apps - to reference. The core IGB code also uses this services API. It contains interfaces that are publicly exposed as well as implementation classes which are not. Hiding the implementation details while exposing the API gives the project greater flexibility and gives developers a clearer guidelines on how to extend IGB.

The Web Links module accesses implementation of the  IGB Service interfaces via the service registry managed by the OSGI runtime. There are several ways this could be done; however, our preferred way to access services is with the use of the Declarative Service @Component annotation.

Note that in 2016, the core IGB team will focus on refining the IGB Services module to make the APIs easier to use for developers.

 

Code Block
languagejava
titleExample of Service Access Using DS
linenumberstrue
@Component(immediate = true)
public class LinkControl implements AnnotationContextMenuProvider {

    private static final String SEARCH_WEB_ICONPATH = "searchweb.png";
    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(MenuIcon.class);    
    private IgbService igbService;

    public LinkControl() {
    }
    @Activate
    private void activate() {
    }
    @Reference
    public void setIgbService(IgbService igbService) {
        this.igbService = igbService;
    }
...

In the above code block, you can see the @Component annotation is used to declare that this class will be managed as a component by the OSGi runtime.  See the Declarative Services specification for more details (http://wiki.osgi.org/wiki/Declarative_Services).  

Next, there is the creation of a privately scoped IgbService variable along with a setter for this variable.  The setter is annotated with the @Reference annotation to signal to the OSGi component runtime that this setter in an injection point for another component managed by the runtime.  When IgbService becomes available it will be "injected" (in the dependency injection sense of the terminology) into this service for reference before the component is "Activated".  

As you can see, there is an activate method annotated with the @Activate annotation to signal that this method should be called once all service dependencies have been satisfied.  The content of the method makes it clear that the igbService variable must have been instantiated before this reference.  This short block of code is a great example of the power of Declarative Services to manage the lifecycle of our services.  With almost no work, we can publish and consume services from the registry.

IgbMenuItemProvider

...


  @Override
    public Optional<ContextMenuItem> buildMenuItem(AnnotationContextEvent event) {
        if (event.getSelectedItems().isEmpty()) {
            return Optional.empty();
        }
        SeqSymmetry primarySym = event.getSelectedItems().get(0);
        if (primarySym instanceof CdsSeqSymmetry) {
            primarySym = ((CdsSeqSymmetry) primarySym).getPropertySymmetry();
        }
        return buildContextMenuItem(primarySym); // a private method defined elsewhere in the class
    }

In the above code block, the @Component annotation declares that this class will be managed as a "component" by the OSGi runtime.  See the Declarative Services specification for more details (http://wiki.osgi.org/wiki/Declarative_Services).  

Next, there is the creation of a privately scoped IgbService variable along with a setter for this variable.  The setter is annotated with the @Reference annotation to signal to the OSGi component runtime that this setter in an injection point for another component managed by the runtime.  When IgbService becomes available it will be "injected" (in the dependency injection sense of the terminology) into this service for reference before the component is "Activated".  

As you can see, there is an activate method annotated with the @Activate annotation to signal that this method should be called once all service dependencies have been satisfied.  The content of the method makes it clear that the igbService variable must have been instantiated before this reference.  This short block of code is a great example of the power of Declarative Services to manage the lifecycle of our services.  With almost no work, we can publish and consume services from the registry.

Context Menu API (com.lorainelab:context-menu-api)

The LinkControl class implements the interface AnnotationContextMenuProvider, defined in the context-menu-api module. This interface defines methods that enable the framework (IGB itself) to add menu items to context menus when users right-click them in IGB.

The key method is "buildMenuItem" which accepts an AnnotationContextEvent, created when users right-click an annotation in the main display in IGB. Note that in IGB, an "annotation" is defined as anything with start and end coordinates on genomic sequence, such as gene models, RNA-Seq read alignments, and so on.

Code Block
languagejava
titlecom.lorainelab.context.menu.AnnotationContextMenuProvider
package com.lorainelab.context.menu;

public interface AnnotationContextMenuProvider {
    public Optional<ContextMenuItem> buildMenuItem(AnnotationContextEvent event);
    public MenuSection getMenuSection();
    public static enum MenuSection {
        INFORMATION, SEQUENCE, APP, UI_ACTION;
    }
} 

When LinkControl intercepts such an event, it examines the event and returns an instance of ContextMenuItem, as appropriate. The ContextMenuItem class is a "model" object only; it doesn't specify which GUI framework must be used to create the menu and add it to the IGB interface.

Note that this design facilitates moving IGB from using Swing to JavaFX, a newer GUI toolkit that is replacing Swing in Java applications. framework handles the work of creating 

IgbMenuItemProvider

This interface, defined in the IGB Services module, is designed allow module developers to hook into the applications main toolbar.

Image Removed 

Code Block
languagejava
public interface IgbMenuItemProvider {
    public String getParentMenuName();
    public JRPMenuItem getMenuItem();
    public int getMenuItemWeight();
} 

...