Getting PHP to work with Jasperreports using php/javabridge

Here are some code scraps from php code that integrates with jasper 3.0 reports. I’ll have to add more documentation to make this a full article as well but the jasper approach proved too error-prone to conditional issues (data-bound) so it might never get finished at all. I hope this can help people deal with some of the problems I had incorporating this all into a web application.

As a reference, also take a look at Richard Johnson’s blog, it put me direction on how to tackle the gap between java and php. I suck at java, I really don’t like it either but this is a rare occasion to have to deal with it. I didn’t know about php_java module until I tried to google for solutions to write my own kinda jasper-server engine but driven by php.

After some googling about I came across a blog entry from Richard Johnson’s blog, the first posting describing a way to do it. A tad later, a second post appears to elaborate on new findings.

I noticed a few things in his example once I was digging into my solution. Don’t misinterpret, I really needed his groundbreaking work. It educated me and made me learn more about java/jasper/bridge inner workings. I could then start on compiling the stuff from source in order to create debian packages. Which in Debian is a piece of cake. So thanks, now comes …

the comments.

  • Richard’s example doesn’t show you how to use a real datasource, that proved to be hard getting this to work with certain datatypes interaction between my mysql db and java of jasper. The TIME type in particular proves challenging
  • Locale problems. I live in Belgium and we like our dots and comma’s in the right place for us. Running a report as the example showed gave me wrong formatted dates due to misinterpretation between comma‘s and dot‘s.

Next comes some code snippets from the php source, I can’t post it all but I hope all relevant pieces are. In the config.php we have defined:

...
// Declare the paths where the JRXML Reports are stored : java likes absolute paths, we don't in our webapp...
$reportsPath ="/home/tester/public_html/";  // DEV

// Declare the Reports Library Path, we used debian etch packages !!! (universe)
$jasperReportsLib = "/usr/lib/jvm/java-6-sun-1.6.0.06/jre/lib/ext";

// Database part
// Connection URL (since it is in the server, it can be defined as localhost)
$jasperConnectString="jdbc:mysql://" . $host . ":3306/" . $database;
...

In our generator we have this code working:

...
require_once("java/Java.inc");
...
global $reportsPath;
global $jasperReportsLib;
global $jasperConnectString;
global $user,$password;
...

Now here we have the workhorse of the bridge for jasper integration, it uses the bridge and mysql database that contains data but also records with report type entries and associated jrxml files. This is information not really needed to understand the issue here but I’ll give it anyway so you know what this comes from, $reportsPath and $xmlsource are filled in using a query from a set of report tables (that also contains the input fields data so it is in fact a report engine). I also integrate RLIB report engine with this. This also uses xml files so these 2 integrate well next to each other.

...
checkJavaExtension();

// Open the Directory for reading of the Jasper Report Libabries
$handle = @opendir($jasperReportsLib);

// Add all the Jar file path class (Class Path)
while(($new_item = readdir($handle))!==false) {
$java_library_path .= 'file:'.$jasperReportsLib.'/'.$new_item .';';
}

$compileManager = new JavaClass("net.sf.jasperreports.engine.JasperCompileManager");
$report = $compileManager->compileReport(realpath($reportsPath . "/" . $xmlsource));

$fillManager = new JavaClass("net.sf.jasperreports.engine.JasperFillManager");

// Loading the libraries classpath
java_require($java_library_path);

// Create the JDBC Connection
$Conn = new Java("org.altic.jasperReports.JdbcConnection");

// Call the driver to be used
$Conn->setDriver("com.mysql.jdbc.Driver");

// Connection URL (since it is in the server, it can be defined as localhost)
$Conn->setConnectString($jasperConnectString);
// Mysql Server Connection Username
$Conn->setUser($user);
// Mysql Server Connection Password
$Conn->setPassword($password);

$params = new Java("java.util.HashMap");
if (isset($my_dev_imei)) {
$params->put("InputImei", convertValue($my_dev_imei,"java.lang.String"));
}

$params->put("InputStartDate",convertValue($my_start ,"java.sql.Timestamp"));
$params->put("InputStopDate",convertValue($my_stop ,"java.sql.Timestamp"));

$params->put("InputCustomer", convertValue($my_account,"java.lang.String"));

// This took me a while , also see convertValue function
$params->put("REPORT_LOCALE", convertValue("nl_BE","java.util.Locale"));

/* Test empty datasource stuff
$emptyDataSource = new Java("net.sf.jasperreports.engine.JREmptyDataSource");
$jasperPrint = $fillManager->fillReport($report, $params, $emptyDataSource);
*/


$jasperPrint = $fillManager->fillReport($report, $params, $Conn->getConnection());

$outputPath = realpath(".")."/output/"."output.pdf";

$exportManager = new JavaClass("net.sf.jasperreports.engine.JasperExportManager");
$exportManager->exportReportToPdfFile($jasperPrint, $outputPath);

header("Content-type: application/pdf");
readfile($outputPath);
unlink($outputPath);
...

Here’s my version of this file so you can actually get the Locale stuff across through the report parameter. In Ireports itself you usually don’t have to worry about locales. But using the php/java bridge that’s a different story. You’ll notice the difference with Richards code easily.

/**
* convert a php value to a java one...
* @param string $value
* @param string $className
* @returns boolean success
*/


function convertValue($value, $className)
{
// if we are a string, just use the normal conversion
// methods from the java extension...
try
{
if ($className == 'java.lang.String')
{
$temp = new Java('java.lang.String', $value);
return $temp;
}
else if ($className == 'java.lang.Boolean' ||
$className == 'java.lang.Integer' ||
$className == 'java.lang.Long' ||
$className == 'java.lang.Short' ||
$className == 'java.lang.Double' ||
$className == 'java.math.BigDecimal')
{
$temp = new Java($className, $value);
return $temp;
}
else if ($className == 'java.sql.Timestamp' ||  $className == 'java.sql.Time')
{
$temp = new Java($className);
$javaObject = $temp->valueOf($value);
return $javaObject;
}
else if ($className == 'java.util.Locale')
{
$value_arr=explode("_",$value);
$temp = new Java($className, $value_arr[0], $value_arr[1]);
return $temp;
}
else if ($className == "java.util.Date")
{
$temp = new Java('java.text.DateFormat');
$javaObject = $temp->parse($value);
return $javaObject;
//$format = new Java('java.text.SimpleDateFormat', "MM-dd-yyyy HH:mm:ss");
//$format->format(new Java('java.util.Date'));
}
}
catch (Exception $err)
{
echo (  'unable to convert value, ' . $value .
' could not be converted to ' . $className . ' ');
//' could not be converted to ' . $className . ' ' . $err);

return false;
}

echo (  'unable to convert value, class name '.$className.
' not recognised');
return false;
}

Below is the checkJavaExtension again as it was.

/*  Start a java instance like this :
* /usr/lib/jvm/java-1.6.0-openjdk-i386/bin/java -Djava.class.path=-Djava.class.path=/usr/lib/php5/20090626/JavaBridge.jar -Djava.library.path=/usr/lib/php5/20090626/ -Dphp.java.bridge.base=/usr/lib/php5/20090626/ -Djava.awt.headless=true -jar /var/jars/JavaBridge.jar SERVLET:8080 2>/var/log/JavaBridge.log
* Jasperreports php function
* see if the java extension was loaded.
*/

function checkJavaExtension()
{
if(!extension_loaded('java'))
{
$sapi_type = php_sapi_name();
$port = (isset($_SERVER['SERVER_PORT']) && (($_SERVER['SERVER_PORT'])>1024)) ? $_SERVER['SERVER_PORT'] : '9267';
if ($sapi_type == "cgi" || $sapi_type == "cgi-fcgi" || $sapi_type == "cli")
{
if(!(PHP_SHLIB_SUFFIX=="so" && @dl('java.so'))&&!(PHP_SHLIB_SUFFIX=="dll" && @dl('php_java.dll'))&&!(@include_once("java
/Java.inc"
))&&!(require_once("http://127.0.0.1:$port/java/Java.inc")))
{
return "java extension not installed.";
}
}
else
{
if(!(@include_once("java/Java.inc")))
{
require_once("http://127.0.0.1:$port/java/Java.inc");
}
}
}
if(!function_exists("java_get_server_name"))
{
return "The loaded java extension is not the PHP/Java Bridge";
}

return true;
}

Versions:

  • PHP Version 5.2.0-8+etch11

.deb files I had to create and load:

  • php-java-bridge-j2ee_5.2.2-1_all.deb: Debian binary package (format 2.0)
  • php-java-bridge-devel_5.2.2-1_all.deb: Debian binary package (format 2.0)

Also, in the apache config file : /etc/php5/apache2/conf.d/java.ini

;; -*- mode: Scheme; tab-width:4 -*-

;; java.ini: Activate the PHP/Java extension

;; zend_extension = "/path/to/java.so"
extension = java.so

;; If you have installed the java-servlet.ini leave this file alone,
;; edit the servlet or standalone ini file.  Otherwise uncomment the
;; following java section and one of the following options:
;;[java]

;; If you want to start the default JVM automatically when the HTTP
;; server starts, disable java.hosts, java.servlet and
;; java.socketname, set java.java_home= and
;; java.java=, for example:

;;java.java_home = /usr/lib/jvm/java-6-sun-1.6.0.06
;;java.java      = /usr/lib/jvm/java-6-sun-1.6.0.06/jre/bin/java

;; If you have a servlet engine or an application server, deploy
;; JavaBridge.war and re-start the servlet engine or the application
;; server. Comment out the "java.socketname" option and uncomment
;; the following options.

; java.hosts     = 127.0.0.1:8080
; java.servlet   = On ;; Off, On or User

;; The request log level between 0 (log off) and 4 (log debug). The
;; default request log level is initialized with the value from to the
;; Java system property "php.java.bridge.default_log_level" or from
;; the servlet's init-param: servlet_log_level (see WEB-INF/web.xml)
;; The default level is 2 (servlet) or 3 (standalone).

;;java.log_level  = 3

;; If you want to use log4j "chainsaw" running on localhost, port 4445
;; enable the following option. The default "log file" is the standard
;; error.

; java.log_file  = @127.0.0.1:4445