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.