Running PHP Scripts inside of Tomcat and accessing Java Classes via Caucho Quercus
Setup and install of Quercus allowing php to access java classes
In my current environment we have many php developers. These php developers find coding with Java Web Frameworks to be cumbersome. We are currently using Struts 1.X which although was not my first choice seems to be what was adopted . As a note : I am eagerly anticipating Web Beans (based on Jboss Seam) Reference Implementation to be standardized. http://jcp.org/en/jsr/detail?id=299
However they love all the other great stuff that comes with Java such as IDE (Eclipse)
Integration, JUnit, Spring, plethora of open source apis available in third party jars, etc…
So they would like the ability to be able to code business logic in java and code presentation logic in a dynamic language and specifically php. I also looked into groovy and this looks promising however its difficuly to justify learning a new language even if groovy is more java friendly than php.
I had previously played around with jsr 223. I played with the reference implementation and was able to get it working however it was sold as highly experimental and although Zend Technologies (bascially the owners of PHP) were part of the original JSR223 they seem to have dropped out the mix (I can only assume because Zend makes money on their own version of PHP – Java Integration so why would they throw that revenue down the drain).
See https://scripting.dev.java.net/ you’ll notice that PHP isnt really in the main list there. Which means that sun is not putting any effort into getting php to run under tomcat. However at the bottom of the pae you will notice the link for Caucho’s Quercus.
http://www.caucho.com/resin-3.0/quercus/- pure Java implementation of PHP
Caucho is responsible for Resin ( JEE App Server) and Hessian (Remoting Protocal) and now for Quercus.
I decided to look into this. I thought I would document what was done since the installation into an existing web app is really not documented anywhere on Caucho’s site.
What is Quercus, basically what Caucho did was write the a scripting engine for php in java, so they duplicated most if not all of the functionality in phps underlying C libraries. They claim this is much faster than in C and I cant speak to that, however there are blatant benefits to using Javas Connection Pooling which I can see how this would speed things up dramaitically. Basically with the Quercus jars installed on your classpath and the Quercus servlet enabled you can run php directly under you java web application and access java classes directly from php.
I had some issues initially with the conversion of java.math.BigDecimal but I put in a bug and they had it fixed within a week, which I thought was great.
To install Quercus:
I would pull the latest snapshot from subversion and build the jars, but you can also pull the war seperately and just copy the jars from inside of the web-inf lib there.
You can pull from subversion via this repository link and build the newest version of resin jars (mind you only need 3 jars).
svn://svn.caucho.com/resin/trunk
You can get the war from here:
You can just install the war and begin editing php scripts , but I’m more interested in using php in existing web apps we have. So I pulled in the quercus jars. Namely:
quercus.jar
script-10.jar
resin-util.jar
If you have an existing web application place all of these in the
WEB-INF/lib of your application. I installed these as third party jars in maven and listed them as dependencies. I had some problems with Windows locking the jars during a redeploy so I recommend putting these jars in $CATALINA_HOME/common/lib this way you can redeploy and not worry abou these files continuing to remain locked and blocking the undeploy during development.
The next thing is to enable the caucho php interpreting servlet, the example for this is in the quercus war.
<servlet>
<servlet-name>Quercus Servlet</servlet-name>
<servlet-class>com.caucho.quercus.servlet.QuercusServlet</servlet-class>
<!-- Specifies the encoding Quercus should use to read in PHP scripts.
Uncomment this if you're having "invalid utf-8" errors.
-->
<!--
<init-param>
<param-name>script-encoding</param-name>
<param-value>ISO-8859-1</param-value>
</init-param>
-->
<!-- Tells Quercus to use the following JDBC database and to ignore the
arguments of mysql_connect().
- From Sinner
Note this must be JNDI configured (see readme)
and wont work with CMS Spring Configured Non JNDI pool
this wont be an issue if all bus logic is done in spring anyways
but if you want to use php mysql libraries directly another connection
pool will need to be configured via JNDI
-->
<!--
<init-param>
<param-name>database</param-name>
<param-value>jdbc/test</param-value>
</init-param>
-->
<!--
<init-param>
<param-name>php-ini-file</param-name>
<param-value>WEB-INF/php.ini</param-value>
</init-param>
-->
</servlet>
<servlet-mapping>
<servlet-name>Quercus Servlet</servlet-name>
<url-pattern>*.php</url-pattern>
</servlet-mapping>
Once this is done put some php scripts under your web applications web root and access them and you’ll see that php is interpreted inside of the tomcat web container. Now what is really intersting about this is that now you can also access java classes from inside of the php scripts.
Below is an example of a php script accessing a Java Spring Managed Business Logic Bean and using several java classes. This opens a lot of possibilities and I’m somewhat concerned about how the code will be developed and if seperation of concerns (wrt MVC will be maintained) however it certainly allows for quick prototyping and the other benefits that go along with a Dynamic Language such as PHP.
See how the java imports are used and how java classes are accessed from the php script.
<?php
import java.lang.Double;
import com.cms.php.BigDecimalFactory;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import com.cms.shared.Utility;
import com.cms.shared.ext.firestorm.FirestormUtil;
import com.cms.shared.security.SecurityUser;
import com.cms.fuel.FuelManager;
import com.cms.fuel.exceptions.FuelException;
import com.cms.fuel.FuelmasterQueryParams;
import com.cms.shared.gen.firestorm.cmssql.dto.Fuelmaster;
import com.cms.shared.gen.firestorm.cmssql.dto.FuelmasterPriceComponent;
FirestormUtil::setChgProg($_SERVER['PHP_SELF']); //set for auditing
FirestormUtil::setChguser("???"); //need to set for auditing, have to get security worked out
//how to call a static method
$springApplicationContext = FirestormUtil::getSpringApplicationContext();
//get manager
$fuelManager = $springApplicationContext->getBean("fuelManager");
echo '<br/>var_dump($fuelManager)=';
var_dump($fuelManager);//displays toString or that of object if that is all there is
$companyId = "20";
$locationId = "SFB";
$category = "IR";
$trtype = "PUR";
$effdate= new Date();
$vendor = "005069";
$creditAccount = "130047";
$debitAccount = "150047";
$params = new FuelmasterQueryParams();
$params->setComp($companyId);
$params->setLocn($locationId);
$params->setCategory($category);
$params->setTrtype($trtype);
$params->setEffdate($effdate);
$params->setVendor($vendor);
$params->setFirstResult(0);
$params->setMaxResults(null);
$fuelmasterList = $fuelManager->getFuelmasters($params);
foreach ($fuelmasterList as $fuelmaster)
{
echo '<br/>$fuelmaster=';
echo $fuelmaster; //will display toString method of fuelmaster dto
//var_dump($fuelmaster->getRate());
}
//$double = Double::parseDouble(3.1);
//var_dump($double);
$bigDecimal = new BigDecimal(3.3);
echo '<br/>var_dump($bigDecimal)=';
var_dump($bigDecimal);
//insert fuel manager
try {
$dto = new Fuelmaster(); //Firestorm DTO
$dto->setComp($companyId);
$dto->setLocn($locationId);
$dto->setCategory($category);
$dto->setTrtype($trtype);
$dto->setEffdate($effdate);
$dto->setVendor($vendor);
$dto->setGlacct($creditAccount);
$dto->setGllocn($locationId);
$dto->setGloffset($debitAccount);
$dto->setGloflocn($locationId);
$dto->setRate($bigDecimal);
$dto->setIncident($bigDecimal);
$dto->setRatetype("U"); //Unit * Rate + Incident
$prices = new ArrayList();
//always include default price as first in the price list
$price = new FuelmasterPriceComponent();
$price->setFuelmasterRecId($dto->getRecId());
$price->setUnits(new BigDecimal(0));
$price->setRate($dto->getRate());
$price->setComments("DEFAULT PRICE");
$prices->add($price);
$fuelManager->insertFuelmaster($dto, $prices);
$newDto = $fuelManager->getFuelmaster($dto->getComp(),
$dto->getLocn(), $dto->getCategory(),
$dto->getTrtype(), $dto->getEffdate(),
$dto->getVendor()
);
echo '<br/>$newDto';
echo $newDto;
} catch (Exception $e) {
echo "Caught exception: ". $e->getMessage();
//echo "StackTrace: ". Utility::getStackTrace($e); //not working
}
?>
Re: Running PHP Scripts inside of Tomcat and accessing Java Classes via Caucho Quercus
Thanks for the great tips. I'm trying to get a hold of the 3 .jar files that you mentioned in your post. I'm not familiar with packing and unpacking things from war files. I looked at the SVN repository, but I don't see the jar files in there either. Can you tell me how I can get them to put in my existing web app?
Also, the section you wrote that has the <servlet> info, I'm I correct to place that in my web.xml file for my tomcat 6 application?
Thanks for posting this helpful article!
Cheers,
Courtenay
Re: Running PHP Scripts inside of Tomcat and accessing Java Classes via Caucho Quercus
Sorry I don't have an email server running on this machine yet.
Howwever hopefully you will see this when you look at this again.
To answer your questions:
1. Yes the servlet mapping goes in your web.xml file
2. What I did was pull down the resin project into eclipse using the url:
svn://svn.caucho.com/resin/trunk
Then I ran the ant build.xml with the default target ( you can do this from eclipse or from the command line)
This will create a /lib directory in your project and the jars above will be in that directory. Hope that helps!
The other option is to pull these jars out of the war file located at:
http://quercus.caucho.com/download/quercus-3.1.3.war
You can pull them out of the war file by opening the war with winzip and extracting those jars. However I would suggest pulling down the project since its got the BigDecimal fix in it.
Re: Running PHP Scripts inside of Tomcat and accessing Java Classes via Caucho Quercus
I'm hoping I haven't missed anything important. I just received the following error message in my browser:
type Exception report
message
description <u>The server encountered an internal error () that prevented it from fulfilling this request.</u>
exception
java.lang.IllegalStateException
org.apache.catalina.connector.ResponseFacade.sendRedirect(ResponseFacade.java:435)
com.caucho.quercus.lib.HttpModule.header(HttpModule.java:112)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
java.lang.reflect.Method.invoke(Unknown Source)
com.caucho.quercus.module.StaticFunction.invoke(StaticFunction.java:120)
com.caucho.quercus.env.JavaInvoker.call(JavaInvoker.java:615)
com.caucho.quercus.env.JavaInvoker.call(JavaInvoker.java:488)
com.caucho.quercus.env.JavaInvoker.call(JavaInvoker.java:473)
com.caucho.quercus.expr.FunctionExpr.evalImpl(FunctionExpr.java:182)
com.caucho.quercus.expr.FunctionExpr.eval(FunctionExpr.java:126)
com.caucho.quercus.program.ExprStatement.execute(ExprStatement.java:64)
com.caucho.quercus.program.BlockStatement.execute(BlockStatement.java:99)
com.caucho.quercus.program.QuercusProgram.execute(QuercusProgram.java:239)
com.caucho.quercus.page.InterpretedPage.execute(InterpretedPage.java:61)
com.caucho.quercus.page.QuercusPage.executeTop(QuercusPage.java:119)
com.caucho.quercus.servlet.QuercusServletImpl.service(QuercusServletImpl.java:163)
com.caucho.quercus.servlet.QuercusServlet.service(QuercusServlet.java:353)
javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
I updated my web.xml file by pasting in the info given above inside my <web-app> tags
and I used Eclipse to open the quercus war and copied the 3 files from
eclipse_workspace\quercus-3.1.3\WebContent\WEB-INF\lib
to
C:\apache-tomcat-6.0.14\lib I hope this is correct.
Thanks so much for assisting me on this!
Courtenay
Re: Running PHP Scripts inside of Tomcat and accessing Java Classes via Caucho Quercus
Re: Running PHP Scripts inside of Tomcat and accessing Java Classes via Caucho Quercus
Re: Running PHP Scripts inside of Tomcat and accessing Java Classes via Caucho Quercus
JAR files were uploaded to $TOMCAT_HOME/common/lib
Lines were added to $TOMCAT_HOME/$LIFERAY_HOME/WEB-INF/web.xml
- See http://pastebin.com/f36fc9578 for what was added
PHP.ini was uploaded to $TOMCAT_HOME/$LIFERAY_HOME/WEB-INF/
