Writing a Plugin part 3 - Implementing an API

Please ensure you have run the dependent module Part 2 before following this tutorial.

In this module you will learn how to implement a plugin interface and then package the plugin into a JAR ready to distribute and deploy.

Implement a Plugin API

In this example we will implement a reporting plugin by implementing the IPluginReport interface.

The main method in this interface is generateReport. It is where the report is generated. The return method is a byte[] so the framework can send the report back to the users browser.

        public byte[] generateReport(final EntityManager em, final PluginData data) throws RapidDeployException

Example Report Plugin Class

Here is an example class

package com.midvision.rapiddeploy.plugins.report.custom;

import java.io.InputStream;
import java.util.Map;

import javax.persistence.EntityManager;

import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.design.JasperDesign;
import net.sf.jasperreports.engine.xml.JRXmlLoader;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

import com.midvision.rapiddeploy.domain.plugin.IPluginReport;
import com.midvision.rapiddeploy.domain.plugin.PluginData;
import com.midvision.rapiddeploy.domain.plugin.PluginType;
import com.midvision.rapiddeploy.exceptions.RapidDeployException;
import com.midvision.rapiddeploy.plugins.report.generator.JasperReportGenerator;
import com.midvision.rapiddeploy.plugins.report.generator.MyCustomReportGenerator;
import com.midvision.rapiddeploy.service.report.AbstractReportHandler;

@SuppressWarnings("unused")
public class CustomReports extends AbstractReportHandler<CustomReportsDomain> implements IPluginReport {
        
    private static final Log log = LogFactory.getLog(CustomReports.class);
        
        private CustomReportsDomain customReportsDomain;

    /*
     * This method returns the unique plugin name and version.
     * 
     * @see com.midvision.rapiddeploy.domain.plugin.IPluginFunction#getPluginName()
     */
        @Override
        public String getPluginName() {
                return "Custom Reports (v" + getPluginVersion() + ")";
        }

    /*
     * This method returns this plugin type.  This plugin is a reporting plugin.
     * 
     * @see com.midvision.rapiddeploy.domain.plugin.IPluginFunction#getPluginType()
     */
        @Override
        public PluginType getPluginType() {
                return PluginType.report;
        }

    /*
     * This method initialises the plugin and loads the default UI data.
     * 
     * @see com.midvision.rapiddeploy.service.plugin.AbstractPluginHandler#init()
     */
        @Override
        public void init() {
                customReportsDomain = super.init(CustomReportsDomain.class);
        }

    /*
     * The method returns the version of the plugin
     * 
     * @see com.midvision.rapiddeploy.domain.plugin.IPluginFunction#getPluginVersion()
     */
        @Override
        public String getPluginVersion() {
                return "1.0.0";
        }

    /**
     * This method is used to load the report template from the plugin JAR file based on the 
     * template name the user selects in the UI.
     * 
     * @param templateName
     * @return
     * @throws RapidDeployException
     */
        private InputStream loadReportTemplate(final String templateName) throws RapidDeployException {
                if (StringUtils.isNotBlank(templateName)) {
                        for (final String validTemplateName : getAllReportTemplateNames()) {
                                if (validTemplateName.trim().equals(templateName)) {
                                        if ("List Tables".equals(templateName)) {
                                                return CustomReports.class.getClassLoader().getResourceAsStream("com/midvision/rapiddeploy/plugins/report/custom/templates/tableList.jrxml");
                                        }
                                }
                        }
                }
                throw new RapidDeployException("Report template named [" + templateName + "] has not been defined for this plugin."); 
        }
        
    /**
     * This method loads the report logo from the plugin Jar file
     * @return
     * @throws RapidDeployException
     */
        private InputStream loadReportLogo() throws RapidDeployException {
                return CustomReports.class.getClassLoader().getResourceAsStream("com/midvision/rapiddeploy/plugins/report/custom/templates/images/my_report_logo.png");
        }
        
    /*
     * This method implements the Reporting plugin interface.  It generates the report as a byte array.
     * 
     * The database connection and the UI plugin data are parameters to this method.
     * In this example the JasperReport template is loaded and compiled.  
     * Then the report, data, and format are passed into the JasperReportGenerator to fill the template with the
     * data and then generate the report. 
     * 
     * @see com.midvision.rapiddeploy.domain.plugin.IPluginReport#generateReport(javax.persistence.EntityManager, com.midvision.rapiddeploy.domain.plugin.PluginData)
     */
        @Override
        public byte[] generateReport(final EntityManager em, final PluginData data) throws RapidDeployException {
                this.setPluginData(data);
                final String selectedReportTemplateName = getSelectedReportTemplateName();
        final String reportFormat = getSelectedReportFormat();
        final Map<String, Object> params = getSelectedReportParameters();
                
                InputStream templateStream = null;
        try {// Load report template
                templateStream = loadReportTemplate(getSelectedReportTemplateName());
            final JasperDesign design = JRXmlLoader.load(templateStream);
            // Compile report template
            final JasperReport report = JasperCompileManager.compileReport(design);
            params.put("logo", loadReportLogo());
            
                final Session session = em.unwrap(org.hibernate.Session.class);
                try {
                        final JasperReportGenerator generator = new JasperReportGenerator(report, params, reportFormat);
                        session.doWork(generator);
                        return generator.getReport();
                } finally {
                        if (session != null) session.close();
                }
                
                /*
                 * If you want to use another database connection to get the data then you can 
                 * use a different Hibernate session factory.
                 * 
                 * You can also create a new ReportGenerator if you do not want to use JapserReports.
                 * New report generators must implement the org.hibernate.jdbc.Work Interface.
                 * 
                 */
                
                /*
                SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
            Session session1 = sessionFactory.openSession();
            try {
                final MyCustomReportGenerator myGenerator = new MyCustomReportGenerator(params, reportFormat);
                session1.doWork(myGenerator);
                        return myGenerator.getReport();
                } finally {
                        if (session1 != null) session1.close();
                }       
                */
        } catch (final Exception e) {
                throw new RapidDeployException("Error generating " + reportFormat + " report.", e);
                } finally {
                        IOUtils.closeQuietly(templateStream);
                }       
        }

    /*
     * This method returns the report icon, which is diplayed when the plugin is selected.
     * 
     * @see com.midvision.rapiddeploy.service.plugin.AbstractPluginHandler#getIconResourceName()
     */
    @Override
    public String getIconResourceName() {
        return "image/jasper_report_64.png";
    }

    /*
     * This method returns the thumbnail report icon, which is diplayed when the plugin is in a table view.
     * 
     * @see com.midvision.rapiddeploy.service.plugin.AbstractPluginHandler#getThumbnailResourceName()
     */
    @Override
    public String getThumbnailResourceName() {
        return "image/jasper_report_24.png";
    }
}

Create the Service Provider

For the framework to be able to load the plugin from within the Jar file, a service provider file needs to be created to define which class contains the plugin implementation. In this example, create a file in the META-INF/services directory called com.midvision.rapiddeploy.domain.plugin.IPluginFunction. Add the class name of the plugin implementation class.

com.midvision.rapiddeploy.plugins.report.custom.CustomReports

Build the Plugin

Use maven to build the JAR file by running: mvn clean install.

Deploy the Plugin.

Copy the built Jar file to the $MV_HOME/lib dir, then restart the RapidDeploy server.

Conclusion

We have learnt how we implement a plugin API to generate a report. We then created a service provider so the framework knows which class to load.

Finally the plugin was built and deployed to the framework server.

Download the example project here