Jasper Reports and PHP

Update October 27th, 2007: I have written a more recent entry that should make life a lot easier.


I’m sure most of us have come across Jasper Reports at some stage. Jasper gives Java developers a complete reporting infrastructure so dynamic reports can be generated from databases in a pretty, printer friendly format.

It has many similarities to the infamous Crystal Reports. It is however, completely open source and designed from the ground up to be flexible, extendable and valuable.

In short: it rocks.

So, how do we get this cool functionality with PHP? Well the obvious solution is to do it all in Java and use the good ole exec (or similar) to create the report and throw the output back to the browser. There are a couple of issues with this however that make it undesirable:

  1. It is slow. Every time a report is generated we have to wait for the Java virtual machine to start, execute the code and unload again. Not the best.
  2. Passing parameters between PHP and Jasper becomes troublesome. You will have to pass things either through the exec command, or use something like popen so you can communicate to and from Java though standard in / out.

Thankfully we can have PHP talk to Java efficiently through the excellent work of the guys at PHP/Java Bridge.

The bridge works by having a Java “server” running in the background. Communication is done between the VM and PHP through a light weight network protocol. This approach has a number of advantages including stability (something that the normal PHP extension has failed to achieve) and it neatly address our two major concerns above.

All is not rosy however. The PHP/Java Bridge comes with its own set of challenges, especially when dealing with Jasper.

  1. The current directory of the JVM (or “user.dir”) is unknown.

Jasper has a funny way of achieving things. First, it takes a nicely formatted XML file as input and converts this into a temporary Java source file. It then compiles this .java file through javac. After that it loads it up, passes any params to it and gets it to create a report that can be exported to a variety of formats. Sounds complicated but this approach really makes things more flexible.

We run into problems here when jasper comes to creating the java source representation of the XML .jrxml file. By default, jasper will create the temp file in the current working directory. Unfortunately this is usually root (/), which the JVM has no write access to (thankfully) so it dies a horrible screaming death.

This can be overridden by setting the System property “jasper.reports.compile.temp”, however this does not appear to work when setting it through PHP.

  1. Classpaths are all screwy. I actually have 1 installation of the bridge that by all rights should not be working as the required Jars are not in the classpath of the JVM at all. The issues, unfortunately, are usually the other way around.

This issue raises its head once the java file is created and now needs to be compiled. For some reason, any Jars that are added to the classpath through the java_require command are not available to the javac call when jasper wants to go ahead and compile the java source file.

Once again, there is a system property that can be used as the class path for Jasper, this is “jasper.reports.compile.class.path”. However, as I mentioned above, setting this in PHP does not work.

  1. Relative directories are a no-no.

Because PHP and java are running separately, each has their own “working” directory. We cannot simply pass java a file and expect it to know what path it is in. Use absolute paths for everything in Java.

Fortunately there are a couple of solutions for the above. The first is altering the way PHP creates a JVM. By modifying the java options in php.ini we can make PHP connect to the java server through a TCP socket. We can then start the java server through a simple java command with the appropriate class path and user.dir settings passed through the command line. The drawback to this approach is that it means we have to start the java server separately to the web server, and the whole thing will come crashing down around us if we start apache without java running.

An alternative is to write another java server based on the standard one that comes with the bridge to have the environment setup the way we want. The problem with this approach is that we are lazy and time is money.

The other option that I thought of and the one I’ve gone with is creating a little wrapper class to set the appropriate system properties before creating the Jasper report.

And here it is: download

To use, compile and package it up into a .jar file for use by the bridge.

This class takes away most of the pain in creating a jasper report. It provides utility functions for retrieving the parameters a report requires and exporting reports to the default supported formats. It also manages a cache of the reports so they don’t have to be recompiled each time they are run, which is the most expensive part of the whole process.

We can then use the following code to run a report. Remember that $javaParams is a Java HashMap of java objects, not a PHP array:

Or this to get a listing of all the params a report requires. Note that I had to convert between the java HashMap and the PHP associative array:

Now, we have to make sure we pass java values back as parameters to the runReport function above. Thankfully there are not that many java types we can use as Jasper parameters and most of them take strings in one form or another. You may find this function useful for converting between PHP and Java values:

Okay, now a few words of wisdom to finish up this little post to help get you to Jasper Reports nirvana:

32 Responses to “Jasper Reports and PHP”

  1. t14n4t4 Says:

    what do you mean with $jasperClassPath? I don’t understand.
    Where do you put jasper class?Thanks b4

  2. t14n4t4 Says:

    how about with $tempFile = tempnam(TMP, ”);. Please help me.

  3. blog Says:

    $jasperClassPath is a string with the absoulte paths to all the required Jasper jar files. It is passed directly to java as the classpath to use when compiling jasper report.

    $tempFile = tempnam(TMP, “); generates a test filename in the TMP directory. TMP is a constant that can be declared like:
    define(’TMP’, ‘/tmp’);

    More info on the tempnam command here: http://au3.php.net/tmpname

  4. anata_arie Says:

    is it gonna run well on PHP 5 and mysql 5?
    Thank U.

  5. anata_arie Says:

    Is it gonna run well on PHP 5 and mysql 5?
    I’ve already try it manytimes but still not work.Help me please…..
    Thanks

  6. blog Says:

    Yes, it does work on PHP 5 and MySQL 5.

    Be sure to check your Apache error log as the Java related errors do not generally appear in your browser through PHP.

  7. lucianc Says:

    The issues related to report compilation (#1 and #2, if I understand correctly) might be resolved by dropping the javac-based report compiler in favor of the JDT-based one.

    The JDT-based report compiler has two advantages:
    1. It does not need to use temp files to compile a report, it can do so by only using in-memory data.
    2. It uses the context classloader (as in Thread.getContextClassLoader()) to resolve classes while compiling reports, so it doesn’t need jasper.reports.compile.class.path to be set.

    Given these features, we recommend the JDT-based compiler as it’s easier to use in almost any situation. The compiler is automatically used when the jdt-compiler jar is present on the application’s classpath (and no compiler is explicitly used).

    I don’t guarantee that using the JDT compiler would solve the first two issues as I don’t know how the PHP bridge works, but it’s worth trying.

    Regards,
    Lucian

  8. blog Says:

    Thanks Lucian, removes a lot of the frustration.

    I have added a new entry with a simpler approach that makes use of the JDT compiler.

  9. Jagadmaya Solution - Integration PHP/JasperReports Says:

    [...] Jasper Reports and PHP It is a very good article which brings already a certain abstraction of complexity To marble Carryforward in PHP. It is necessary I think of going even further. But it is a very good beginning. http://www.rjohnson.id.au/wordpress/2007/02/04/jasper-carryforwards-and-php/ [...]

  10. Richard Johnson » Blog Archive » Bullet-Proof Jasper Reports and PHP Says:

    [...] so I got a fair bit of traffic about my last entry where I attempted to explain how I managed to get PHP to talk to Jasper through use of the PHP/Java [...]

  11. Marco Says:

    Hi !

    I try to export a jasperreport to excel from php…

    my code :
    $sPar = new JavaClass(”net.sf.jasperreports.engine.export.JRXlsExporterParameter”);

    $sJem = new JavaClass(”net.sf.jasperreports.engine.export.JRXlsExporter”);
    $sJem->setParameter($sPar->JASPER_PRINT, $print);
    $sJem->setParameter($sPar->OUTPUT_FILE_NAME, $reportsPath .$reportFileName.”.xls”);
    $sJem->setParameter($sPar->IS_ONE_PAGE_PER_SHEET, false);
    $sJem->exportReport();

    but i get this error:
    java stack trace: java.lang.Exception: Invoke failed: [c(JRXlsExporter)]->setParameter(o(JRExporterParameter), o(JasperPrint)). Cause: java.lang.NoSuchMethodException: setParameter(o(JRExporterParameter), o(JasperPrint)). Candidates: [] Responsible VM: 1.5.0_10@http://java.sun.com/ at php.java.bridge.JavaBridge.Invoke(JavaBridge.java:1096) at php.java.bridge.Request.handleRequest(Request.java:342) at php.java.bridge.Request.handleRequests(Request.java:388) at php.java.bridge.JavaBridge.run(JavaBridge.java:200) at php.java.bridge.BaseThreadPool$Delegate.run(BaseThreadPool.java:66) Caused by: java.lang.NoSuchMethodException: setParameter(o(JRExporterParameter), o(JasperPrint)). Candidates: [] … 5 more

    i apreciated you help…

    (sorry por my poor english)

    Thanks guys

  12. Ignacio Says:

    Hey, I’ve got a
    java.security.AccessControlException
    it’s thrown when on first line of the constructor of the ReportGenerator object when it tries to
    System.setProperty(”jasper.reports.compile.class.path”)
    How can i solve this?

  13. Tony B Says:

    Hi, I am unable to pass the database connection thus am getting an empty (null) result. Someone pls help me to get this working

  14. vikas Says:

    Hi Richard,
    I am facing problem in jasper report.
    i am getting date field from data base as Long.
    I want to display it as dd-mm-yyyy, how can i do this conversion in jrxml.Please help.
    regards,
    Vikas

  15. Nisha Says:

    Hi Richard,
    I am facing a problem ..would be gr8 if you could provide some help. I am new to jasper reports and I am trying to integrae jasper with eclipse IDE. I have php-java bridge up . Initially had unzipped into a seperate folder but now have put the contents of Java folder and javaBridge.jar in the eclipse project folder . Now could you please tell me how can I proceed with Jasper integration with eclipse.
    Thanks
    Nisha

  16. Nisha Says:

    HI Richard,
    I am trying to use jasperreports and php. While I call the method compile manager by passing a test.jrxml following error is thrown. Have been trying from days but not getting any clue.. Could u plzzzzz help.

    Errors were encountered when compiling report expressions class file: D:\wamp\bin\php\php5.2.8\ext\HI_1238371884860_172091.java:4: package net.sf.jasperreports.engine does not exist import net.sf.jasperreports.engine.*; ^ D:\wamp\bin\php\php5.2.8\ext\HI_1238371884860_172091.java:5: package net.sf.jasperreports.engine.fill does not exist import net.sf.jasperreports.engine.fill.*; ^ D:\wamp\bin\php\php5.2.8\ext\HI_1238371884860_172091.java:18: cannot resolve symbol symbol : class JREvaluator location: class HI_1238371884860_172091 public class HI_1238371884860_172091 extends JREvaluator ^ D:\wamp\bin\php\php5.2.8\ext\HI_1238371884860_172091.java:25: cannot resolve symbol symbol : class JRFillParameter location: class HI_1238371884860_172091 private JRFillParameter parameter_REPORT_LOCALE = null; ^ D:\wamp\bin\php\php5.2.8\ext\HI_1238371884860_172091.java:26: cannot resolve symbol symbol : class JRFillParameter location: class HI_1238371884860_172091 private JRFillParameter parameter_REPORT_TIME_ZONE = null; ^

  17. Lesliê Cardoso Says:

    Hi Richard,

    I managed to make the report produce! But not passing the archive *.jrxml to compile how in the code down:

    $compileManager = new JavaClass (” net.sf.jasperreports.engine. JasperCompileManager “);
    $report = $compileManager-> compileReport (realpath (”relatorio.jrxml”));

    It would like what knowledge *.jar what I put to work with this class or even it might be in charge for the email or to put in annexe for us!
    Thank you!

  18. Prego Says:

    Hi Richard,

    I use xampp as Apache and Tomcat installed xampp’s own I wonder where I put the Java Bridge it will run on the server or Apache Mark up, not really clear you could help me?

  19. Sibu Says:

    We have a PHP project we have to create jasper report, how to integrate java with PHP to produce jasper report.Please let me know ur suggestions soon and i have no idea about jasper report and PHP

  20. ruksg Says:

    Hi doing a system in drupal and i want to make it so that reports can be printed depending on search results - how can we go abt this?
    coding with php and mysql on the drupal system.
    i have absolutely no idea how to integrate jasper reports to this drupal system.
    please help

  21. Merrill Says:

    I’m one of those PHP programmers, who barely can spell java, so I need some help. I apologize for my ignorance. I’m trying to compile the wrapper ReportGenerator.java, downloaded from this site and I keep getting a bunch of errors like:

    ReportGenerator.java:1: package net.sf.jasperreports.engine does not exist
    import net.sf.jasperreports.engine.*;

    I’m running xampp, with their tomcat plugin on WinXp. I have java installed, the jdk, and I’ve installed the JasperServer, which is running. I can see it in the tomcat manager.

    Oh, also I have iReport installed and it is working fine. I’ve developed several reports with no problems.

    It appears it simply cannot find the JasperServer classes required, but I don’t know why. Can someone help me, please?

    Thanks,

    Merrill

  22. Raven Says:

    Hello Mr. Richard. I’m just giving this post a shot, hoping and trying my luck on getting solutions on PHP generating report using JasperReport. The problem is I dont know how and where to start with the integration/configuration. I’m only using plain php/html in generating my reports. Though i’ve heard about jasperreports before, but i haven’t yet tried using it. I’ve already read your entry regarding JasperReports and PHP which you posted October 2007 but i was still at a loss on how to go on regarding the integration/configuration. Hope you could help me with this. Thank you very much.

  23. Bruno Says:

    @Merrill

    I had the exact same problem as yours and it was really hard to find a solution. I took me three days of extensive search to find a clue.

    I’m not really sure the following steps are correct but I got my scripts working perfectly after doing them.

    First, go to your IReport install dir, search its folder and subfolder recursively for *.jar.
    After locating them all, select them, CTRL+C, go to your tomcat WEB-INF/ext dir and paste them there.
    Then, go to your program files/java dir and copy those same files into your jre/ext dir.

    Restart tomcat and you’re set!

    Also, notice I didn’t install the JasperServer at all, just IReport, and I’m using the PHP/JavaBridge .war file. Just copy the JavaBridge.war file into your tomcat/webapps dir and restart tomcat.

    Regards,

    Bruno

  24. Integrar JasperReports en PHP « avanttic blog Says:

    [...] http://www.rjohnson.id.au/wordpress/2007/02/04/jasper-reports-and-php/ [...]

  25. adedekene Says:

    Hello,

    I’m using PHP 5.3.0 and Apache 2.2.11 with WAMPServer.
    I have downloaded the JavaBridge.jar and put it in
    the directory of php’s extension. And I change also the php.ini, I add in “Module Settings” :

    [java]
    java.java_home = “c:\Program Files\Java\jdk1.5.0_10\bin”
    java.java = “c:\Program Files\Java\jdk1.5.0_10\bin\javaw.exe”
    java.class.path = “C:\wamp\bin\php\php5.3.0\ext\JavaBridge.jar”
    java.library.path = “C:\wamp\bin\php\php5.3.0\ext”
    ;java.hosts = “127.0.0.1:8080″
    ;java.servlet = On
    java.log_level = 2

    I restarted the services. But the problem is that when I try to execute Java’s code in PHP

    ex:$system = new Java(’java.lang.System’);

    The class Java is not found

  26. Shashank Says:

    @richard : good tutorial it really helped me.

    @adedekene : I don’t know much but i think the value for classpath i.e.java.class.path should be same as that of CLASSPATH defined for java at the time of installation.

  27. Kanwar Singh Says:

    Hi,

    I am getting this error message please help:

    java stack trace: java.lang.Exception: CreateInstance failed: new JdbcConnection. Cause: java.lang.ClassNotFoundException: JdbcConnection VM: 1.6.0_17@http://java.sun.com/ at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1374) at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1220) at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:332) at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:264) at php.java.bridge.Util.classForName(Util.java:1518) at php.java.bridge.JavaBridge.CreateObject(JavaBridge.java:445) at php.java.bridge.Request.handleRequest(Request.java:458) at php.java.bridge.Request.handleRequests(Request.java:500) at php.java.bridge.http.ContextRunner.run(ContextRunner.java:145) at php.java.bridge.ThreadPool$Delegate.run(ThreadPool.java:60) Caused by: java.lang.ClassNotFoundException: JdbcConnection …

  28. Arun Says:

    Can anyone send me the compiled jar file of the ReportGenerator.java….as i am a newbie to java am not able to compile it.

  29. mathewssunny Says:

    Hi Richard,

    I had created a project devolped in PHP(Symfony) and mysql with Javabridge and jasper libraries.I did html and Pdf export using following code

    require_once(”http://127.0.0.1:8080/JavaBridge/java/Java.inc”); [^]

    $jasper_abs_path = sfConfig::get(’sf_upload_dir’).”/jasper_reports/”;

    $class = new JavaClass(”java.lang.Class”);
    $class->forName(”com.mysql.jdbc.Driver”);
    $driverManager = new JavaClass(”java.sql.DriverManager”);
    $conn_path = ‘jdbc:mysql://’.$host.’/’.$dbname.’?user=’.$db_user.’&password=’.$db_pass; [^]
    $conn = $driverManager->getConnection($conn_path);
    $test_path = $jasper_abs_path.”test.jrxml”;
    $compileManager = new JavaClass(”net.sf.jasperreports.engine.JasperCompileManager”);
    $report = $compileManager->compileReport($test_path);
    $fillManager = new JavaClass(”net.sf.jasperreports.engine.JasperFillManager”);
    $params = new Java(”java.util.HashMap”);
    $emptyDataSource = new Java(”net.sf.jasperreports.engine.JREmptyDataSource”);
    $jasperPrint = $fillManager->fillReport($report, $params, $conn);
    $exportManager = new JavaClass(”net.sf.jasperreports.engine.JasperExportManager”);

    //Pdf
    $test_pdf = $jasper_abs_path.”$test.pdf”;
    $exportManager->exportReportToPdfFile($jasperPrint,$test_pdf);
    //HTML
    $test_html = $jasper_abs_path.”test.html”;
    $exportManager->exportReportToHtmlFile($jasperPrint,test.html);

    I need the code for XLS,CSV,RTF for the same..

    Thanks
    Sunny Mathew

  30. Arun Says:

    @Mathew, you can check this link for the jasperreports api there are functions for XLS,CSV,RTF similar to the PDF export.
    http://jasperreports.sourceforge.net/api/index.html?net/sf/jasperreports/engine/export/JRXlsAbstractExporter.html

  31. mathewssunny Says:

    @Arun
    Thanks for the reply ..

    $jasper_abs_path = sfConfig::get(’sf_upload_dir’).”/jasper_reports/”;
    $class = new JavaClass(”java.lang.Class”);
    $class->forName(”com.mysql.jdbc.Driver”);
    $driverManager = new JavaClass(”java.sql.DriverManager”);
    $conn_path = ‘jdbc:mysql://’.$host.’/’.$dbname.’?user=’.$db_user.’&password=’.$db_pass; [^]
    $conn = $driverManager->getConnection($conn_path);
    $test_path = $jasper_abs_path.”test.jrxml”;
    $compileManager = new JavaClass(”net.sf.jasperreports.engine.JasperCompileManager”);
    $report = $compileManager->compileReport($test_path);
    $fillManager = new JavaClass(”net.sf.jasperreports.engine.JasperFillManager”);
    $params = new Java(”java.util.HashMap”);
    $jasperPrint = $fillManager->fillReport($report, $params, $conn);

    //XLS
    $test_xls = $jasper_abs_path.”$test.xls”;
    $parameter = new JavaClass(”net.sf.jasperreports.engine.export.JRXlsExporterParameter”);
    $exportManager = new JavaClass(”net.sf.jasperreports.engine.export.JRXlsExporter”);
    $exportManager->setParameter($parameter->JASPER_PRINT, $print);
    $exportManager->setParameter($parameter->OUTPUT_FILE_NAME, $test_xls);
    $exportManager->setParameter($parameter->IS_ONE_PAGE_PER_SHEET, false);
    $exportManager->exportReport();

    I am getting error for this code …
    //
    500 | Internal Server Error | java_InternalException
    stack trace

    * at ()
    in http://127.0.0.1:8080/JavaBridge/java/Java.inc line 195 …
    * at java_ThrowExceptionProxyFactory->getProxy(14, ‘net.sf.jasperreports.engine.JREmptyDataSource’, ‘T’, 1)
    in http://127.0.0.1:8080/JavaBridge/java/Java.inc line 232 …
    * at java_Arg->getResult(1)
    in http://127.0.0.1:8080/JavaBridge/java/Java.inc line 360 …
    * at java_Client->getWrappedResult(1)
    in http://127.0.0.1:8080/JavaBridge/java/Java.inc line 366 …
    * at java_Client->getResult()
    in http://127.0.0.1:8080/JavaBridge/java/Java.inc line 560 …
    * at java_Client->invokeMethod(13, ’setParameter’, array(’JASPER_PRINT’, object(’java_InternalJava’)))
    in http://127.0.0.1:8080/JavaBridge/java/Java.inc line 1752 …
    * at java_JavaProxy->__call(’setParameter’, array(’JASPER_PRINT’, object(’java_InternalJava’)))
    in http://127.0.0.1:8080/JavaBridge/java/Java.inc line 1851 …
    * at java_AbstractJava->__call(’setParameter’, array(’JASPER_PRINT’, object(’java_InternalJava’)))
    in http://127.0.0.1:8080/JavaBridge/java/Java.inc line 1999 …
    * at Java->__call(’setParameter’, array(’JASPER_PRINT’, object(’java_InternalJava’)))
    in n/a line n/a …
    //

    Help Me….

    Regards
    Sunny Mathew

  32. Dave Jarvis Says:

    Hey, Richard.

    Your PHP instructions partially inspired me to write a technical manual.

    http://www.whitemagicsoftware.com/books/indispensable/

    I have also published all of the source code (for free) on the site.

    Thanks for all the help!

Leave a Reply