Jump to content
We've recently updated our Privacy Statement, available here ×

gdmoreno

Members
  • Posts

    114
  • Joined

  • Last visited

 Content Type 

Profiles

Forum

Events

Featured Visualizations

Knowledge Base

Documentation (PDF Downloads)

Blog

Documentation (Test Area)

Documentation

Dr. Jaspersoft Webinar Series

Downloads

Everything posted by gdmoreno

  1. IntroductionIn iReport, you can set up multiple datasources within the same report. This is especially useful when dealing with table, list and subreport components, which can use their own subdatasets. Using different JDBC Connection for subreport, list and table componentsA subreport, table or list component can use a different JDBC connection than the main report. Normally the $P{REPORT_CONNECTION} parameter is used to pass the main report's database to the subreport, list or table component. Since we want to use a different database connection we need to create a parameter that will create a new database connection. Create a separate parameter, and give it a name for the second database connection. Set the type to java.sql.Connection. Note that you may have to manually enter the datatype if it isn't available in the drop down menu. Uncheck the 'Use as prompt' box. You'll now define the JDBC URL in the default value expression for that parameter. Use the following expression to create the jdbc connection: java.sql.DriverManager.getConnection("jdbc:postgresql://dbhost:5432/database","dbusername","dbpassword") // Modify this to match your datasource When Using the Subreport componentFrom the palette in the main report, drag the subreport element onto your report. The subreport wizard will appear. On the first page, choose the path to your subreport and click 'next'. On the second page, select 'don't use any connection or datasource' and click 'next'. Click 'next' through the parameters section on the third page. Choose an option for your subreport expression and click 'Finish'. Click on the subreport element to highlight it and edit the Parameters property by clicking the (...) button. Add the following parameters: SUBREPORT_CONNECTION = $P{SUBREPORT_CONNECTION} If you recive a driver error when trying to run the report in iReport, you may need to copy your database driver jar file to your jdk_version/jre/lib/ext directory. When deploying to JasperServer, make sure your subreport is added as a resource to the main report and that the main report correctly refers to the subreport in the subreport expression with the repo syntax ("repo:subreport.jrxml"). Using List or Table componentsList and Table components are useful when working with subdatasets, which can have their own queries separate from the main report's query, as well as their own datasources. Table componentsWhen dragging over a table component to one of the report's bands, iReport will prompt you for the subdataset it is for. After choosing, it will then prompt your for the fields you want, and finally the datasource to use. Once you've got the Table component in the design area, you can edit the table datasource (by right-clicking on the component). In the "Connection/Datasource Expression" tab, set the drop-down to "Don't use connection or datasource"In the "Parameters" tab, add a new parameter:Set the "Dataset parameter name" to REPORT_CONNECTIONSet its corresponding Value Expression to the parameter containing the database connection statementList componentsWhen dragging over a list component to one of the report's bands, iReport will not prompt you for anything, unlike the process for the table component. You'll have to configure it once it's on the design area. Once the List component is in the design area, you can edit the table datasource (by right-clicking on the component). In the "Connection/Datasource Expression" tab, set the drop-down to "Don't use connection or datasource"In the "Parameters" tab, add a new parameter:Set the "Dataset parameter name" to REPORT_CONNECTIONSet its corresponding Value Expression to the parameter containing the database connection statementOnce you have that ready, you will be ready to drag fields, values or parameters for that subdataset over to the List element's area. TroubleshootingWe have noticed that on some systems, we need to change the report language from "Groovy" to "Java" for the above process to work. It seems like it's an intermittent process.
  2. IntroductionIn this document, we discuss how to upgrade your server from an older version to a newer version. We present the two strategies, and we discuss the risk factors involved in upgrading. The greater number of customizations, the greater the risk involved in upgrading. We discuss how other factors can complicate an upgrade in a separate article. Why Upgrade?Every release of JasperReports Server brings greater stability and functionality. In addition, each release contains fixes for bugs found in previous versions. Each version contains its own release notes, which highlights new functionality and what bugs were fixed, so that's a useful place to start when deciding whether to upgrade or not. Upgrade Process and ChecklistUpgrading can be a complicated process, especially if you've added customizations and if JasperReports Server integrates with other applications. To get started, make sure you have this available: Documentation from the last installation - this is where you documented your installation and configuration steps, the steps you took to integrate it with other applications, and steps you took for applying custom software A test environment - an environment where you'll test out your upgrade. Make sure that it's similar to what you have in a production environment so that tests will be valid Backup your database and the web application's filesystem - you want to make you can revert to a good, known state. Read the installation guide for the version you're testing - each version contains slightly different information. At the end of this document, there's a table with where you can find the installation guides for each server version.Upgrading from version to versionTo upgrade from one server version to another, please check the table below. If you're going from a particularly older server version to a newer version, you may need to pass through an intermediate server version before getting to your final destination. Source/Target 3.5 3.7.1 4.0 4.1 4.2 4.5 3.5 X Follow upgrade notes in 3.7.1 guide Upgrade to 3.7.1 first, then upgrade to 4.0 Upgrade to 3.7.1 first, then upgrade to 4.1 Upgrade to 3.7.1 first, then upgrade to 4.2 Upgrade to 3.7.1 first, then upgrade to 4.5 3.7.1 X X Follow upgrade notes in 4.0 Install guide Follow upgrade notes in 4.1 Install guide Follow upgrade notes in 4.2 Install guide Follow upgrade notes in 4.5 Install guide 4.0 X X X Follow upgrade notes in 4.1 Install guide Follow upgrade notes in 4.2 Install guide Follow upgrade notes in 4.5 Install guide 4.1 X X X X Follow upgrade notes in 4.2 Install guide Follow upgrade notes in 4.5 Install guide 4.2 X X X X X Follow upgrade notes in 4.5 Install guide 4.5 X X X X X X If your server version is older than version 3.5, please refer to the JasperServer Installation guide for version 3.5, which you can find here - http://support.jaspersoft.com/downloads-JSJA-Pro3_5.html. Just download the link for "JasperServer Professional Version v3.5 Install Guide (3.5.x)". Upgrading StrategiesThere are two principal strategies for moving to a new version: Upgrading the server environment - this involves replacing the old application with the new application version, and doing an upgrade to the database (running scripts that update the JasperReports Server database schema that works with the application) Installing the new server version, and moving old repository to new server - this involves creating a separate installation for the new server version, applying the same customizations that were added to the older server version, and then exporting the repository from the old server and importing it to the new server version.Which strategy to choose? Advantages Disadvantages Strategy 1 - Upgrade Server Easier to do Often see unexpected errors Strategy 2 - Install new version You start from a clean environment Can take longer Installation Guides and where you can find themThe installation guides are in the support portal in the server documentation section. Version Location 3.5 http://support.jaspersoft.com/downloads-JSJA-Pro3_5.html 3.7.1 http://support.jaspersoft.com/downloads_JSJA-Pro3_7_1.html 4.0 http://support.jaspersoft.com/downloads_Jaspersoft_BI_4_0_0.html 4.1 http://support.jaspersoft.com/downloads_JasperReportsServer_4_1.html 4.2 http://support.jaspersoft.com/downloads_JasperReportsServer_4_2.html 4.5 http://support.jaspersoft.com/downloads_JasperReportsServer_4_5_0.html Testing Environment Make sure to have access to a testing environment where you can test out the new version This should hopefully include access to data that's similar to what you find in production We also recommend doing "before and after" testing of report execution times If your old server environment went through acceptance testing of some kind, then you could perform those same tests on the new server's environment.
  3. IntroductionSetting up a server environment in production usually involves customizations and configurations that need to be taken into account when doing an upgrade. In general, doing custom software not based on API's is high risk, since the underlying server code may change from version to version, which could force you to refactor that custom code for the new server version. Of medium risk are changes to JSP pages. JSP pages don't change that much from version to version (aside from the UI changes in 4.0), but you'll still need to be careful. The least risky changes are changes made to configuration files (such as jasperreports.properties and others), since they are just property files that drive the server's behavior and don't change much from version to version. CategoriesThe most typical customization topics include: SSO Software Customization Engine level (e.g., State Street's "service engine", etc) UI Customization (Themes, Flows, Chart Customizers) Changes to JSP files (landing pages after login, decorators, etc) Additional values in properties files (jasperreports.properties, virtualizer settings, other properties files under WEB-INF directory) Modifications to the JasperReports Library Development of customized web services Custom Datasources (and associated Spring configuration files) Other layers of the software stack (databases, operating systems, JVM, web servers, etc) Documentation - you have a detailed list and a procedure for installing customizations and other modifications to JRS.SSOThe security framework that ships with JasperReports Server changed from using Acegi Security in 3.5.1 to using Spring Security 2.0 in version 3.7. Consequently, if there is custom software that uses Acegi, it will break in newer versions of the server, and that code will have to be refactored. If there's a straightforward configuration you applied from the Authentication cookbook (such as enabling LDAP authentication), you'll have to follow re-apply those configuration settings in the new server version's Spring configuration file; this is less risky than doing actual custom software. Software CustomizationsEngine-level customizationsThese are the most delicate, because the server code these customizations are based on changes from server version to server version. Since they are not going through API's, there is no guarantee that the custom software would even compile, much less work properly. UI CustomizationsThemes were introduced in version 4.0, so if you're upgrading from that version to a newer version, you should not encounter problems at this level. Themes are stored in the repository, so by simply exporting and importing the repository, you'll get the themes you defined. Flow files are XML files you find under /jasperserver-pro/WEB-INF/flows, so if there are changes you've made there, they should port without a problem. Chart customizers, which involve custom Java code packed up in JAR files, should port as well. JSP file customizationThese are low-to-medium level of complexity, should be fairly straightforward to port. The big changes happened from version 3.7.1 to 4.0, so customizations done in 3.7.1 that you wish to bring over to version 4.0+ will need a little bit more work. Modified properties filesThese changes include modified version of jasperreports.properties, ehcache.xml, log4j.properties and other files you may have changed on your old server version. It's usually enough to just apply the same modifications from your old server to your new server. Modifying the JasperReports LibraryThe JasperReports library is open source, and from time to time, customers with deep technical knowledge and expertise modify the underlying library on which the server is based. This is a really deep, fundamental change to the lowest level of functionality that we don't recommend and that clients take at their own peril. The same caveats that we mentioned for engine-level customizations apply here. Custom DatasourcesPorting custom datasources from one environment to another is of medium risk. Custom datasources involve the development of custom software and configuring Spring files. What makes it less risky than pure custom software development is that custom datasources use API's that won't change (The nitty-gritty is that they are JAVA classes that implement interfaces for datasources, query executers and executer factories, and these interfaces will not change). Other layers of the software stackSince JasperReports Server is one element within its software stack, we need to take into account changes you may have done outside of JasperReports Server. This includes web servers, databases, operating systems, and JVM settings. You can check which product versions work best with JasperReports Server by checking your version's Supported Platforms sheet, which you can find here: http://www.jaspersoft.com/supported-platforms. Web ServersThis is typically the least problematic element in the software stack, but properties to remember include any global timeout settings you may defined for your old server environment as well as defining concurrent user settings. Application ServersSettings that have an impact here include the size of the database connection pool and the number of threads available to JasperReports Server. Make sure to look what your old server's settings are. Java Virtual MachineThe JVM settings are usually defined in the application server's startup script, so make sure to note what they are there in your old server environment. If you use the default settings for the JVM, you'll get lower memory settings, which would impact performance. DatabasesBeyond just making sure that you're using a supported database, make sure to optimize it in the same way you may have done with another database. It's also easy to forget about the database driver, so take care to use the most current driver, and work back from there if you're not getting the performance you want. Process for upgradingTo get a good sense of how challenging an upgrade can be, you can try doing it yourself on a test environment. You should then be able to tell what parts, if any, broke during the upgrade. By listing the parts that broke, you can get a sense of the level of effort needed to do the upgrade.
  4. IntroductionChart customizers allow us to customize how charts get output in iReport and in JasperReports Server, specifically those charts that are based on the JFreeChart library. To customize a graph, a class must be written that implements the net.sf.jasperreports.engine.JRChartCustomizer interface. The only one method to implement is the customize method. Setting up the Eclipse project Create a new Eclipse project, as you normally would Create a lib directory, and add the necessary JAR filesYou'll need to add the JFreeChart JAR file, and the JasperReports JAR files, which you can copy from the JasperReports Server LIB directory (/jasperserver-pro/WEB-INF/lib): All the JAR files whose filenames start with "jasperreports", and jfreechart-1.0.12.jar; the exact JFreeChart version number will probably vary on your JasperReports Server installation. You'll also need to add jcommon-1.0.15.jar, which you can also find in JasperReports Server's LIB directory. Finally, you'll also need the AspectJ JAR files, which you can also find in the JasperReports Server LIB directory. Just add all the JAR files whose filenames start with the label "aspectj". Make sure to add those JAR files to the library build pathCreating a custom classYou'll need to create a class that either implements the JRChartCustomizer interface, or that extends the JRAbstractChartCustomizer class. In the sample code below, the class implements the customize method so that every shape in the chart will be a circle. Creating the right shape and using the JFreeChart API is beyond the scope of this article; suffice it to say that there exist rich resources out on the web. package test.customizer; import java.awt.*; import java.awt.geom.*; import org.aspectj.asm.internal.HandleProviderDelimiter; import org.jfree.chart.*; import org.jfree.chart.plot.*; import org.jfree.chart.renderer.*; import org.jfree.chart.renderer.category.CategoryItemRenderer; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.data.category.CategoryDataset; import org.jfree.data.general.PieDataset; import org.jfree.data.xy.XYDataset; import org.jfree.util.ShapeUtilities; import net.sf.jasperreports.engine.JRAbstractChartCustomizer; import net.sf.jasperreports.engine.JRChart; public class ColorScaleCustomizer extends JRAbstractChartCustomizer { public void customize(JFreeChart freeChart, JRChart jrChart) { XYPlot plot = (XYPlot)freeChart.getPlot(); XYItemRenderer renderer = (XYItemRenderer)plot.getRenderer(); int seriesCount = plot.getSeriesCount(); for (int i = 0; i < seriesCount; i++) { Shape shape = new Ellipse2D.Double(0, 0, 5, 5); renderer.setSeriesShape(i, shape); } }} Create a JAR fileWhen you're done creating the customizer classes, you'll need to export the Eclipse project as a JAR file. You can do this by right-clicking on the project name, choosing export, and going through the steps of creating a JAR file. You do not need to export project-specific files (like the .classpath and the .project files). Add the JAR file to the iReport classpathOnce the JAR file has been exported out to your filesystem, you'll need to add it the iReport classpath. You do this in iReport by choosing "Tools -> Options" and then adding the JAR file to the classpath tab, as in the screenshot below.
  5. IntroductionIn this article, we go through the process of creating a custom datasource. As an example to illustrate the process, we will use the financial data that you can pull down from the Yahoo Finance site. This is meant only as an example of how you can pull data dynamically to then make it available for use with JasperReports. Yahoo Finance is a leading financial news and data site, which stores historical financial data that we will use here. Yahoo Finance provides a URL-based API for requesting stock data, where you can dynamically build a URL with parameters to specify the stock, the date ranges, and the data you're interested in pulling down. For example, the URL below: http://ichart.finance.yahoo.com/table.csv?s=YHOO&d=0&e=28&f=2010&g=d&a=3&b=12&c=2009&ignore=.csv will return a CSV file for Yahoo (its ticker symbol is YHOO). The URL parameters stand for the following: sn Ticker symbol (YHOO in the example) a The "from month" - 1 b The "from day" (two digits) c The "from year" d The "to month" - 1 e The "to day" (two digits) f The "to year" g d for day, m for month, y for yearly The goal of this article is to show how you can pull this data down for use by JasperReports. To accomplish this, we will need to create an implementation of the JRDataSource interface. The JRDataSource InterfaceThe JRDataSource interface consists of two method declarations: next() and getFieldValue(). The "next" method advances the pointer to the next record in the data JasperReports is parsing, while getFieldValue, which takes a field name as a parameter, returns the value for that field of the record that JasperReports is currently examining. A good way to remember what the data represented by the JRDataSource interface looks like is to think of it as a generic set of data rows, with each row having its own set of fields, whose values correspond to a known set of field names. In a way, it's an abstraction of what data organized in rows and columns looks like. public interface JRDataSource { /*** Tries to position the cursor on the next element in the data source.* @return true if there is a next record, false otherwise* @throws JRException if any error occurs while trying to move to the next element*/public boolean next() throws JRException; /*** Gets the field value for the current position.* @return an object containing the field value. The object type must be the field object type.*/public Object getFieldValue(JRField jrField) throws JRException; } Implementing the YahooFinanceDataSource classDevelopment RequirementsTo develop this example class, I created an Eclipse project and added the JAR files from JasperReports Server's WEB-INF/lib directory, as well as the JAR files from Tomcat's lib directory. The object variablesTo keep track of the individual rows in the datasource, I set up the rows as individual String values within an ArrayList object. The index variable serves to keep track of which row in the datasource the application is processing, while the listLength variable keeps track of the array's size. private ArrayList arrs; private int index = -1; private int listLength = 0; Implementing the constructorFor this example, I set up a default constructor, although you can easily imagine other constructors with parameters that a user has entered in somewhere. Its job is to go out and call the URL we saw at the beginning of this article, and populate the arrs and listLength object variables. Once this object is populated with the right data, JasperReports can go ahead and use that datasource object to fill in a report. public YahooFinanceDataSource() { String request = 'http://ichart.finance.yahoo.com/table.csv?s=YHOO&d=0&e=28&f=2010&g=d&a=3&b=12&c=2009&ignore=.csv'; // this is the URL we saw at the beginning InputStream rstream = null; HttpClient client = new HttpClient(); GetMethod method = new GetMethod(request); arrs = new ArrayList(); // Send GET request int statusCode = 0; try { statusCode = client.executeMethod(method); } catch (HttpException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } if (statusCode != HttpStatus.SC_OK) { System.err.println('Method failed: ' + method.getStatusLine()); } // Get the response body try { rstream = method.getResponseBodyAsStream(); } catch (IOException e) { e.printStackTrace(); } // Process the response from Yahoo! Web Services int rowNum = 0; BufferedReader br = new BufferedReader(new InputStreamReader(rstream)); String line; try { while ((line = br.readLine()) != null) { // Add the line into the array of strings arrs.add(line); rowNum++; } listLength = rowNum; } catch (IOException e) { e.printStackTrace(); } try { br.close(); } catch (IOException e) { e.printStackTrace(); } } Implementing the 'next' methodImplementing the 'next' method is straightforward: public boolean next() throws JRException { index++ ; return (index < listLength); } The index value is getting incremented, and then compared against the listLength variable, which stores the number of records that the datasource object contains. If the index value equals the listLength value, then the method returns false, indicating to the application that it's reached the datasource's last record. Implementing the 'getFieldValue' methodImplementing the 'getFieldValue' involves returning the value of the requested field; the getFieldValue method gets a parameter specifying what field to return from the current record in the datasource. The implementation below is straightforward and quite typical for custom datasources; it's checking the requested fieldname for the known set of fields and returning the value that corresponds to that position in the datasource's record. public Object getFieldValue(JRField jrField) throws JRException { Object value = null; String fieldName = jrField.getName(); String line = (String) arrs.get(index); String [] data = line.split(','); if (fieldName.equals('Date')) { value = data[0]; } else if (fieldName.equals('Open')) { value = [1]; } else if (fieldName.equals('High')) { value = data[2]; } else if (fieldName.equals('Low')) { value = [3]; } else if (fieldName.equals('Close')) { value = [4]; } else if (fieldName.equals('Volume')) { value = [5]; } else if (fieldName.equals('Adj Close')) { value = [6]; } return value ; } Other methodsYou can also add other methods and calls to helper classes; once you're in the custom development world, you can make calls to whatever's at your disposal. In my own case, I added the method below, to make the field names I'm defining easily available to a client of the YahooFinanceDataSource class. public static String fieldNames() { String fieldNames = {'Date', 'Open', 'High', 'Low', 'Close', 'Volume', 'Adj Close'}; return fieldNames; } Using the custom datasource classTo use the custom datasource in a server environment, you'll need to create associated ExecuterFactory and QueryExecuter classes (these would both be custom implementations of interfaces in JasperReports Server), which is outside the scope of this article. An easy way to test this custom datasource is to create a custom DataSourceProvider class that uses the custom datasource class, and deploy these two classes for use within iReport. We look at that idea in an article you can find here: Using a custom DataSourceProvider in iReport with custom datasources
  6. IntroductionIn this article, we take a look at how to use a custom datasource in iReport Designer. For this example article, we will use the example custom datasource we developed in another article, the one about pulling data from the Yahoo Finance website. One easy way to use the custom datasource is to first implement a custom DataSourceProvider class, and have that class manage the instantiation of the custom datasource. By packaging up these two classes (and any dependent classes) into a JAR, and then adding that JAR file to the iReport Designer class, we can create a datasource in iReport Designer of the "JRDataSourceProvider" type, and then design a report as we normally would. This article uses the Yahoo Finance custom datasource as an example to illustrate that process. The JRDataSourceProvider interfaceThe JRDataSourceProvider interface is a simple interface to implement; in our example we only care about implementing the "create" method (it instantiates the custom datasource) and the "getFields" method, which iReport Designer calls to figure out the fields that the report will have for the custom datasource. package net.sf.jasperreports.engine; public interface JRDataSourceProvider { public boolean supportsGetFieldsOperation(); public JRField getFields(JasperReport report) throws JRException, UnsupportedOperationException; public JRDataSource create(JasperReport report) throws JRException; public void dispose(JRDataSource dataSource) throws JRException; } Implementing the YahooFinanceDataSourceProvider classDevelopment requirementsTo develop this example class, I created an Eclipse project and added the JAR files from JasperReports Server's WEB-INF/lib directory, as well as the JAR files from Tomcat's lib directory. There is no need to implement a constructor in this example, or to maintain object variables. For the "dispose" method, I gave it an empty implementation. Implementing the "supportsGetFieldsOperation" methodThis method is straightforward, all you need to do is return true if the data source provider indicates that it is able to introspect the data source and discover the available fields, and false otherwise. public boolean supportsGetFieldsOperation() { // TODO Auto-generated method stub return false; } In my example I'm returning false, because instead of doing an introspection of the data, I'm defining the datasource fields elsewhere. But that's just a development choice I made for the sake of this example. Implementing the "create" methodImplementing this method is straightforward too, all it does is create an instance of the custom datasource object. Of course, you can add more complexity to how you create the custom datasource, but the essence of the problem is that you create the custom datasource in this method. public JRDataSource create(JasperReport report) throws JRException { // TODO Auto-generated method stub return new YahooFinanceDataSource(); } Implementing the "getFields" methodThis method returns an array of JRField objects that represent the datasource's available fields for designing reports. In this method, I call a static method on the custom datasource class (the YahooFinanceDataSource.fieldNames() call) that returns the list of field names as a String array, which I then convert to JRField objects, returning an array of JRFields objects in the method's last line. public JRField[] getFields(JasperReport report) throws JRException, UnsupportedOperationException { ArrayList fields = new ArrayList(); String [] fieldNames = YahooFinanceDataSource.fieldNames(); for (String s : fieldNames) { JRDesignField field = new JRDesignField(); field.setName(s); field.setValueClassName("java.lang.String"); fields.add(field); } return (JRField[]) fields.toArray(new JRField[fields.size()]); } Process for deploying to iReport DesignerThe process is straightforward, but it does involve several steps. Package up a JAR fileThis JAR file needs to contain the custom DataSourceProvider and datasource classes you developed, as well as any dependent classes. You can do it in Eclipse by right-clicking on the project name and choosing the export function; you'll need to go through several steps, and in the end, Eclipse will create a JAR file for you on your file system. Add the JAR file to the iReport Designer classpathAfter launching iReport Designer, go to "Tools" then "Options", and then a new window should appear in the foreground. Choose the "Classpath" tab, and then hit the "Add JAR" button. Browse to the location where you saved your JAR from the previous step and hit "OK" to finish the process. Add the custom datasource in iReport Designer as a "report datasource"Click on the "report datasources" button (the fourth icon from the top left), and you should get a "Connections / Datasources" pop-up like the one below: Click on "New" Select the "JRDataSourceProvider" option and hit "Next." On the next step, give it a name (it doesn't matter what it is), and then fill in the fully qualified class name of the custom DataSourceProvider class you developed earlier. Hit the "Test" button to confirm success Save the connectionCreate the report queryNow we will create the report query, which is in fact just referencing the fields that are available in the custom datasource. Click on the report query button, click on the "DataSource Provider" tab, and hit the "Get fields from datasource" button; that should generate the fields that are available in the custom datasource. If you've successfully gotten to this point, you can hit the "Refresh Preview Data" button, and it will go ahead and display the data that the custom datasource contains, such as in the image below: Now you can go ahead and design a report as you normally would, such as in the screenshot below: And when clicking the "Preview" button, you can achieve the usual result you get when creating reports, such as what you see below:
  7. IntroductionThe problems we're addressing are: How to create a custom authentication filterHow to create profile attributes with the custom filterHow to call up the profile attribute when creating a custom data source - when we pull up the attribute values, then we'll be able to take those values and do any extra processing.Overview of SolutionCreate the custom authentication filterHave it read the request parameters from the URLAdd the profile attribute values for the parameters used in the User DTO objectsExecute the reportCall the report via a URLHave the custom data source use the profile attribute values to do extra processingCreate the custom authentication filter classHave it implement javax.servlet.Filter and org.springframework.beans.factory.InitializingBeanImplement the doFilter methodHave it create the profile attributes based on the request parametersCheck if the parameter contains values necessary for a User DTO objectIf so, then create a profile attribute based on that key-value pair package example.cds.auth; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.IOException; import java.util.Iterator; import java.util.Map; import java.util.Set; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.InitializingBean; import org.springframework.security.Authentication; import org.springframework.security.context.SecurityContextHolder; import com.jaspersoft.jasperserver.api.metadata.user.domain.ProfileAttribute; import com.jaspersoft.jasperserver.api.metadata.user.domain.impl.client.MetadataUserDetails; import com.jaspersoft.jasperserver.api.metadata.user.service.ProfileAttributeService; import com.jaspersoft.jasperserver.api.metadata.user.service.impl.ProfileAttributeServiceImpl; public class SimpleAuthFilter implements Filter, InitializingBean { private static Log log = LogFactory.getLog(SimpleAuthFilter.class); private static final String [] userAttribs = {"teacherId", "userType","schoolId", "districtId"}; private ProfileAttributeService profileAttributeService; public ProfileAttributeService getProfileAttributeService() { return profileAttributeService; } public void setProfileAttributeService( ProfileAttributeService profileAttributeService) { this.profileAttributeService = profileAttributeService; } @Override public void afterPropertiesSet() throws Exception { // TODO Auto-generated method stub } @Override public void destroy() { // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // TODO Auto-generated method stub HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; MetadataUserDetails user = null; Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null && auth.getPrincipal() != null) { if (auth.getPrincipal() instanceof MetadataUserDetails) { user = (MetadataUserDetails) auth.getPrincipal(); Map<String, String[]> map = req.getParameterMap(); // if teacherId, userType, schoolType or districtId in param map // then add them to profile attribute processReqParams(map, user); } else { log.debug("The authentication object was not of the correct type: " + auth.getPrincipal().getClass().getName()); } } chain.doFilter(req, res); } @Override public void init(FilterConfig arg0) throws ServletException { // TODO Auto-generated method stub } // This particular method could be greatly improved; for example it could // first check for the existence of those attributes before trying to // create them, but this is meant as an example private void processReqParams(Map<String, String[]> map, MetadataUserDetails user) { Set keys = map.keySet(); Iterator i = keys.iterator(); while (i.hasNext()) { Object o = i.next(); String key = (String) o; String value = (String) map.get(o)[0]; log.debug("key = " + key + ", value = " + value); if (isFromUserDTO(key) == true && value != null) { // Create the profile attribute if (profileAttributeService == null) { log.debug("profileAttributeService is null :("); } else { log.debug("profileAttributeService is not null"); } ProfileAttribute attrib = profileAttributeService.newProfileAttribute(null); attrib.setPrincipal(user); attrib.setAttrName(key); attrib.setAttrValue(value); profileAttributeService.putProfileAttribute(null, attrib); } else { log.debug("The key is not in the UserDTO or the value is null"); } } } private boolean isFromUserDTO(String key) { boolean isFromUserDTO = false; for (String s : userAttribs) { if (key.equals(s)) { isFromUserDTO = true; break; } } return isFromUserDTO; } } Modify the Spring configuration fileModify the applicationContext-security-web.xml file in the /jasperserver-pro/WEB-INF directory. Add the bean definition and add the bean id to the filterChainProxy. For example, the bean ID definition will look like this: <bean id="SimpleAuthFilter" class="example.cds.auth.SimpleAuthFilter"> <property name="profileAttributeService"> <ref bean="profileAttributeService" /> </property> </bean> Adding the bean ID to the filterChainProxy will look like this: /**=httpSessionContextIntegrationFilter , ${bean.loggingFilter} , ${bean.userPreferencesFilter} , ${bean.authenticationProcessingFilter} , ${bean.userPreferencesFilter} , SimpleAuthFilter,${bean.basicProcessingFilter} , requestParameterAuthenticationFilter , JIAuthenticationSynchronizer , anonymousProcessingFilter , exceptionTranslationFilter , filterInvocationInterceptor , switchUserProcessingFilter How to add profile attributesI added profile attributes in the processReqParams method above, and it's an example of how to programmatically add profile attributes for a logged-in user. The method calls up the profileAttributeService that was defined for the SimpleAuthFilter bean, which allows us to manipulate profile attributes via the engine. You can verify that the attributes have been written to the database by consulting the jiprofileattributes table. How to pull up the profile attributesOnce the profile attributes are stored, we can go ahead and pull them up when creating the custom data source object. The process is to: Call up the data source constructorWithin the constructor, call up the current user objectGet that user's profile attributesDo any extra processing at this point with the profile attribute valuesBelow you'll find an example of how to pull up the profile attributes data. It's a modified version of the CustomDataSource class that our products ships with, which you can find in the samples directory. public CustomDataSource() { MetadataUserDetails user = null; Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null && auth.getPrincipal() != null) { if (auth.getPrincipal() instanceof MetadataUserDetails) { user = (MetadataUserDetails) auth.getPrincipal(); // if teacherId, userType, schoolType or districtId in param map // then add them to profile attribute List attribs = user.getAttributes(); int size = attribs.size(); if (size > 0) { Iterator i = attribs.iterator(); while (i.hasNext()) { //String attrib = (String) i.next(); ProfileAttributeImpl attrib = (ProfileAttributeImpl) i.next(); String attribName = attrib.getAttrName(); String attribValue = attrib.getAttrValue(); LOG.debug("Attrib: Name = " + attribName + ", Value = " + attribValue); } } } else { LOG.debug("The authentication object was not of the correct type: " + auth.getPrincipal().getClass().getName()); } } } How to call the report via a URLYou can construct a URL such as the one below: http://localhost:8080/jasperserver-pro/flow.html?_flowId=viewReportFlow&standAlone=true&the_city_distinct=Dallas&userType=teacher&_flowId=viewReportFlow&ParentFolderUri=%2Fpublic&reportUnit=%2Fpublic%2FsampleCDS_Report This URL is based on the Custom data source example that ships with the product: "the_city_distinct" is a value that gets fed into an input control. I changed the input control to "not mandatory" and the report executes without bringing up the pop-up window asking the user to populate a value for this parameter"userType" is a parameter key I used to simulate an attribute we wish to work withYou can also add j_username and j_password parameters to perform the authentication as well.
  8. This functionality is no longer applicable to our latest version. Please look at the actual documentation: JasperReports® Server Authentication Cookbook Follow the link here. IntroductionThis article gives a short explanation of how to integrate JAAS with JasperReports Server. JAAS stands for Java Authentication and Authorization Service, and is used to integrate with existing security systems. This article shows the process of how to configure it. ProcedureThese are the basic steps, and I cover them in greater detail in the following sections Create a login.conf file in the <TOMCAT_HOME>/webapps/jasperserver-pro/WEB-INF directory.Create the custom authentication software that integrates with the authentication serviceModify the applicationContext-security.xml Spring configuration file to register the custom authentication and authorization classCreate a login.conf fileThe login.conf file specifies the actual JAAS module to use for authentication by JasperReports Server. In the sample below, the dsJndiName is used by the Hyperic auth module, which is similar to the standard JDBC module except it support a few variations on encrypted passwords in the database: ARC { com.hyperic.arc.auth.ArcJdbcAuthModule required dsJndiName='jdbc/HypericDS'; }; Alternatively, here's another way to connect to the database: ENTRY { com.client.loginmodule.LoginModule required debug="true" log_level="ALL" logger_class="com.dbloginmodule.util.ScreenLoggerImpl" jdbcDriver="oracle.jdbc.driver.OracleDriver" jdbcUrl="jdbc:oracle:thin:@oraclehost:1521:dev" security_role="AuthenticatedUser"; }; The class defined above is the login module to the authentication system that JasperReports Server will be using. Make sure that this class, and any class it refererences, are packaged up in a JAR and deployed to /jasperserver-pro/WEB-INF/lib. Create the custom authentication softwareThe Authentication ProviderYou'll also need to create a custom piece of software, usually some Java classes (sometimes just one Java class), that does the authentication work. Your custom class will need to extend org.springframework.security.providers.jaas.JaasAuthenticationProvider, and will need to override the authenticate method. The Authority GranterYou'll also need to create a class that implements the org.springframework.security.providers.jaas.AuthorityGranter interface. The purpose of this class is that as an authority granter, it maps an authenticated user to a set of roles. The interface has only one method. Modify the Spring security fileYou'll now need to modify the applicationContext-security.xml Spring configuration file, which you can find in the /jasperserver-pro/WEB-INF directory. Find the entry below: <!--ref local="jaasAuthenticationProvider"/--> and uncomment it: <ref local="jaasAuthenticationProvider"/> (By default, JasperReports Server ships with this line commented out.) Next, find the bean reference for jaasAuthenticationProvider, which is by default commented out, and uncomment it out. Apply these changes: Change the class name for the bean so that it matches the custom authentication provider you built in the previous step.Change the loginContextName to the name in the login.conf file (in the examples above, the names are the ones in all capital letters.)Add any call-back handlers you may need to the list of call back handlersAdd the class you developed above to the list of authority granters.The bean definition may end up looking like this: <bean class="com.jaspersoft.ps.CustomAuthenticationProvider" id="jaasAuthenticationProvider"> <property name="loginConfig"> <value>/WEB-INF/login.conf</value> </property> <property name="loginContextName"> <value>CLEARVIEW</value> </property> <property name="callbackHandlers"> <list> <bean class="org.springframework.security.providers.jaas.JaasNameCallbackHandler" /> <bean class="org.springframework.security.providers.jaas.JaasPasswordCallbackHandler" /> <bean class="com.jaspersoft.ps.CustomCallBackHandler" /> </list> </property> <property name="authorityGranters"> <list> <bean class="com.jaspersoft.ps.client.security.AuthorityGranter"> <property name="baseRole"> <value>ROLE_USER</value> </property> </bean> </list> </property> </bean>
  9. IntroductionIn this article, we will take a look at adding a filter to the custom data source example that JasperReports Server ships with. Unlike database-based filters, in this example we have to read the values from the custom data source itself to build out the values for the input control. ProblemWe want to add an input control to the custom datasource example that JRS ships with, and we want the values for that input control to be those from the "city" field in the custom data source. Setting up the Custom Data Source exampleGo to where you installed JasperReports Server, find the samples directory, and browse down to the customDataSource subdirectory. For example, on my system that folder is C:Program Filesjasperreports-server-4.2samplescustomDataSource. Do these steps to deploy the example: Stop the JasperReports Server instance Edit the build.xml file in the customDataSource subdirectory, and modify the value of the webAppDir property to match where JasperReports Server is installed in the Tomcat directory From the command line, type this command: ant deploy Re-start the JasperReports Server instanceWhen you log in to JasperReports Server, you will see that you have more choices for data source types. Choose the type "My Custom Data Source", and fill out the form: Name: give it a name Resource ID: give it the same name as above Foo Foo: foo Bar Bar: bar Leave the "Save Location" in the repository's /public directorySubmit the datasource. At this point, you can go ahead and create the report: Choose a directory in the repository, and add a "JasperReport" resource there. You will need to name it, add a datasource (where you will reference the datasource you saved in the above step) Upload the JRXML file (that's the report template file) from the samplescustomDataSourcereportssimpleCDS.jrxml directory Save and run the reportWhen you run it, you should see a list of people's names grouped by the city in which they live. Steps to add a filter to the custom datasource-based reportWe'll need to add a parameter and a filter to the report so that when we implement the input control on the server, it will know to filter the results based on the parameter that will get filled in by the input control. In iReport, open up the simpleCDS.jrxml and do this: Add a parameter, call it "the_city_distinct", keep its type as java.lang.String Add a filter to the report. You do this by bringing up the report query editor, then clicking the "Filter expression..." button, and then adding the filter expression to filter on (for example), the city field: $F{the_city}.equals( $P{the_city_distinct} ) The expression above makes sure that "the_city" field matches the value of the "the_city_distinct" parameter Make sure to upload the report to the server when you're done.Build a custom query executerWe're going to have to build a custom query executer in order to get the input control properly working, since by default, we can only set up input controls that use SQL, HQL, or Domain query languages, all of which require a datasource with a REPORT_CONNECTION. Since the data is in the custom data source, which in our case is part of a custom data source class, we'll have to build our own custom query executer. There are three items that we need to build for the custom query executer: An Executer Factory A custom query executer A custom data sourceThe Executer Factory classThe Executer Factory class implements the net.sf.jasperreports.engine.query.JRQueryExecuterFactory interface, and for our example, the key method is the "createQueryExecuter" method. Its role is to create the custom query executer object. package example.cds;import java.util.Map;import net.sf.jasperreports.engine.JRDataset;import net.sf.jasperreports.engine.JRException;import net.sf.jasperreports.engine.JRValueParameter;import net.sf.jasperreports.engine.query.JRQueryExecuter;import net.sf.jasperreports.engine.query.JRQueryExecuterFactory;public class CDSExecuterFactory implements JRQueryExecuterFactory { @Override public JRQueryExecuter createQueryExecuter(JRDataset dataset, Map parameters) throws JRException { // TODO Auto-generated method stub return new CDSQueryExecuter(dataset, parameters); } @Override public Object getBuiltinParameters() { // TODO Auto-generated method stub return null; } @Override public boolean supportsQueryParameterType(String arg0) { // TODO Auto-generated method stub return false; } } [/code]The custom query executerThe custom query executer is in charge of creating the custom datasource object itself. In the example below, I'm doing a simple check, but I could just as easily call the custom datasource constructor without doing any checks. Similarly, we could also add more steps to the "createDatasource" method if we needed to extra processing. package example.cds;import java.util.Map;import net.sf.jasperreports.engine.JRDataSource; import net.sf.jasperreports.engine.JRDataset; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.query.JRAbstractQueryExecuter; public class CDSQueryExecuter extends JRAbstractQueryExecuter { public CDSQueryExecuter(JRDataset dataset, Map parameters) { super(dataset, parameters); parseQuery(); } @Override public boolean cancelQuery() throws JRException { // TODO Auto-generated method stub return false; } @Override public void close() { // TODO Auto-generated method stub } @Override public JRDataSource createDatasource() throws JRException { // TODO Auto-generated method stub if (getQueryString().equals("cities")) { return new CustomDataSource("the_city"); } else { return new CustomDataSource(); } } @Override protected String getParameterReplacement(String arg0) { // TODO Auto-generated method stub return null; } } [/code]The custom datasource classThe custom datasource class is based on the source file that ships with JasperReports Server, and you can find it under samplescustomDataSourcesrcexamplecdsCustomDataSource.java. I'm including only the bits of code that I modified to get my example working. I modified the constructor only to show that you can pull the user's profile attributes, if necessary, to do any extra processing that may be necessary to build the custom datasource. public CustomDataSource(){ MetadataUserDetails user = null; Authentication auth = SecurityContextHolder .getContext() .getAuthentication(); if (auth != null && auth.getPrincipal() != null) { if (auth.getPrincipal() instanceof MetadataUserDetails) { user = (MetadataUserDetails) auth.getPrincipal(); List attribs = user.getAttributes(); int size = attribs.size(); if (size > 0) { Iterator i = attribs.iterator(); while (i.hasNext()) { ProfileAttributeImpl attrib = (ProfileAttributeImpl) i.next(); String attribName = attrib.getAttrName(); String attribValue = attrib.getAttrValue(); } } } } } [/code]The getField method is where we process the individual fields for a given record, while the JasperReports library is iterating through the datasource. The extra bit I added is the last "if" statement, where I check for the existence of "the_city_distinct" as a field value request; this name has to match the parameter name we gave the report. public Object getFieldValue(JRField field) throws JRException { Object value = null; String fieldName = field.getName(); if ("the_city".equals(fieldName)) { value = data[index][0]; } else if ("id".equals(fieldName)) { value = data[index][1]; } else if ("name".equals(fieldName)) { value = data[index][2]; } else if ("street".equals(fieldName)) { value = data[index][3]; } else if ("the_city_distinct".equals(fieldName)) { value = data[index][0]; } return value; } [/code]Setup before building the input controlSince the goal is to create the input control so that it uses the custom data source, we're going to have to enable the custom query executer to appear in the list of possible query languages when creating an input control. This is what we have to do: Edit WEB-INF/flows/queryBeans.xmlFind the property whose name is queryLanguages. To the list of values, create a new list item and give it the label you want for the Districts custom data source. Something like CustomQuery Edit the WEB-INF/classes/jasperreports.properties fileAdd this line: net.sf.jasperreports.query.executer.factory.CustomQuery=fully.qualified.package.CustomQueryExecuterFactory Make sure the custom query executer factory is properly referenced, it needs the full package name and class name. Doing this provides the link between the UI and the matching query executer factory. Edit WEB-INF/bundles/jasperserver_messages.propertiesAdd this line: query.language.CustomQuery.label=Custom Query You can change the Custom Query value to whatever you want, it's what will appear in the UI when creating the input control. Creating the Input ControlHere are the steps for creating the input control. Log into JasperReports Server as an administrator, browse to the repository Create a new input control on a folder of your choice Type: Single-select query Prompt Text: a user-friendly value here will do On the "Locate Datatypes" screen choose: "Define a query in the next step." Hit Next. On the "Name the Query" screen: Enter in a name On the "Link to a Datasource" screen, choose: "Do not link to a data source." Hit Next. On the "Define the Query" screen, choose the values below, and then hit the "Save" button: Query Language: the language you set up from previous attempts Query String: you can leave it blank. On the "Provide parameters for the value column" screen: In the value column: enter in the name of the field you added in the JRXML In the visible columns, enter in the name of the same field you added in the value column. Make sure to click on the "Add" link afterwards. Since I've been using "the_city_distinct" all along, for the sake of this example, you may wish to populate both those fields with "the_city_distinct." Add the Input Control to the ReportNow you have to link the report to the input control you just created. You can do this by going to the repository, finding the report, and editing it. There's a button for "Controls and Resources"; once there, you'll add the input control you just created by locating it in the repository. Process for deploying to a test systemOnce you have built and compiled the custom code, you'll need to deploy it to a test server environment. I suggest doing it like this: Package up the classes you've developed into a separate JAR file With the server shut down, copy it to /jasperserver-pro/WEB-INF/lib Re-start the server, test
  10. IntroductionAt the browser level, you can do a lot of testing on both performance and checking how elements are displayed. Jaspersoft recommends a Firefox plug-in named Firebug. This is an excellent tool only available for Firefox but Chrome, IE7 and IE8 have similar tools. You can use Firebug to troubleshoot performance issues, such as executing reports, dashboards, or any other page in the JasperReports Server application. That way, you can tell whether slow performance is due to slow server responses, or slow browser processing. For example, some JavaScript-heavy pages can appear to take a long time to load, and the root cause may be that the JavaScript (or some other browser-side component) is slowing down the browser from showing the page to the end user. OverviewWhen Firefox is installed and opened, Firebug will appear at the bottom right of the browser, right below your page. Firebug ships with several tools: Console which allows to test some jasvascrit on the page. This a very powerful and the simplest way to select an element on the page view its properties and check the behavior when you change something. HTML allows to view the source code of the HTML page CSS gives you access to all the Stylesheet files used by the page displayed and allow to modify it Script gives access to all the javascript from the page and eventually makes possible to change it DOM is focusing on objects and properties of each of them Net gives all the information the user need regarding the order elements are downloaded and rendered and how long it took for it.On each of those 6 sections, a search filter makes it easy to go directly to what you're looking for. PerformanceThe most useful tool performance-wise is Net. If you activate it before rendering a page, let say one of your reports, then on opening your report, 'Net' will display in detail every single event which happens to open the page and the timing for each element. You can thus very easily and very quickly identify what is slowing down the report rendering. And if the page renders very quickly but it takes a long time to be able to view the report, then you can conclude that you need to investigate server side issues. Net also provides more information on each call. An important point when troubleshooting performance is the size of each element. Net gives you all that for each element. A large element will naturally take more time to load, so this might be another clue to improve your performance. User InterfaceFirebug is a great tool for performance but it also provides a multitude of functionality to customize the UI in any aspect, though you will need some knowledge about CSS and HTML to use all its power. Either by right clicking directly on an element on the page (report) or by clicking on the "Inspect Element" button, you can select an element (a title, a picture, a chart, an element in the table) and it will automatically highlight the HTML code in the Firebug section at the bottom of the page. Then for that element all aspects of the appearance will be displayed on the right panel of firebug either as CSS in the styles tab, or as the result of that CSS in computed tab, or in a more graphical way especially for margins and padding in Layout tab. Changing anything in the style, the padding, the alignment will show in real time how it will be displayed on the page. If your satisfied with result, you can then copy all the modifications you've made right into your theme. See our User Guides for more information on themes in JasperReports Server 4.0 and greater. ResourcesTo learn more about Firebug, have a look at these online tutorials: http://www.evotech.net/blog/2007/06/introduction-to-firebug/ http://www.w3resource.com/web-development-tools/firebug-tutorials.phpor watch those videos: (including a demo showing how to check performance) (focused on page style and appearance and also performance) http://www.youtube.com/watch?v=2xxfvuZFHsM (JavaScript debugging)
  11. IntroductionJConsole allows you to monitor a Java Virtual Machine and the applications that run on it. A slightly improved tool similar to JConsole is JVisualVM and they're setup is the same. By enabling JMX (Java Management Extensions) settings on the JVM at startup, JConsole can then monitor JVM performance and how much resources its applications are consuming. JConsole and JVisualVM ship with the JDK, and is thus an ideal starting point for monitoring server activity. JConsole is useful for: Monitoring memory usage (heap, non-heap, and native)This would tell you how close you are to using the maximum memory you defined for the heap. Looking at its values also gives you sense of how much garbage collection is going on. If the application server is getting close to the maximum Java heap size, it might result in excess garbage collection and slowness. Monitoring the number of threads in useYou might take a look at these values when testing concurrency issues, or to check individual stack traces. Monitoring the CPU usage for the JVMThis might help you determine whether or not it's the JVM that's responsible for the slowness, or whether it's some other element in the software stack (slow database, slow network connections, slow web server, etc). OverviewThis article assumes you are using Tomcat; the equivalent settings for other application servers (such as WebSphere and WebLogic) are left for the reader to verify. I worked out this article's example using a Ubuntu virtual environment; there is nothing to stop you from adapting this example to a Windows environment. Modifying the Tomcat startup scriptThe first step is to modify the Tomcat startup script to include JMX settings; this will allow the JConsole application to connect to the JMX port and thus monitor the server's activity. The first step is to modify the setenv.sh script, which controls the Tomcat startup settings. Add the CATALINA_OPTS variable: export CATALINA_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8999 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=<IP Address>" These options turn on JMX functionality, activate port 8999 for JMX monitoring, and set the hostname to the IP address of the machine that's hosting JRS. After you have modified the script, you can go ahead and start up the Tomcat server. Starting up JConsoleFrom the command line, and as long as the JDK bin directory is in your path, go ahead and simply type "jconsole" into the command line. You should get something like this at start up: Select the "Remote Process" option and fill in the data with the server name (or IP) Once you connect, and you are on the "Overview" tab, you'll get a summary view of the JVM state: Observe the behavior before and after a test condition you're interested in. If you know that performing a certain task results in slower performance, do a before-and-after comparison to quantify that observation. Using the tool, you'll be able to compare the values for memory usage, thread count, class loading, and CPU usage. For example, you might test: Ad Hoc Reports - before running them, while you're running them, after they've run If you see normal values and are still experiencing slow performance (for example, low CPU% but the end user experiences a slow system anyway), then at least you will have eliminated the JVM as the source of your performance issues. Analyze the chart dataBesides just presenting the data graphically, you can download the chart data in CSV format. By simply right-clicking on a chart image, JConsole will give you the opportunity to save the chart data to your disk. This allows to analyze the data with another tool, such as Excel. For example, the data in the Memory tab: can be turned into this: Resourceshttp://download.oracle.com/javase/6/docs/technotes/guides/management/jconsole.html - This is the official JConsole user guide from Oracle
  12. Introduction This article is about using JMeter to perform some basic performance and concurrency testing on JasperReports Server. In this article, we will tackle some different scenarios: Executing canned reports Creating Ad Hoc Reports Simulating Concurrency See also these screencasts for reference: The numbers that the JMeter application generates could be used for benchmarking performance for different environments, basic load testing, and measuring gains after tuning. The numbers that JMeter produces are best used and interpreted when we can compare them to another set of results, with each set of results representing a different set of conditions (e.g., different memory settings, different network throughput, different number of concurrent users, etc.) The numbers will also vary from customer to customer, mainly because a customer's requirements vary; report size, query complexity, network latency, software versions and configuration as well as concurrency requirements all vary from customer to customer. When writing this article, I used the foodmart sample database that ships with JasperReports Server, since it gives any customer can use it if they've installed it. For systems with larger databases, the process would be the same. Downloading and Installing JMeter JMeter is a desktop Java application, and you can download it from http://jakarta.apache.org/jmeter. The instructions below deal with JMeter version 2.4. Ideally you would install JMeter on a development and testing machine, with that machine being able to access the instance of JasperReports Server you want to test. Configuring JMeter With the JMeter proxy in place, you can create a test plan by simply visiting the web site you wish you test. Clicking around the website will add entries to your test plan. This is what it looks like when you first kick it off: Create the Thread Group Start JMeter Select "Test Plan" on the tree Right click on the "Test Plan" and add a new thread group: Add -> Threads -> Thread Group Select the Thread Group At this stage, you can also define the number of threads to run when executing the test plan, which will mimic the number of concurrent users. I suggest using 50 as the initial value. Right click and choose "Add -> Config Element -> HTTP Request Defaults" Server Name or IP: Fill it in with the environment you're going to test. This can be tricky if you're testing it on your own environment (localhost); I got a local test plan working by using a Ubuntu virtual environment running on my PC. JMeter doesn't seem to like recording steps that go through localhost. Port Number: the port number of the JRS instance you're testing; usually 8080 in default JRS installations There are boxes at the bottom that you can fill out to configure a proxy server, if necessary Configure the JMeter Proxy We're now going to create the Proxy Server using JMeter. On the JMeter application, select workbench, right-click on it, and choose "Add -> Non-test Elements -> HTTP Proxy Server". We will need to modify some of its values: Port: Change it from 8080 to some other port that's not in use Target Controller: Choose "Test Plan -> Thread Group". Click the "Add" button in "Patterns to Include". Since we want to get an idea of overall server performance, add ".*" (minus the double-quotes) to the entry. That pattern means that it will consider all file requests coming from the server. You can limit it by specifying, for instance, only HTML files, only GIF files, etc, by defining the URL pattern that matches the file you want. Click the "Start" button - this kicks off the proxy Note: When defining the proxy server, take note that after you save your test plan, and then close down JMeter, it will often appear that you have lost your proxy server configuration; my recommendation is to do the steps outlined in this document all in one go; that way you will have recorded your test plan, which is the main point of using the proxy server and JMeter, all in one go. Configure your browser to go through the proxy In this step, we configure the browser to make its HTTP requests go through the proxy server we just set up on JMeter. Once we have configured the browser in this way, all the HTTP requests the browser makes will go through the JMeter proxy server, which will in turn record these steps as part of the test plan we want to create. You can do this with Internet Explorer, Firefox or Chrome; they each have settings where we can define the proxy server. The Firefox and Internet Explorer settings go through the same process; the Chrome settings are slightly different, but achieve the same goal. As a developer, since you're just running the JMeter application in your own test environment, it's fine to just set the proxy server to "localhost", since you are running JMeter locally anyway. Firefox Internet Explorer Chrome 1. Tools -> Options 2. Click on the "Advanced" button 3. Choose the Network Tab 4. Click on the "Settings" button for configuring how Firefox connects to the Internet. 5. Choose the "Manual Proxy Configuration" radio button 6. Set the proxy hostname and port number 1. Tools -> Internet Options 2. Choose the "Connections" tab 3. Click on the "LAN settings" button 4. Click on the "Use a proxy server for your LAN" checkbox 5. Set the proxy hostname and port number 1. Type "chrome://settings/" into your browser window 2. Click on the "Under the Hood" option 3. Click on the "Change proxy settings..." button 4. Click on the "LAN settings" button 5. Click on the "Use a proxy server for your LAN" checkbo Create the Test Plan Using the Browser The goal of the test plan will be to go through JasperReports Server UI, login, execute a series of reports, and create a sample ad hoc report. We will then execute the test plan. We can imagine a test plan covering at least these cases: ! Low Thread Count High Thread Count Canned Report - Low volume | Canned Report - Large volume | Ad Hoc Report - Low Volume | Ad Hoc Report - High Volume | Ad Hoc Report - Topic | Ad Hoc Report: Domain and Crosstab | Now that you have the proxy server set up and running, and you have configured your browser to pass all the HTTP requests via the JMeter proxy server, you can now record your test plan. The general procedure is this: Use your browser to login to JasperReports Server Perform the functionality that you wish to load test Quit your browser when done When you switch back to the JMeter application, you will find that JMeter has recorded the HTTP requests you executed in your browser: The list of items on the left-hand side show all the HTTP requests that your browser made when interacting with JasperReports Server. Turning on the Reporting Function Now that we have the test plan defined, we'll switch on the reporting function, so that once we execute the test plan, we'll be able to see how well the application performed. On the JMeter application, select the "Thread Group" Right-click on the "Thread Group", and choose "Add -> Listener -> Aggregate Report" With the "Thread Group" still selected, choose the number of threads, which represent the number of concurrent users. Run the Test Plan Now you are ready to kick off the test plan. You do this by choosing "Run -> Start" from the JMeter top-level menu. While the test plan is running, you will see that the aggregate report is being filled in at the same time, and a little green box on the top-right corner of the JMeter application appears, indicating that it is currently running. Note that there is a "Save Table Data" button - this will allow you to save the data the test plan generated, which you can then use to compare with different test scenarios. Analyze JMeter logs Use the aggregate report to test different environments, and not just the thread count. The environment includes elements like: JVM settings Application Server Thread Count Database Driver versions used Network Latency - throughput between different elements in the software stack Version of JasperReports Server that is used Any element that is a candidate for tuning can be added to that list. The idea is to be able to compare JMeter aggregate reports for environments that use the different settings, and thus determine what impact each of the elements we have tuned have had on the overall aggregate performance: did it improve performance or not? Other Performance Tips: Measure and Test Test DB Queries with DBTester Measure Ad Hoc Performance Ad Hoc Set query limits on the "Ad Hoc Options" page Modify the Ad Hoc caching values if your data does not update constantly Tomcat Compress HTTP responses at the Tomcat level Increase the maximum thread count at the Tomcat level Set up a cache filter for Tomcat or JBoss Disable automatic deployment on production servers using Tomcat Database Index columns in the database to speed up domain-based reports Set the fetch size in the jasperreports.properties file to modify the number of rows to fetch JasperReports Configuration Items Modify Virtualizer Settings for Very Large Reports Modify the jasperreports.properties file to disable multi-line data processing Back to JasperReports Server Performance Tips and Tricks Resources This article was based in no small part on the document below, which outlines the step-by-step procedure we discussed in this article. http://jakarta.apache.org/jmeter/usermanual/jmeter_proxy_step_by_step.pdf
  13. IntroductionThis is an article based on work one of our Professional Services consultants recently completed. In this article, he discusses how to create custom components in iReport, and enabling their use in iReport's palette. This article is meant for developers who are interested in developing a custom component for the iReport tool. The article uses the NetBeans IDE to develop the example. It contains code samples in Java, and makes reference to configuration files. You can find the Word version of this article here: CustomComponent_-_Reserved_for_Technical_Training-BI.zip. Problem to SolveA client requests to add a new item into the Palette window of iReport in order to easily let a report designer insert a QRCode, which is a type of barcode, into their report. The client is using Jasperserver 3.7 and iReport 3.7.6. The client also expects to be able to encode QR Code using different character set. JasperReports API VersionLet's take some time to summarize which environment is the best accordingly to your needs. Depending on the environment you are aiming to deploy your component for, it is important to develop against the right version of JasperReports API. For example, if you plan to deploy a report which will contain your component into JasperServer 3.7, you must use JasperReports v3.7.1.1 library since Jasperserver 3.7 support only JR v3.7.1.1 and earlier. But if the report will be produce only on iReport v3.7.6 and will never be deployed on Jasperser then you can use JR 3.7.6. As shown in the table below it is important to choose the right version of JasperReports library to make sure your custom component will be compliant with the desired environment. JasperReports API VersioniReport Designer v3.7.1.1iReport Designer v3.7.6iReport Designer v4.0JasperReports Server v3.7JasperReports Server v4.03.7.1.1XXXXX3.7.6 XX X4.0 X XQR CodeHere is the QR Code definition according to Wikipedia: A QR Code is a specific matrix barcode (or two-dimensional code), readable by dedicated QR Barcode reader and camera phones. The code consists of black modules arranged in a square pattern on a white background. The information encoded can be text, URL or other data. iReportiReport is a Java-based program that helps users and developers that use the JasperReports library to visually design reports. Through a rich and very simple to use GUI, it provides all the most important functions to rapidly create nice but very complex reports, saving a lot of time. Palette WindowMost of the items that can be integrated into a report are displayed in the palette window. To open this window, shown in Figure 2, select Window -> Palette (Ctrl + Shift + 8). Implementation stepsTo implement this requirement we need to: Create the custom component that will provide the QR Code functionalityCreate the Plugin to integrate the custom component into iReport.It is highly recommended to use NetBeans IDE when creating plugin since it provides the developer with all the required plug-in development facilities. Based on this recommendation, this tutorial is using NetBeans technology. Creation of the custom componentSince the client is looking for a solution compliant with iReport version 3.7.6, the JasperReports 3.7.6 library will be used. The following section defines the custom component, as described in JasperReports Ultimate Guide. DefinitionA custom component is a feature that allows users to extend the functionality of the JasperReports engine by plugging-in custom-made visual components that take data as input and produce content that is embedded into the resulting documents. Custom components can be anything from simple barcodes to tables, and professional charting packages. The custom component support relies on two other major features of the JasperReports library: extension support and generic elements. Both are described in separate sections of JasperReports ultimate Guide and you need to be familiar with them before delving into custom components. A custom component is a special report element that has a defined structure and produces some output in generated reports, just like usual report elements do. Just like a normal chart element in a report template will produce an image element in the generated document, or a crosstab element present in the report template will produce a series of frames and text fields that makes up a tabular structure in the generated documents, generic custom components declared in a report template could generate any combination of simple elements and generic elements as the output of their built-in calculation and layout logic. Custom components come in bundles. A component bundle is a package comprising of one or several components that share the same XML namespace and schema. For example, a chart component bundle can contain multiple chart components, one for each type of chart (pie chart component, bar chart component and so on). Now that we all understand what a custom component is, let's begin our implementation. Implementation overviewWe can define four (4) milestones that need to be taken care of to implement a custom component. Define the interface of the custom componentTransformation XML <- -> Java ObjectImplementation of the report lifecycleCompilationFillingGlue all the pieces togetherThe following section will detail those steps using the QR Code sample. InterfaceWe need to design the XML schema use for the custom component element. The schema defines the target namespace and the definition of the QR Code. It can define as many elements as you want, for the sake of the demonstration only one element is define. The figure below represents the file component.xsd that defines the schema of the QR Code element. Let's describe the content of the file. The target namespace. Must be unique per schema.Indicate the usage of "jr" prefix to define http://jasperreports.sourceforge.net/jasperreports namespaceName of our QR Code elementIndicate that element can substitute a "jr:component"Indicate that element extends "jr:componentType"Indicate the use of "code expression" that will provide the text that need to be encoded as a QR CodeList of attributes for our element Once the component interface has been defined and the XML schema implemented, we need a Java object model that mirrors the schema. This model is composed of an Interface called: QRCodeComponent that extends net.sf.jasperreports.engine.component.Component interface and contains the fields that match the QRCode XML element properties. A default implementation is provided with StandardQRCodeComponent: public class StandardQRCodeComponent implements QRCodeComponent, Serializable, JRChangeEventsSupport{ private String charSet; private String errorCorrection; private JRExpression codeExpression; private EvaluationTimeEnum evaluationTimeValue = EvaluationTimeEnum.NOW; private String evaluationGroup; ….}[/code]TransformationWe need to take care of the transformations of XML fragments to Java Objects and back. In other words, we need to implement the code that will generate the XML into the "jrxml" file while creating a report and digest the XML at runtime into our Java object (QRCodeComponent). Parsing an XML fragment into a corresponding Java object is done by writing a class that defines Commons Digester rules that apply to the QRCode XML element. We use the com.jaspersoft.ps.customcomponent.ComponentsXmlHandler class for this operation, which implements XmlDigesterConfigurer and ComponentXmlWriter. "ƒ Java Object -> XMLProducing XML fragments from a QRCodeComponent object is the task of the writeToXml method of the same class. The method receives a component instance and an XML report writer and outputs to the writer the XML fragment that corresponds to the component. public void writeToXml( ComponentKey componentKey, Component component, JRXmlWriter reportWriter) throws IOException{ if (component instanceof QRCodeComponent) { QRCodeComponent barcode = (QRCodeComponent) component; writeQRCode(barcode, componentKey, reportWriter); }} protected void writeQRCode( QRCodeComponent barcode, ComponentKey componentKey, JRXmlWriter reportWriter) throws IOException{ log.debug("writeQRXml"); JRXmlWriteHelper writer = reportWriter.getXmlWriteHelper(); XmlNamespace namespace = new XmlNamespace( ComponentsExtensionsRegistryFactory.NAMESPACE, componentKey.getNamespacePrefix(), ComponentsExtensionsRegistryFactory.XSD_LOCATION ); writer.startElement("qrcode", namespace); writer.addAttribute("charSet", barcode.getCharSet()); writer.addAttribute("errorCorrection", barcode.getErrorCorrection()); if (barcode.getEvaluationTimeValue() != EvaluationTimeEnum.NOW) { writer.addAttribute(JRXmlConstants.ATTRIBUTE_evaluationTime, barcode.getEvaluationTimeValue()); } writer.addAttribute(JRXmlConstants.ATTRIBUTE_evaluationGroup, barcode.getEvaluationGroup()); writer.writeExpression("codeExpression", barcode.getCodeExpression(), false); writer.closeElement();}[/code]XML -> Java ObjectDigesting the XML schema into a Java object is performed into configureDigester method. The class registers rules as the following: public void configureDigester(Digester digester){ log.debug("Rules configuration"); String barcodePattern = "*/componentElement/qrcode"; digester.addObjectCreate( barcodePattern, StandardQRCodeComponent.class ); digester.addSetProperties( barcodePattern, //properties to be ignored by this rule new String[]{ JRXmlConstants.ATTRIBUTE_evaluationTime }, new String[0] ); digester.addRule( barcodePattern, new XmlConstantPropertyRule( JRXmlConstants.ATTRIBUTE_evaluationTime, "evaluationTimeValue", EvaluationTimeEnum.values() ) ); String barcodeExpressionPattern = barcodePattern + "/codeExpression"; digester.addFactoryCreate( barcodeExpressionPattern, JRExpressionFactory.StringExpressionFactory.class.getName() ); digester.addCallMethod( barcodeExpressionPattern, "setText", 0 ); digester.addSetNext( barcodeExpressionPattern, "setCodeExpression", JRExpression.class.getName() );}[/code]Report life cycleCompilationWe will now write the code that manages qrcode components during report compilation. At report compilation time, custom components need to be validated, expressions need to be collected from them and component instances have to be generated for inclusion in compiled reports. The QRCodeCompiler class contains three methods that take care of these tasks: The verify method checks that the QRCode expression and type have been specified (and logs broken rule errors if the component is not valid)public void verify(Component component, JRVerifier verifier){ QRCodeComponent barcode = (QRCodeComponent) component; JRExpression codeExpression = barcode.getCodeExpression(); if (codeExpression == null) { verifier.addBrokenRule("QR Code expression is null", barcode); } else { String valueClass = codeExpression.getValueClassName(); if (valueClass == null) { verifier.addBrokenRule("QR Code expression value class not set", codeExpression); } else if (!"java.lang.String".equals(valueClass)) { verifier.addBrokenRule( "Class " + valueClass + " not supported for barcode expression. Use java.lang.String instead.", codeExpression ); } } EvaluationTimeEnum evaluationTime = barcode.getEvaluationTimeValue(); if (evaluationTime == EvaluationTimeEnum.AUTO) { verifier.addBrokenRule("Auto evaluation time is not supported for barcodes", barcode); } else if (evaluationTime == EvaluationTimeEnum.GROUP) { String evaluationGroup = barcode.getEvaluationGroup(); if (evaluationGroup == null || evaluationGroup.length() == 0) { verifier.addBrokenRule("No evaluation group set for barcode", barcode); } else if (!verifier.getReportDesign().getGroupsMap().containsKey(evaluationGroup)) { verifier.addBrokenRule("Barcode evalution group "" + evaluationGroup + " not found" ,barcode); } }}[/code]The collectExpressions method collects the expression embedded into the componentpublic void collectExpressions(Component component, JRExpressionCollector collector){ QRCodeComponent barcode = (QRCodeComponent) component; collector.addExpression(barcode.getCodeExpression());}[/code]The toCompiledComponent method creates a barcode object which includes the compiled expression instance obtained from net.sf.jasperreports.engine.base.JRBaseObjectFactory (which is an object factory that produces object instances for compiled reports).public Component toCompiledComponent(Component component, JRBaseObjectFactory baseFactory){ QRCodeComponent barcode = (QRCodeComponent) component; StandardQRCodeComponent compiledBarcode = new StandardQRCodeComponent(barcode, baseFactory); return compiledBarcode;}[/code]As report compilation has been covered, we go on to the next phase in a report's life cycle, namely report fill. FillingAt filling time, we need to get a QRCode component instance as designed in a report template, collect dynamic data from the report data set and produce an element that will get included in the generated report. This is the job of a class that implements the net.sf.jasperreports.engine.component.FillComponent interface, and we will name our implementation QRCodeFillComponent. Fill component instances are created by a factory class called QRCodeFillFactory. Since our project contains only one component, the Factory will always return an instance of QRCodeFillComponent. The QRCodeFillComponent class extends the abstract net.sf.jasperreports.engine.component.BaseFillComponent class that provides basic functionality for component fill implementations. The class includes methods that are called by the engine at different stages during the fill process. The evaluate method evaluates the qrcode expression and stores the result in a class member (code) which is going to be used later in the process: public void evaluate(byte evaluation) throws JRException{ code = (String) fillContext.evaluate( barcodeComponent.getCodeExpression(), evaluation );}[/code]The prepare method allows the fill component to decide whether it will produce an output element, and whether it needs to stretch the space assigned at design time for the element in order to fit the generated output. The fill barcode implementation checks whether the barcode expression has yielded a non-null result and specifies in this case that it will produce a print element that does not stretch; otherwise it specifies that it will not produce an output element: public FillPrepareResult prepare(int availableHeight){ return code == null ? FillPrepareResult.NO_PRINT_NO_OVERFLOW : FillPrepareResult.PRINT_NO_STRETCH ;}[/code]Before implementing the fill method which will actually produce the report element that will get included in the filled report, we get to the point where we need to come up with what we want to include in the generated report. QRCodes can be rendered as images, so writing an image renderer that knows how to draw a QRCode would do the job. Thus we write the QRcodeRenderer class which is a SVG renderer that uses the ZXing API to draw a barcode. Now that we have the QRCode image renderer, we can write the fill method of QRCodeFillComponent which will create images that use this renderer. The method uses the evaluated QRCode expression and the QRCode component attributes to create a barcode renderer: public JRPrintElement fill(){ JRTemplateImage templateImage = getTemplateImage(); JRTemplatePrintImage image = new JRTemplatePrintImage(templateImage); JRComponentElement element = fillContext.getComponentElement(); image.setX(element.getX()); image.setY(fillContext.getElementPrintY()); image.setWidth(element.getWidth()); image.setHeight(element.getHeight()); QRCodeInfo barcodeInfo = new QRCodeInfo(); barcodeInfo.setCode(code); barcodeInfo.setCharSet(barcodeComponent.getCharSet()); barcodeInfo.setErrorCorrection(barcodeComponent.getErrorCorrection()); QRCodeRenderer renderer = new QRCodeRenderer(barcodeInfo); image.setRenderer(renderer); return image; }[/code]Glue the pieces togetherWe have all the code required to handle QRCode components and we need to put it all together into a component package. Components are registered as JasperReports extensions, and we will use a hard coded extension factory for our component. We will write a class that implements net.sf.jasperreports.extensions.ExtensionsRegistryFactory and returns our component implementation as extension. In our implementation, this class is com.jaspersoft.ps.customcomponent.qrcode.ComponentsExtensionsRegistryFactory. The class creates a component manager object which glues handler classes used for report compilation and filling. Component transformations between JRXML and object model are the responsibility of an XML handler object, which also specifies the XML namespace and schema for the component. To register the component extension, we will need to write a jasperreports_extension.properties file and put it as a resource at the root of our class folder. The file contains a property that registers our extension factory: net.sf.jasperreports.extension.registry.factory.components = com.jaspersoft.ps.customcomponent.qrcode.ComponentsExtensionsRegistryFactory[/code]TestingTo test our implementation we need to create a sample report that contains our QRCode component. <componentElement> <reportElement x="17" y="14" width="50" height="50"/> <jr:qrcode xmlns:jr="ttp://jasperreports.sourceforge.net/jasperreports/components/ps" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports/components/ps components.xsd"> <jr:codeExpression> "JasperSoft" </jr:codeExpression> </jr:qrcode></componentElement>[/code]The QRCode component is wrapped in a componentElement report element that also includes common element attributes such as position and size. The component itself uses the namespace we have chosen for our implementation, and specifies the barcode type and code expression. To wrap everything up, the classes need to be compiled and packaged into a JAR file together with the rest of the resources used by the code. Most of the time, if not every time, we would like to use this custom component inside iReport to let the report designer add this component into their design. The following section describes how to create the plugin and deploy it under iReport. Creation of the plugin in NetBeansPre-requisiteIn order to create a plugin that will be added into iReport to use our component we need the following: NetBeans 6.5.1 IDE or later (NetBeans 6.9.1 IDE is used for this tutorial) http://netbeans.org/iReport v.3.7.1.1 or later (iReport 3.7.6 is used for this tutorial) iReport Designer Project PageNetBeans ProjectAdd iReport NetBeans platform to the environment Tools -> NetBeans Platform -> Add Platform... Choose the iReport platform you would like to develop your plugin on. (Root folder of iReport installation. Platform Name should be filled automatically)Click NextRename the platform if needed, then Finish. (In this tutorial, the platform name is: Jaspersoft iReport Designer 3.7.6)Create a New Project File -> New Project... -> NetBeans Modules -> Module Click NextEnter the Project Name, Project Locationselect the "Standalone Platform" radio buttonChoose Jaspersoft iReport Designer v3.7.6 from the dropdown list, click Next Enter the Code Name Base, the Module Display NameCheck "Generate XML Layer", then Finish The figure below displays the tree created at the end of the wizard. Run your project (with the Rub Main Project button on the tool bar). A special version of iReport will start. Even if our plug-in does nothing yet, you can see the it in the list of installed plugin in the plugin manager (Tools -> Plugins). Close iReport and come back to the IDE. It's time to write some code. Implementation of the pluginClose iReport and come back to the IDE. It's time to write some code. As a reminder, our client wants to add the component into the Palette window as shown in the figure below. IRPITEM description fileThe first thing we should do is create an irpitem file. Irpitem stands for iReport Palette item. This file is only needed when you desire to add your component into the Palette window. id=QRCodeItemIdname=qrcode.nametooltip=qrcode.tooltipicon16=com/jaspersoft/ps/component/qrcode/qr_code_16x16.pngicon32=com/jaspersoft/ps/component/qrcode/qr_code_32x32.pngaction=com.jaspersoft.ps.component.qrcode.QRCodeAction[/code]"qrcode.name" and "qrcode.tooltip" can be translated in a resource Bundle added to the global resource bundle of iReport, in this case an installer class for your module should do something like: I18n.addBundleLocation(ResourceBundle.getBundle("/com/jaspersoft/ps/component/qrcode"));[/code]To create a module installer class, use the NetBEans wizard. Layer.xmlTo add the palette item in the iReport palette we have to modify the layer.xml file (an important file of your NetBeans plugin). You need to add the following chunk of xml to your layer.xml: <folder name="palette"> <folder name="ReportElements"> <attr name="SystemFileSystem.localizingBundle" stringvalue="com.jaspersoft.ireport.locale.Bundle" /> <file name="QRCodeItem.irpitem" url="QRCode.irpitem" /> </folder></folder>[/code]This will add your item inside the ReportElements section of the palette. You can even create your own palette section, or use the Tools section. The Big PictureNow that our item appears in the Report Elements of the palette window, let's have a look on what happen under the hood when we drag'n'drop this item inside any band of the report. Create a JRDesignElement that include our Custom Component.Create a JRDesignElementWidget object that will manage all events that may occur on our JRDesignElement object.Create the ElementNode object including its propertiesQRCodeActionAs specified in our irpitem file ("action" property in Figure 21) we have to implement our extension of com.jaspersoft.ireport.designer.palette.PaletteItemAction or, as we did in this sample, it's more specific implementation com.jaspersoft.ireport.designer.palette.actions.CreateReportElementAction. com.jaspersoft.ps.component.qrcode.QRCodeAction overrides "createReportElement" to build our own JRDesignElement and link it to our Custom component. The following listing represents the code from QCAction that create a JRDesignComponentElement, set default attributes and link to our custom component. public JRDesignElement createReportElement(JasperDesign jd) { JRDesignComponentElement component = new JRDesignComponentElement(); component.setWidth(50); component.setHeight(50); StandardQRCodeComponent componentImpl = new StandardQRCodeComponent(); componentImpl.setEvaluationTimeValue(EvaluationTimeEnum.NOW); JRDesignExpression exp = new JRDesignExpression(); exp.setValueClassName("java.lang.String"); componentImpl.setCodeExpression(exp); component.setComponent(componentImpl); component.setComponentKey( new ComponentKey( "http://jasperreports.sourceforge.net/jasperreports/components/ps", "jr", "qrcode" ) ); return component;}[/code]QRCodeElementNodeFactoryThe responsibility of this factory is to create a JRDesignElementWidget and an ElementNode object based accordingly to the component. In this tutorial, the factory only needs to manage one component, QRCodeComponent. The JRDesignElementWidget object will be injected into our QRCodeComponent as a PropertyChangeListener and will manage all the events that may occurred on the item's properties inside iReport. Pay attention to the statement beginning with c.getEventSupport() in the code sample below. For example, when we increase the QRCode item in the report, the engine will check if a JRDesignElementWidget has been injected into the component and then call its propertyChange method. public class JRQRCodeComponentWidget extends JRDesignElementWidget { public JRQRCodeComponentWidget( AbstractReportObjectScene scene, JRDesignElement element ) { super(scene, element); if (((JRDesignComponentElement)element).getComponent() instanceof StandardQRCodeComponent) { StandardQRCodeComponent c = ( StandardQRCodeComponent ) ( ( JRDesignComponentElement)element ).getComponent(); c.getEventSupport().addPropertyChangeListener(this); } } @Override public void propertyChange(PropertyChangeEvent evt) { if ( evt.getPropertyName().equals(StandardQRCodeComponent.PROPERTY_CHAR_SET) || evt.getPropertyName().equals(StandardQRCodeComponent.PROPERTY_ERROR_CORRECTION) || evt.getPropertyName().equals(StandardQRCodeComponent.PROPERTY_CODE_EXPRESSION) ) { updateBounds(); this.repaint(); this.revalidate(true); this.getSelectionWidget().updateBounds(); this.getSelectionWidget().revalidate(true); getScene().validate(); } super.propertyChange(evt); }}[/code]The ElementNode is as its name says an object that can be added into a band in iReport. As you can see on Figure 23, the QR Code is an element node contained inside "Detail 1" band (node). At the code level, this Element Node is the wrapper of our component and its properties. Each time our QR Code item is selected, either from the tree or inside the design section of the report, jasperreports engine calls the method createSheet. This method define which properties should be included into the properties window. Refer to the next section titled Component's properties. protected Sheet createSheet() { Sheet sheet = super.createSheet(); // adding common properties... Sheet.Set set = Sheet.createPropertiesSet(); set.setName("QRCode"); StandardQRCodeComponent component = (StandardQRCodeComponent)( (JRDesignComponentElement)getElement()).getComponent(); set.setDisplayName(I18n.getString("qrcode")); JRDesignDataset dataset = ModelUtils.getElementDataset(getElement(), getJasperDesign()); set.put(new QRCodeCodeExpressionProperty(component, dataset)); set.put(new QRCodeEvaluationTimeProperty(component, dataset));//, dataset)); set.put(new QRCodeEvaluationGroupProperty(component, dataset)); set.put(new QRCodeCharSetProperty(component) ); set.put(new QRCodeErrorCorrectionProperty(component) ); ModelUtils.getElementDataset(getElement(), getJasperDesign()))); sheet.put( set); return sheet;}[/code]Component's propertiesThe component's properties are the input parameters (optional or mandatory) that need to be passed to the custom component. Each properties must extend org.openide.nodes.Node.Property or one of its child classes such as StringProperty, ExpressionProperty, PropertySupport or else. As shown in the fiure below, the component supports five (5) specific properties: Code Expression Which is the String that will be encoded as a QR CodeEvaluation Time and Evaluation Group Defines the time at which the component has to be processed. The evaluation of an expression can be done when the report engine "encounters" the element during the creation of the report (evaluation time "now") or it can be postponed.Character Set Charset used to encode the code expressionError Correction For more detail, follow this link: http://en.wikipedia.org/wiki/QR_Code#Storage Let's focus on the Character set property. A class named: QRCodeCharSetProperty extends com.jaspersoft.ireport.designer.sheet.properties.StringProperty. The implementation provides the information on the property such as the name and the description (as seen at the bottom of Figure 24), validates the input value (must be a valid Character Set) and store this value for future use. DeploymentWe are now ready to deploy our hard work to iReport. Create the NBM filePre-requisiteNote that iReport 3.7.6 has been developed on NetBeans 6.5.1. So if you are using NetBeans 6.9.1, you will have to add the following property use.pack200=false[/code]at the end of this property file ($ProjectRoot/nbproject/project.properties) in order to disable the pack200 feature. Otherwise, the plugin install in iReport will not work. Step-by-stepPackaging our plugin for deployment, we need to generate a NBM file( NetBeans Module). From NetBeans IDE, right-click on your plugin project and select "Create NBM".Installing the plugin into iReport Launch iReport v3.7.6 or laterTools -> PluginsChoose "Downloaded" tabClick on "Add Plugins..." buttonBrowse to your NBM file generated in step 1.a, then "Open"Click on the "Install" buttonYou are now ready to use your new component.
  14. IntroductionIn this article, we'll walk through integrating OpenLDAP with JasperServer. This article has been tested for both JasperServer 3.7 through JasperReports Server 4.7. For JasperReports Server 5.0 and beyond, please study the sample configuration file that's in the samples directory, under the externalAuth-sample-config subdirectory. This article assumes that you already have JasperServer already installed, and that you need to install and integrate OpenLDAP with JasperServer. This article also assumes that you're working with Ubuntu. The principal steps are: Install necessary libraries before installing OpenLDAPInstall OpenLDAPConfigure OpenLDAPLoad Test DataVerify Test Data in OpenLDAP with third-party ldap management GUI toolIntegrate OpenLDAP with JasperServerTestYou can test the integration by going to the JasperServer login page, and using a user login and password that you loaded in the LDAP test data. At the end of this article, there is a troubleshooting and sample files section, which provide examples of what we are talking about. InstallationThis page is going through the steps to install OpenLDAP version 2.4.23. A Ubuntu 10.04.1 32bit environment was used. Recommended page to guide you: openldap, quick start Install Berkeley DBRather than downloading it from the web and doing manual install Synaptic Package manager gives all you need to get Berkeley DB. You'll need to install these packages: libdb4.8++libdb4.8-javadb4.8-utillibdb-je-javalibdb-dev (version 4.8)The Synaptic Package manager will install everything for you; you may need to restart your machine to have the changes take effect. Install OpenLDAP Download OpenLDAP from here - http://www.openldap.org/software/download/. You will download openldap-2.4.23.tgz. Unzip and untar to a temporary location. From main OpenLDAP Directory run $ sudo ./configure Then run everything as root, if you don't you might get errors. $ sudo make depend $ sudo make $ sudo make test $ sudo make install Then found everything installed in /usr/local/etc/openldap Creating Test DataThe first step is start running the LDAP server. You do this by: $ cd /usr/local/libexec $ sudo ./slapd To configure the server, you can modify the slapd.conf and ldap.conf files in /usr/local/etc/openldap. I'm attaching the files I used to this page as a reference. Using JXPlorer (an LDAP GUI tool)The second step is to see what data is stored there. A useful, lightweight Java tool that allows you to see the contents of the LDAP store is JXplorer (http://jxplorer.org/). It allows you to see an LDAP structure via a GUI application. If you're just inspecting the data, all you have to do is supply the server hostname, the port (it should be 389 for OpenLDAP, not the default 19389), and you can connect. If you want to do write or delete operations, then you'll have to login as the root LDAP user. There are many other tools available, but this one is sufficient for our purposes here. Loading Test DataI created a file with test data to load into the LDAP store, it's called Sample_29112010.ldif and is attached to this page. The entries use the some of the schemas that ship with OpenLDAP. From the command line you can add the test file with this command: cd [Directory where the sample file lives] $ ldapadd -h ubuntu -p 389 -D "cn=Manager,o=Jaspersoft" -w secret -f Sample_16122010_2.ldif The test records set up each person with object classes of top, person, and organizationalPerson. Each record defines a person's uid, password, and role. Since this is just an example, I've set up the test data to store a person's JasperServer role in the title field. You can verify the records by downloading the Sample_16122010_2.ldif file that is attached to this page, they are text files you can easily inspect. You can verify that it loaded successfully by using the JXplorer tool to view the data. You'll be able to see everything except the password. Searching for DataYou can use the ldapsearch command line tool to search for entries according to criteria you want. Below is an example where we're searching for all entries whose sn value is Jensen, and displaying only the uid and userPassword attributes. $ ldapsearch -h ubuntu -b "o=Jaspersoft" "sn=Jensen" uid userPassword Integrate it with JasperServerThe next steps are to: Modify the applicationContext-security.xml fileModify the applicationContext-multiTenancy-security.xml fileRestart JasperServerModifying the applicationContext-security.xml fileUncomment out the reference to the ldapAuthenticationProvider bean in the definition of the authenticationManager bean.Uncomment out and modify the values for the ldapContextSource bean - there's an example in the troubleshooting section belowSpecify the location of where to begin the search in the constructor-arg parameterSpecify the LDAP user DN and password that will be performing the actual operations on the LDAP server.Uncomment out the userSearch beanUncomment out the ldapAuthenticationProvider beanFor our example, look for the groupRoleAttribute property and and modify its value to title - that is the field name in the user record we will be using in this example. By modifying it to title, we're telling JasperServer that this field contains the user's role within JasperServer.Modifying the applicationContext-multiTenancy-security.xmlThere's two items to modify in this file: Enable the reference to the ldapExternalUserProcessor bean, which is commented out and is part of the definition of the mtUserAuthorityServiceTarget bean.Enable the ldapExternalUserProcessor bean, which is commented out by default.TroubleshootingVerify the LDAP structure is right in the ldapContextSourceThe integration isn't very complicated, but it does assume that you understand the structure of an LDAP directory. In this example, the node at which the uid search starts is defined in the ldapContextSource bean. It contains a constructor-arg parameter which tells the server where to search for users. <bean id="ldapContextSource"> <!-- constructor-arg value="ldap://localhost:389/o=Jaspersoft,c=US"/ --> <constructor-arg value="ldap://localhost:389/o=Jaspersoft"/> <!-- You may not need the next properties --> <property name="userDn"> <value>cn=Manager,o=Jaspersoft</value> </property> <property name="password"> <value>secret</value> </property> </bean> In the above example, JasperServer requests that the user searches start at the "o=Jaspersoft" node. That means that a user's DN must end with "o=Jaspersoft" for it each to match. Be careful when editing XML filesSince we're editing XML files by hand, we have to make double-sure that we're not doing something incorrectly! Look at the logs for cluesYou have two logs to look at: The jasperserver.log file in the /jasperserver-pro/WEB-INF/logs/ directory the syslog file in the /var/log directory - if it is not in this directory, use the locate command to find it. It will contain system logging when JasperServer interacts with OpenLDAP.OpenLDAP itself can have its own log file, which you can define in the slapd.conf file - setting it up is beyond the scope of this page.Sample FilesApplicationContext-multiTenancy-security.zip - this Zip contains a sample xml file we used for testing the content on this page.ApplicationContext-security(1).zip - this Zip contains a sample xml file for the Spring security configuration.Ldap.zip - this Zip file contains configuration files for OpenLDAP as well as sample data for testing the integration.
×
×
  • Create New...