<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html">
<TITLE>PHP/ Java Bridge FAQ</TITLE>
</HEAD>
<H1>PHP/Java Bridge FAQ</H1>
This file contains answers to frequently asked questions. Please see <a href="http://php-java-bridge.sourceforge.net/pjb">http://php-java-bridge.sourceforge.net</a> for more information.
<H2>General questions</H2>
<H4>Which alternatives exist?</H4>
<p>There are at least 4 other php to java bridges which offer a similar API. One was distributed with PHP 4.0.0 and has been discontinued. Two other bridges are proprietary software, built into a vendor-specific framework from Zend or a J2EE application server from Caucho.
Furthermore IBM's Zero framework for Eclipse contains a PHP interpreter written in pure Java and a php to java bridge which can call Java methods in-process.</p>
<p>The only free alternatives are SOAP and XML-RPC, which are up to 50 times slower than the XML protocol implementation used by this PHP/Java Bridge.</p>
<H4>Does it support PHP 4?</H4>
<p>No. Use PHP/Java Bridge version 4.3.2 or below instead.</p>
<H4>What about portability?</H4>
<p>A portable PHP application can use the "new Java(...)" constructor
and the "java(...)" function. To access a Java type use
<blockquote>
<code>
$System = java("java.lang.System");
</code>
</blockquote>
To create an instance of a Java type use
<blockquote>
<code>
$hashMap = new java("java.util.HashMap");
</code>
</blockquote>
</p>
<H4>Do I need a Java Application Server or Servlet Engine?</H4>
<p>Not necessarily. The bridge and the VM can run as a sub component
of IIS or Apache. If you have compiled and installed the support
library "java.so" or "php_java.dll", it can automatically start a VM when
the HTTP server activates the PHP script engine pool (in
<code>minit()</code>) and terminates it when the pool is destroyed (in
the <code>mshutdown()</code> hook). The request-handling threads
attach to the persistent VM using the PHP/Java Bridge protocol.
</p>
<p>
However, the main advantage of starting the VM via an external
application server or servlet engine is that it provides a standard
execution environment for Java which is independent of the HTTP server
and how it manages its internal PHP script engine pool.
</p>
<p>
A simple "servlet engine" emulation is built into the
JavaBridge.jar. It can be started by double-clicking on JavaBridge.jar
or with the command:
<blockquote>
<code>
java JVM-OPTIONS -jar JavaBridge.jar SOCKET LOG-LEVEL LOG-FILE
</code>
</blockquote>
Please see the output of <code>java -jar JavaBridge.jar --help</code>
and the INSTALL.STANDALONE document for details.
</p>
<H4>Ho do I enable logging?</H4>
<p>
Copy <code>log4j.jar</code> into <code>java.ext.dirs</code>. Example for JDK 6:
<blockquote>
<code>
cp log4j.jar /usr/java/packages/lib/ext
</code>
</blockquote>
</p>
<p>
Start the log4j viewer. Example for JDK 6:
<blockquote>
<code>
/opt/jdk1.6/bin/java org.apache.log4j.chainsaw.Main
</code>
</blockquote>
</p>
<p>
Start the application server with the options <code>-Dphp.java.bridge.default_log_level=LEVEL</code>. Example for tomcat:
<blockquote>
<code>
JAVA_HOME=/opt/jdk1.6 JAVA_OPTS="-Dphp.java.bridge.default_log_level=5" bin/catalina.sh run
</code>
</blockquote>
Example for the standalone container:
<blockquote>
<code>
java -Dphp.java.bridge.default_log_level=5 -jar JavaBridge.jar SERVLET:8080
</code>
</blockquote>
</p>
<H4>How can I configure the bridge?</H4>
<p>Useful options which can be set before the Java.inc/Mono.inc is loaded are:
<blockquote>
<code>
define ("JAVA_DEBUG", false);<br>
define ("JAVA_HOSTS", "127.0.0.1:8080");<br>
define ("JAVA_PIPE_DIR", null);<br>
require_once ("java/Java.inc");<br>
...
</code>
</blockquote>
Please see the Options.inc for details.
</p>
<p>Server-side options can be set in the
php.java.bridge.global.properties file, see the JavaBridge.jar zip
file (contained in the JavaBridge.war zip file) for details.
</p>
<H4>How do I start the bridge back end when there's another Java VM listening on port 8080?</H4>
<p>Simply deploy the PHP/Java Bridge web archive into the servlet engine or application server listening on port 8080. Or use a different port.</p>
<H4>What's the difference between SERVLET_LOCAL:8080 and INET_LOCAL:8080?</H4>
<p>The standalone option SERVLET_LOCAL emulates a servlet engine and starts a HTTP server which can select the fastest channel supported on this operating system. On Linux this is a named pipe created in /dev/shm or INET_LOCAL as a fall back.</p>
<p>INET_LOCAL always uses local TCP socket communication.</p>
<H4>Whenever I reboot my computer I have to start the bridge back end again. How can I automate this?</H4>
<p>Download and install a servlet engine or J2EE server, for example tomcat.</p>
<H4>Do I have to require Java.inc in each of my scripts? Isn't that very slow?</H4>
<p>The PHP/Java Bridge library <code>Java.inc</code> must be included before it can be used. Therefore the scripts should contain the statement
<blockquote>
<code>
require_once("...java/Java.inc");
</code>
</blockquote>
at the beginning of the script. PHP compiles and caches PHP scripts, the <code>Java.inc</code> library is loaded only once.
</p>
<H4>Can I use Java libraries without installing java?</H4> <p>Yes. On a GNU operating system (e.g.: GNU/Linux, GNU/windows (aka "cygwin"), ...) you can use the GCC compiler to compile Java classes to native code.
Simply compile the C based extension and omit the
<code>--with-java=</code> configure option. The bridge will use the
<code>libgcj</code> library, which is part of the GNU gcc compiler. This library also uses much less system
resources (memory, files) than a "real" Java VM.</p>
<H4>I get a blank page or some other error!?!</H4>
<p>
Check the PHP error log, see your <code>php.ini</code> file for details. If the command:
<blockquote>
<code>
echo '<?php require_once("http://localhost:8080/JavaBridge/java/Java.inc"); echo java("java.lang.System")->getProperties();?>' | php -n -d allow_url_include=On
</code>
</blockquote>
works in the shell but not within apache, then there's something wrong with your <code>php.ini</code> file.
</p>
<H4>How do I create a distributable PHP web application and how can users publish PHP/Java pages?</H4>
<p>
Check if the ISP supports Java. Look for a servlet engine like "tomcat" or a J2EE application server like "resin". Check if your ISP supports PHP >= 4.3.2.
</p>
<p>
In the following example we'll call our web context "webContext".
</p>
<p> Unzip the JavaBridge.jar file and edit Java.inc, replace 8080 with your j2EE port number and JAVA_SERVLET=On to JAVA_SERVLET=User. Example for Linux:
<blockquote>
<code>
mkdir webContext <br>
cd webContext <br>
jar xvf ../JavaBridge.war<br>
</code>
</blockquote>
Edit <code>java/Java.inc</code> as follows (lines marked with a "-" should be
removed, lines marked with a "+" should be added):
<blockquote>
<code>
if(!defineHostFromInitialQuery($JAVA_BASE)) {<br>
- define("JAVA_HOSTS", "127.0.0.1:8080");<br>
+ define("JAVA_HOSTS", "127.0.0.1:YOUR_J2EE_PORT_NUMBER");<br>
- define("JAVA_SERVLET", "On");<br>
+ define("JAVA_SERVLET", "User");<br>
}<br>
</code>
</blockquote>
</p>
<p>
Create a Java web archive, for example "webContext.war". Example for Linux:
<blockquote>
<code>
# ... still within the webContext directory ... <br>
cp ../myJavaLibs/*.jar WEB-INF/lib/ <br>
cp ../myPhpPages/*.php . <br>
jar cvf ../webContext.war *<br>
</code>
</blockquote>
Users can deploy it into their J2EE application server or servlet engine. Example for Linux:
<blockquote>
<code>
cp ../webContext.war /usr/share/tomcat5/webapps/<br>
</code>
</blockquote>
</p>
<p>
Then they need to copy or symlink the created web context "webContext" (or whatever you've called it) to their Apache or IIS htdocs (sometimes called "public_html") directory. Example for Linux:
<blockquote>
<code>
ln -s /usr/share/tomcat5/webapps/webContext $HOME/public_html/webContext<br>
</code>
</blockquote>
</p>
<p>
After restarting the HTTP server, they can
browse to <code>http://HOSTNAME/webContext/yourPhpScript.php</code>. Again, replace the web context "webContext" with the name of your web context.
</p>
<p>NOTE: If users want to run the Java VM on a different computer, they need to start
Tomcat or the J2EE server on that computer using the option <code>-Dphp.java.bridge.promiscuous=true</code>.
And change the <code>127.0.0.1</code> above to the IP address of the computer running
the Java VM and make sure that the computer cannot be accessed directly from the internet!
<H4>Can I use Python instead of PHP?</H4>
<p>Yes, see the examples folder from the source download.</p>
<H4>Can I access Mono or .NET libraries using the pure PHP implementation?</H4>
<p>Yes, there's a PHP library "Mono.inc" generated from "Java.inc" and
a "MonoBridge.exe" generated from "JavaBridge.jar". If you want to
compile from source, use the configure
option <code>--with-mono</code>.</p>
<H2>Class loading questions</H2>
<H4>How do I load Java libraries?</H4>
<p> With <code>java_require("foo.jar;bar.jar;...");</code>. See the README for details.</p>
<H4>Where can I store my java libraries for the PHP/Java Bridge?</H4>
<p>In <code>php.java.bridge.base</code>/lib.</p>
<p> When the system property <code>php.java.bridge.base</code> is not set, libraries are loaded from <code>$HOME/lib</code> where <code>$HOME</code> denotes the home directory of the person or component which has started the java VM.</p>
<p> If libraries should be available globally, store them in <code>java.ext.dirs</code>, for example in <code>/usr/share/java/ext</code>.</p>
<p> If a library is not yet API-stable, store it into a sub directory of the <code>lib</code> directory and use the <code>java_require()</code> procedure. For example <code>java_require("myLibs0.2/foo.jar")</code> loads <code>foo.jar</code> from <code>php.java.bridge.base/lib/myLibs0.2</code>.</p>
<H4>Why can't Apache load my /foo/bar/baz.jar file?</H4>
<p>It probably doesn't have the permission to access it. Check if
baz.jar is a valid Java archive and if its main class is public. Try
to disable Security Enhanced Linux and store the jar file into a
folder accessible by the apache user and then extract the required
Security Enhanced Linux permissions from the audit log.</p>
<H4>Why do I get a ClassNotFoundException?</H4>
<p>You probably haven't required the relevant Java library. Or the
class doesn't exist or it is not public or it throws a java.lang.Error
during initialization. Check which library exports the feature and add
the library to the <code>java_require()</code> statement.</code></p>
<H4>Why do I get a NoClassDefFoundError?</H4>
<p>Because Java doesn't have a module system. All interconnected libraries must be loaded with a single <code>java_require()</code> call. See the README for details.</p>
<H4>I cannot require my JDBC driver!?!</H4>
<p>The <code>java_require()</code> uses the current class loader, not the bootstrap loader. Use:
<blockquote>
<code>
java_require("foo.jar");<br>
...<br>
Class.forName("foo",true,Thread.currentThread().getContextClassLoader());<br>
</code>
</blockquote>
instead.
</p>
<H4>How do I load impure Java libraries?</H4>
<p>You can't. Java libraries must be pure Java.</p>
<p> Please read the
documentation of your J2EE server, Servlet engine or Java VM to see if and
how the environment can handle impure Java libraries. A common approach is to store the Java part in <code>java.ext.dirs</code> and the native part in <code>java.library.path</code>.</p>
<H2>J2EE/Servlet questions</H2>
<a name="load-balancing">
<H4>How do I set up a load balancer for the PHP/Java Bridge cluster?</H4>
<p>
Set up the PHP/Java Bridge cluster as <a href="#cluster">described below</a>. The example uses two nodes named "carlos" and "diego". The HTTP server front end runs on the web server "timon".
</p>
<p>Install Apache 2.2.0 or higher.</p>
<p>Enable <code>proxy_module</code> and <code>proxy_balancer_module</code>. The following example is for Linux (lines marked with "+" should be added to the <code>conf/httpd.conf</code> file):
<blockquote>
<code>
LoadModule rewrite_module modules/mod_rewrite.so<br>
+ LoadModule proxy_module modules/mod_proxy.so<br>
+ LoadModule proxy_balancer_module modules/mod_proxy_balancer.so<br>
LoadModule cache_module modules/mod_cache.so<br>
</code>
</blockquote>
</p>
<p>
Add the following code to the bottom of your <code>conf/httpd.conf</code> file:
<blockquote>
<code>
ProxyPass /JavaBridge balancer://mycluster maxattempts=2<br>
<Proxy balancer://mycluster><br>
BalancerMember http://diego:8080/JavaBridge<br>
BalancerMember http://carlos:8080/JavaBridge<br>
</Proxy><br>
<Location /balancer-manager><br>
SetHandler balancer-manager<br>
Deny from all<br>
Allow from 127.0.0.1<br>
</Location><br>
</code>
</blockquote>
</p>
<p>Start the cluster nodes on "carlos" and "diego".</p>
<p>Browse to <code>http://timon/JavaBridge/</code> (note the trailing slash) and click on the test.php. Click on refresh. Check if both nodes respond.</p>
<p>Browse to <code>http://timon/JavaBridge/sessionSharing.php</code> and click on refresh. Check the cookie value.</p>
<p>Browse to <code>http://timon/balancer-manager/</code>.
<p>Please see the <code>mod_rewrite</code> documentation for more information how to rewrite incoming URLs.</p>
</a>
<a name="cluster">
<H4>Ho do I set up a tomcat cluster?</H4>
<p>
Download tomcat 5 or higher, a Java JRE 5 or higher and the PHP/Java Bridge 4.1.6 or higher.
</p>
<p>
The following example uses two nodes running on two machines called
"carlos" and "diego". The web server is running on a third machine
which has full access to both nodes (at least it needs access to the
ports from the range [9267, [9367).
</p>
<p>
On all nodes: Extract the tomcat distribution into a directory.
</p>
<p>On <strong>"diego"</strong> add the following to <code>conf/server.xml</code>:
<blockquote>
<code>
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"<br>
channelSendOptions="8"><br>
<Manager className="org.apache.catalina.ha.session.DeltaManager"<br>
expireSessionsOnShutdown="false"<br>
notifyListenersOnReplication="true"/><br>
<Channel className="org.apache.catalina.tribes.group.GroupChannel"><br>
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"<br>
address="<strong>diego</strong>"<br>
port="4000"<br>
autoBind="100"<br>
selectorTimeout="5000"<br>
maxThreads="6"/><br>
</Channel><br>
</Cluster><br>
</code>
</blockquote>
</p>
<p>On <strong>"carlos"</strong> add the following to <code>conf/server.xml</code>:
<blockquote>
<code>
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"<br>
channelSendOptions="8"><br>
<Manager className="org.apache.catalina.ha.session.DeltaManager"<br>
expireSessionsOnShutdown="false"<br>
notifyListenersOnReplication="true"/><br>
<Channel className="org.apache.catalina.tribes.group.GroupChannel"><br>
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"<br>
address="<strong>carlos</strong>"<br>
port="4000"<br>
autoBind="100"<br>
selectorTimeout="5000"<br>
maxThreads="6"/><br>
</Channel><br>
</Cluster><br>
</code>
</blockquote>
</p>
<p>Copy the "JavaBridge.war" into the "webapps" directory on "diego" and "carlos" and start both nodes. For example with the command:
<blockquote>
<code>
JAVA_OPTS="-Dphp.java.bridge.promiscuous=true" JAVA_HOME=/opt/jdk1.5 bin/catalina.sh run
</code>
</blockquote>
</p>
<p>Start the web server on the third computer "timon". For example with the command:
<blockquote>
<code>
apachectl restart
</code>
</blockquote>
</p>
<p>Create a PHP script called "sessionSharing-diego.php" and copy it into the web server document root (usually "/var/www/html" or "~/public_html") on "timon":
<blockquote>
<code>
<?php<br>
require_once("http://diego:8080/JavaBridge/java/Java.inc"); // change to "java/Java.inc" if you use a load balancer<br>
$session = java_session();<br>
if(is_null($session->get("counter"))) <br>
$session->put("counter", new Java("java.lang.Integer", 1));<br>
<br>
$counter = java_values($session->get("counter"));<br>
print "HttpSession variable \"counter\": $counter<br>\n";<br>
$next = new Java("java.lang.Integer", $counter+1);<br>
$session->put("counter", $next);<br>
?><br>
</code>
</blockquote>
</p>
<p>Create a PHP script called "sessionSharing-carlos.php" and copy it into the web server document root (usually "/var/www/html" or "~/public_html") on "timon":
<blockquote>
<code>
<?php<br>
require_once("http://carlos:8080/JavaBridge/java/Java.inc"); // change to "java/Java.inc" if you use a load balancer<br>
$session = java_session();<br>
if(is_null($session->get("counter"))) <br>
$session->put("counter", new Java("java.lang.Integer", 1));<br>
<br>
$counter = java_values($session->get("counter"));<br>
print "HttpSession variable \"counter\": $counter<br>\n";<br>
$next = new Java("java.lang.Integer", $counter+1);<br>
$session->put("counter", $next);<br>
?><br>
</code>
</blockquote>
</p>
<p>Start a web brower and remove all cookies</p>
<p>
Browse to <code>http://timon/sessionSharing-carlos.php</code> and check the generated cookie. It should have the key "JSESSIONID", host attribute "timon" and the path attribute "/". The value should be "1". Click on the browser refresh button to increase the value.
</p>
<p>
Use the same browser window and navigate to <code>http://timon/sessionSharing-diego.php</code> and check if there is still only one cookie with the same attributes as before. The value should be "3"
</p>
<p>
Use the same browser window and navigate to <code>http://timon/sessionSharing-carlos.php</code> and check if there is still only one cookie with the same attributes as before. The value should be "4".
</p>
<p>
Switch off "diego" and start it again.
</p>
<p>
Use the same browser window and navigate to <code>http://timon/sessionSharing-diego.php</code> and check if there is still only one cookie with the same attributes as before. The value should be "5"
</p>
<p>
Switch off "carlos".
</p>
<p>
Use the same browser window and navigate to <code>http://timon/sessionSharing-carlos.php</code> and check if you get an exception.
</p>
<p>
Start "carlos".
</p>
<p>
Use the same browser window and navigate to <code>http://timon/sessionSharing-carlos.php</code> and check if there is still only one cookie with the same attributes as before. The value should be "6"
</p>
<p>
Please see your HTTP server documentation and <a href="#load-balancing">the description above</a> how to set it up as a load balancer. This differs from the architecture described above in that the load balancer forwards the requests to the nodes and the nodes run PHP using the FastCGI mechanism.
</p>
</a>
<a name="global-servlet">
<H4>I want to use PHP for all tomcat applications. Apache and IIS are not available, but performance is important. How do I install it?</H4>
</a>
<p> Check if your PHP cgi binary supports the <code>-b</code> flag. If not, compile PHP with the <code>--enable-fastcgi</code> option </p>
<p>
Download and install the J2EE binary: copy <code>JavaBridge.war</code> into the tomcat <code>webapps</code> folder.
</p>
<p>
Copy the <code>JavaBridge.jar</code> and the <code>php-servlet.jar</code> from the JavaBridge.war into the tomcat <code>shared/lib</code> folder. Uncomment the <code>shared_fast_cgi_pool</code> parameter in the <code>JavaBridge/WEB-INF/web.xml</code> and add the lines marked with a <code>+</code> to the tomcat <code>conf/web.xml</code>:
<blockquote>
<code>
<br>
<!-- ================== Built In Servlet Definitions ==================== --><br>
<br>
+ <!-- PHP Servlet --><br>
+ <servlet><br>
+ <servlet-name>GlobalPhpJavaServlet</servlet-name><br>
+ <servlet-class>php.java.servlet.PhpJavaServlet</servlet-class><br>
+ </servlet><br>
+ <!-- PHP CGI Servlet --><br>
+ <servlet><br>
+ <servlet-name>GlobalPhpCGIServlet</servlet-name><br>
+ <servlet-class>php.java.servlet.PhpCGIServlet</servlet-class><br>
+ <init-param><br>
+ <!-- Remember to set the shared_fast_cgi_pool option --><br>
+ <!-- in JavaBridge/WEB-INF/web.xml to On, too. --><br>
+ <param-name>shared_fast_cgi_pool</param-name><br>
+ <param-value>On</param-value><br>
+ </init-param><br>
+ </servlet><br>
<br>
<!-- The default servlet for all web applications, that serves static --><br>
<!-- resources. It processes all requests that are not mapped to other --><br>
[...]<br>
<!-- ================ Built In Servlet Mappings ========================= --><br>
<br>
+ <!-- PHP Servlet Mapping --><br>
+ <servlet-mapping><br>
+ <servlet-name>GlobalPhpJavaServlet</servlet-name><br>
+ <url-pattern>*.phpjavabridge</url-pattern><br>
+ </servlet-mapping><br>
+ <!-- CGI Servlet Mapping --><br>
+ <servlet-mapping><br>
+ <servlet-name>GlobalPhpCGIServlet</servlet-name><br>
+ <url-pattern>*.php</url-pattern><br>
+ </servlet-mapping><br>
<br>
<!-- The servlet mappings for the built in servlets defined above. Note --><br>
<!-- that, by default, the CGI and SSI servlets are *not* mapped. You --><br>
<br>
</web-app><br>
</code>
</blockquote>
</p>
To test the above settings create a directory <code>testapp</code> and copy the <code>test.php</code> file from the <code>JavaBridge.war</code> into this folder. Type <code>cd testapp; jar cf ../testapp.war *</code> and copy the testapp.war into the tomcat <code>webapps</code> folder.
Restart tomcat, browse to
<code>http://yourHost.com:8080/testapp/test.php</code>.
</p>
<p>
Check if the PHP Fast-CGI server is running. The process list should display 5 (default) PHP instances waiting in the PHP Fast-CGI pool. If not, check if your PHP binary has been compiled with Fast-CGI enabled. Copy a Fast-CGI enabled binary into the <code>webapps/JavaBridge/WEB-INF/cgi/</code> folder, if necessary.
</p>
<a name="php-web-app">
<H4>How do I create a standalone PHP web application for distribution and how can users deploy it into tomcat?</H4>
</a>
<p>
Create a directory <code>myApplication</code>, create the directories <code>myApplication/WEB-INF/lib/</code> and <code>myApplication/WEB-INF/cgi/</code>.
Download the J2EE binary and copy the <code>JavaBridge.jar</code> and the <code>php-servlet.jar</code> from the JavaBridge.war to the <code>myApplication/WEB-INF/lib/</code> folder. Copy the contents of the <code>cgi</code> folder to <code>myApplication/WEB-INF/cgi/</code>. Create the file <code>myApplication/WEB-INF/web.xml</code> with the following content:
<blockquote>
<code>
<br>
<web-app><br>
<!-- PHP Servlet --><br>
<servlet><br>
<servlet-name>PhpJavaServlet</servlet-name><br>
<servlet-class>php.java.servlet.PhpJavaServlet</servlet-class><br>
</servlet><br>
<!-- PHP CGI processing servlet, used when Apache/IIS are not available --><br>
<servlet><br>
<servlet-name>PhpCGIServlet</servlet-name><br>
<servlet-class>php.java.servlet.PhpCGIServlet</servlet-class><br>
</servlet><br>
<br>
<!-- PHP Servlet Mapping --><br>
<servlet-mapping><br>
<servlet-name>PhpJavaServlet</servlet-name><br>
<url-pattern>*.phpjavabridge</url-pattern><br>
</servlet-mapping><br>
<!--PHP CGI Servlet Mapping --><br>
<servlet-mapping><br>
<servlet-name>PhpCGIServlet</servlet-name><br>
<url-pattern>*.php</url-pattern><br>
</servlet-mapping><br>
<br>
<!-- Welcome files --><br>
<welcome-file-list><br>
<welcome-file>index.php</welcome-file><br>
</welcome-file-list><br>
</web-app><br>
</code>
</blockquote>
</p>
<p>
Copy the files <code>sessionSharing.jsp</code> and <code>sessionSharing.php</code> from the <code>JavaBridge.war</code> to <code>myApplication</code> and create <code>myApplication.war</code>, for example with the commands: <code>cd myApplication; jar cf ../myApplication.war *</code>. </p>
<p>The web archive can now be distributed, copy it to the tomcat <code>webapps</code> directory and re-start tomcat. Visit <code>http://localhost/myApplication/sessionSharing.php</code> and <code>http://localhost/myApplication/sessionSharing.jsp</code>.
</p>
<a name="http-front-end">
<H4>I want to use Apache/IIS as a front-end and tomcat as a back end. How do I enable PHP for all my applications?</H4>
</a>
<p>
Download and install the J2EE binary: copy <code>JavaBridge.war</code> into the tomcat <code>webapps</code> folder.
Check if the tomcat <code>webapps</code> directory is shared with the Apache/IIS <code>htdocs</code> directory. If not, change the Apache/IIS setting, the following example is for Apache 2. Edit e.g. <code>/etc/httpd/conf/httpd.conf</code> as follows:
<blockquote>
<code>
# documents. By default, all requests are taken from this directory, but<br>
# symbolic links and aliases may be used to point to other locations.<br>
#<br>
-DocumentRoot "/var/www/html"<br>
+DocumentRoot "/var/lib/tomcat5/webapps"<br>
<br>
#<br>
# Each directory to which Apache has access can be configured with respect<br>
#<br>
# This should be changed to whatever you set DocumentRoot to.<br>
#<br>
-<Directory "/var/www/html"><br>
+<Directory "/var/lib/tomcat5/webapps"><br>
</code>
</blockquote>
</p>
<p>
Edit the <code>java.host</code> and <code>java.servlet</code> options in your <code>Java.inc</code> or, if you use the C implementation, the <code>php.ini</code>:
<blockquote>
<code>
[java]<br>
java.hosts = 127.0.0.1:8080<br>
java.servlet = On<br>
</code>
</blockquote>
</p>
To test the above settings create a directory <code>testapp</code> and copy the <code>sessionSharing.php</code> file from the <code>JavaBridge.war</code> into this folder. Type <code>cd testapp; jar cf ../testapp.war *</code> and copy the testapp.war into the tomcat <code>webapps</code> folder.
Restart Apache or IIS and tomcat, browse to
<code>http://localhost/testapp</code>, click on sessionSharing.php and check the generated cookie value. The <code>path</code> value must be <code>/</code>.
</p>
<a name="php-jsp-session-sharing">
<H4>I want to use Apache/IIS as a front-end and tomcat as a back end. How do I enable PHP and JSP for all my applications?</H4>
</a>
<p>
Download the J2EE binary and copy the <code>JavaBridge.jar</code> and the <code>php-servlet.jar</code> from the JavaBridge.war into the tomcat <code>shared/lib</code> folder. Add the lines marked with a <code>+</code> to the tomcat <code>conf/web.xml</code>:
<blockquote>
<code>
<br>
<!-- ================== Built In Servlet Definitions ==================== --><br>
<br>
+ <!-- PHP Servlet --><br>
+ <servlet><br>
+ <servlet-name>GlobalPhpJavaServlet</servlet-name><br>
+ <servlet-class>php.java.servlet.PhpJavaServlet</servlet-class><br>
+ </servlet><br>
<br>
<!-- The default servlet for all web applications, that serves static --><br>
<!-- resources. It processes all requests that are not mapped to other --><br>
[...]<br>
<!-- ================ Built In Servlet Mappings ========================= --><br>
<br>
+ <!-- PHP Servlet Mapping --><br>
+ <servlet-mapping><br>
+ <servlet-name>GlobalPhpJavaServlet</servlet-name><br>
+ <url-pattern>*.phpjavabridge</url-pattern><br>
+ </servlet-mapping><br>
<br>
<!-- The servlet mappings for the built in servlets defined above. Note --><br>
<!-- that, by default, the CGI and SSI servlets are *not* mapped. You --><br>
<br>
</web-app><br>
</code>
</blockquote>
</p>
<p>
Check if the tomcat <code>webapps</code> directory is shared with the Apache/IIS <code>htdocs</code> directory. If not, change the Apache/IIS setting, the following example is for Apache 2. Edit e.g. <code>/etc/httpd/conf/httpd.conf</code> as follows:
<blockquote>
<code>
# documents. By default, all requests are taken from this directory, but<br>
# symbolic links and aliases may be used to point to other locations.<br>
#<br>
-DocumentRoot "/var/www/html"<br>
+DocumentRoot "/var/lib/tomcat5/webapps"<br>
<br>
#<br>
# Each directory to which Apache has access can be configured with respect<br>
#<br>
# This should be changed to whatever you set DocumentRoot to.<br>
#<br>
-<Directory "/var/www/html"><br>
+<Directory "/var/lib/tomcat5/webapps"><br>
</code>
</blockquote>
</p>
<p>
Now that tomcat knows how to handle PHP <code>.phpjavabridge</code> requests and Apache or IIS can access the tomcat webapps, connect the two components: Edit the <code>java.host</code> and <code>java.servlet</code> options in your <code>Java.inc</code> or, if you use the C implementation, the <code>php.ini</code>:
<blockquote>
<code>
[java]<br>
java.hosts = 127.0.0.1:8080<br>
java.servlet = User<br>
</code>
</blockquote>
The above <code>User</code> setting enables session sharing between PHP and JSP, it forwards from <code>http://host<em>/myApp/foo.php</em></code> to the tomcat back end at <code>127.0.0.1:8080</code> using the request <code>PUT <em>/myApp/foo.php</em>javabridge</code>. This triggers the <code>GlobalPhpJavaServlet</code> configured in the tomcat <code>web.xml</code>.
</p>
<p>
Now you need to do the same for JSP. Unlike the PHP/Java Bridge, which only forwards embedded java statements, the tomcat <code>mod_jk</code> adapter must forward all JSP requests.
Download and install <code>mod jk</code>, for example
<code>jakarta-tomcat-connectors-1.2.14.1-src.tar.gz</code>, extract the file into a
folder and type the following commands:
<blockquote>
<code>
cd jakarta-tomcat-connectors-1.2.14.1-src/jk/native/<br>
./configure --with-apxs && make && su -c "make install"<br>
</code>
</blockquote>
Add the following lines to the end of the <code>httpd.conf</code>, the following example is for Apache 2:
<blockquote>
<code>
LoadModule jk_module modules/mod_jk.so<br>
JkAutoAlias /var/lib/tomcat5/webapps<br>
JkMount *.jsp ajp13<br>
</code>
</blockquote>
</p>
To test the above settings create a directory <code>testapp</code> and copy the <code>sessionSharing.php</code> and <code>sessionSharing.jsp</code> from the <code>JavaBridge.war</code> into this folder. Type <code>cd testapp; jar cf ../testapp.war *</code> and copy the testapp.war into the tomcat <code>webapps</code> folder.
Restart Apache or IIS and tomcat, browse to
<code>http://localhost/testapp</code>, click on sessionSharing.php and check the generated cookie value. The <code>path</code> value must be <code>/testapp</code>. Click on sessionSharing.jsp.
</p>
<H4>Does the
bridge run native code within my servlet engine or application
server?</H4> <p>No. The bridge back end is written in pure java, it
doesn't use any native code. Native PHP runs within Apache, IIS, a
FCGI server or via CGI. If the PHP instance crashes, an error page is
returned to the client and the Apache, IIS, CGI container usually starts a new PHP instance for the next
request.</p>
<a name="ssl">
<H4>On Windows some PHP binaries do not support HTTPS/SSL</H4>
</a>
<p>
If you see the message:
<blockquote>
<code>
Warning: fsockopen() [function.fsockopen]: unable to connect to ssl://127.0.0.1:8443
(Unable to find the socket transport "ssl" - did you forget to enable it when you configured PHP?)
</code>
</blockquote>
this means that PHP cannot connect back to the official SSL port.
Please check the
"Registered Stream Socket Transports" from the <code>phpinfo()</code> (see the <code>test.php</code> page), it should display: <code>tcp, udp, ssl, sslv3, sslv2, tls</code>. If not, please recompile PHP with SSL enabled, use the flag <code>--with-openssl</code>.
<p>
A workaround is to use the official non-SSL port or to open a
dedicated local port for the PHP-Java communication. The following
example is for Tomcat:
<p>
<ol>
Disable <code>override_hosts</code> in the web application <code>WEB-INF/web.xml</code>:
<blockquote>
<code>
<init-param><br>
<param-name>override_hosts</param-name><br>
<param-value>Off</param-value><br>
</init-param><br>
</code>
</blockquote>
Open a local port in the tomcat <code>conf/server.xml</code> (only necessary if you have disabled the official non-SSL port):
<blockquote>
<code>
<Service name="Catalina"><br>
[...]<br>
<Connector port="9157" address="127.0.0.1" /><br>
[...]<br>
</Service><br>
</code>
</blockquote>
Set the communication port in the PHP <code>.ini</code> (the <code>phpinfo()</code> or <code>test.php</code> displays the location of the responsible <code>.ini</code> file):
<blockquote>
<code>
[java]<br>
java.hosts = 127.0.0.1:9157<br>
java.servlet = User<br>
</code>
</blockquote>
</ol>
If you want to use the official non-SSL port (usually port number
8080), change the <code>java.hosts</code> line accordingly.<p>
Restart the application server or servlet engine. Check the settings by running <code>phpinfo()</code> or by visiting the <code>test.php</code> page.
</p>
<H4>The EJB example works with the Sun J2EE server, but in JBoss I get a ClassCastException, what's wrong?</H4>
<p>It's a JBoss problem, although this problem may also appear in other application servers which do not strictly separate the application/bean domains. The JavaBridge.war already contains the <code>documentClient.jar</code> as a library, so JBoss references the library classes instead of the bean classes. Just remove the <code>documentClient.jar</code> from the <code>JavaBridge.war</code>, re-deploy <code>JavaBridge.war</code> and run the test again.
</p><p>
In JBoss' default setup the code:
<blockquote>
<code>
// access the home interface<br>
$DocumentHome = new JavaClass("DocumentHome");<br>
$PortableRemoteObject = new JavaClass("javax.rmi.PortableRemoteObject");<br>
$home=$PortableRemoteObject->narrow($objref, $DocumentHome);<br>
</code>
</blockquote>
refences the <code>DocumentHome</code> from the library, which is assignment-incompatible to <code>DocumentHome</code> from the enterprise bean (<code>DocumentHome@WebAppClassLoader</code> != <code>DocumentHome@BeanClassLoader</code>), so you get a ClassCastException in <code>narrow</code>.
</p>
<p>
In contrast the Sun J2EE server correctly separates the beans/applications; the <code>$objref</code> is a unique proxy generated by a parent of the <code>WebAppClassLoader</code>, so that <code>narrow</code> can always cast the proxy to <code>DocumentHome@WebAppClassLoader</code>, even if a class with the same name is already available from the <code>WebAppClassLoader</code>.</p>
<H4>How do I install PHP into the Nutch, Spring, JSF, ..., Framework?</H4>
<p>By providing JSR 223 based PHP beans and a description how to manage them, as usual. The code
<blockquote>
<code>
javax.script.ScriptEngine e = <br>
php.java.script.EngineFactory.getInvocablePhpScriptEngine (this, <br>
application, <br>
request, response);<br>
</code>
</blockquote>
can be used to access the JSR 223 ScriptEngine from the framework, provided that a listener has been configured in the WEB-INF/web.xml:
<blockquote>
<code>
<listener>
<listener-class>php.java.servlet.ContextLoaderListener</listener-class>
</listener>
</code>
</blockquote>
</p>
<H2>General runtime questions</H2>
<H4>How do I reference a class w/o creating an instance?</H4>
<p>
With the <code>java</code> function, for example: <code>java("java.lang.System")</code>.
</p>
<p>The function is defined in <code>http://localhost:8080/JavaBridge/java/Java.inc</code> as:
<blockquote>
<code>
function java($clazz) {<br>
static $classMap = array();<br>
if(array_key_exists($clazz, $classMap)) return $classMap[$clazz];<br>
return classMap[$clazz]=new JavaClass($clazz);<br>
}
</code>
</blockquote>
</p>
<H4>Why does java_context()->getHttpServletRequest()->getSession() return null?</H4>
<p>
PHP scripts must explicitly allocate a session with <code><a href="documentation/PHP-API/html/java_8c.html#doc25">java_session()</a></code>. For example:
<blockquote>
<code>
java_session(); <br>
// now the (Remote-)HttpServletRequest knows about the session: <br>
echo java_context()->getHttpServletRequest()->getSession(); <br>
</code>
</blockquote>
</p>
<H4>Where is my output?</H4> <p><code>System.out</code> and
<code>System.err</code> are redirected to the server log file(s). When
PHP scripts are invoked from a java framework (Java Server Faces for
example), even the PHP output is redirected. For the standalone back
end the output appears in the
<code>/var/log/php-java-bridge.log</code> or in VMBridge.log, see
.ini option <code>java.log_file</code>. For the j2ee back end the
location of the log file(s) depends on the j2ee server
configuration.</p>
<H4>How do I make my script state (objects or variables) persistent?</H4>
<p>If you must code it yourself: with
e.g. <code>java_session()->put("buf", $stringBuffer)</code> or via
<code>$_SESSION["buf"]=$stringBuffer</code>. The
<code>$_SESSION</code> is syntactic sugar provided by the <a
href="http://www.php.net/manual/en/ref.session.php">php session
module</a>, it uses <code><a
href="documentation/PHP-API/html/java_8c.html#doc25">java_session()<a></code>
internaly. If you don't want depend on the PHP session module, for
example if you have compiled PHP without the <code>session.so</code>,
use java_session() instead.</p>
<H4>How many threads does the bridge start?</H4>
<p>Request-handling threads are started
from a thread pool, which limits the number of user requests to 20
(default), see system property
<code>php.java.bridge.threads</code>. All further requests have to
wait until one of the worker threads returns to the pool. </p>
<p>When running in a servlet engine, a <a
href="server/documentation/API/php/java/bridge/http/ContextServer.html">ContextServer</a>
is started which handles the pipe or local socket communication
channel. </p> <p>When java invokes local scripts outside of a HTTP
environment, the bridge starts a <a
href="server/documentation/API/php/java/bridge/http/HttpServer.html">HttpServer</a>,
a <a
href="server/documentation/API/php/java/bridge/http/ContextServer.html">ContextServer</a>
and a <a
href="server/documentation/API/php/java/script/HttpProxy.html">HttpProxy</a>. The
HttpProxy represents the PHP continuation and the HttpServer the
request-handling java continuation associated with the JSR223 script.
</p>
<H4>How do I access enums or inner classes?</H4>
With the <code>classname$inner</code> syntax. For example <br><br>
<code>
public interface php {<br>
public class java {<br>
public enum bridge {JavaBridge, JavaBridgeRunner};<br>
}<br>
}<br>
</code><br>
can be accessed with:<br><br>
<code>
<?php<br>
java_require(getcwd()); // load php.class<br>
$bridge = new java('php$java$bridge');<br>
echo $bridge->JavaBridgeRunner;<br>
?><br>
</code><br>
The above code is not a good programming example but it demonstrates why a different syntax is used to access inner classes.
</p>
<H4>How do I create a primitive array?</H4>
<p>
Primitive types are wrapped by associated java classes.
The following example uses <code>reflect.Array</code> to create a new <code>byte</code> array:<br><br>
<code>
$Byte = new JavaClass("java.lang.Byte");<br>
$byte = $Byte->TYPE;<br>
$Array = new JavaClass("java.lang.reflect.Array");<br>
$byteArray = $Array->newInstance($byte, 255);<br>
$System = new JavaClass("java.lang.System");<br>
$length = $System->in->read($byteArray);<br>
$str = new Java("java.lang.String", $byteArray, 0, $length);<br>
echo "You have typed: $str\n";<br>
</code>
</p>
<H4>How fast is it?</H4> <p>
The following scripts were
executed on one 1.688 GHZ x86 cpu running RedHat Fedora Core 4 Linux and Sun jdk1.6.0_02:
</p>
The PHP 5.2.2 code<br>
<p>
<code>
<?php<br>
java_begin_document();<br>
$buf=new java("java.lang.StringBuffer");<br>
<br>
$i=0;<br>
while($i<400000) {<br>
$i=$i+1;<br>
$buf->append($i);<br>
}<br>
<br>
print $buf->length() . "\n";<br>
java_end_document();<br>
?><br>
</code>
</p>
The ECMAScript ("Mozilla Rhino") code<br>
<p>
<code>
buf = new java.lang.StringBuffer();<br>
for(i=0; i<400000; i++) buf.append(new String(i));<br>
print (buf.toString().length());<br>
</code>
</p>
<p>
<center>
<TABLE
BORDER="1"
><COL><COL><COL><COL><THEAD
><TR
><TH
>Command</TH
><TH
>Script Engine</TH
><TH
>Communication Channel</TH
><TH
>Execution time (real, user, sys)</TH
></TR
></THEAD
<TBODY
><TR
><TD
>time jrunscript -l php t11.php</TD
><TD
>PHP5 + PHP/Java Bridge 4.3.3</TD
><TD
>named pipes</TD
><TD
>0m18.703s,<br>
0m16.209s,<br>
0m0.138s
</TD
></TR
>
<TBODY
><TR
><TD
>time jrunscript -l js t11.js</TD
><TD
>ECMA script</TD
><TD
>none (native code)</TD
><TD
>0m15.338s,<br>
0m14.996s,<br>
0m0.116s
</TD
></TR
>
</TABLE
>
</center>
</p>
<p>
</p>
<H4>How does the bridge handle OutOfMemoryErrors?</H4>
<p>
OutOfMemoryErrors may happen because a cached object cannot be released, either because <p>
<ol>
<li> the object is permanently referenced by a request-handling thread or</li>
<li> the object has been entered into the session or application store or
the object is referenced by a thread outside of the scope of the PHP/Java Bridge.</li>
</ol>
</p>
</p>
<p>When a <code>java.lang.OutOfMemoryError</code> reaches the request-handling thread, the PHP/Java Bridge thread pool removes the thread from its pool and writes a message <code>FATAL: OutOfMemoryError</code> to the PHP/Java Bridge log file. The session store is cleaned and all client connections are terminated without confirmation.
</p>
<p>
If the OutOfMemoryError persists, this means that a thread outside of the PHP/Java Bridge has caused this error condition.
</p>
<p>
The following code example could cause an OutOfMemoryError:<br><br>
<code>
<?php<br>
session_start();<br>
if(!$_SESSION["buffer"]) $_SESSION["buffer"]=new java("java.lang.StringBuffer");<br>
$_SESSION["buffer"]->append(...);<br>
?>
</code>
</p>
<p>
OutOfMemory conditions can be debugged by running the back end with e.g.:<br><br>
<code>
java -agentlib:hprof=heap=sites -jar JavaBridge.jar<br>
</code>
</p>
<H4>How can PHP classes extend Java classes and Java methods?</H4>
<p>
By using <a href="documentation/PHP-API/html/java_8c.html#doc32"><code>java_closure()</code></a> and the visitor pattern for example. The <code>tests.php5</code> folder contains a <code>script_api.php</code> example which shows how to implement <code>java.lang.Runnable</code> to run multiple PHP threads, concurrently accessing a shared resource.
</p>
<H4>How can I execute PHP code on the server?</H4>
<p>
With <code>java_begin_document()/java_end_document()</code>. For example:
<blockquote>
<code>
java_begin_document();<br>
$s = new Java("java.lang.StringBuffer");<br>
for($i=0; $i<10000; $i++) $s->append($i);<br>
java_end_document();<br>
</code>
</blockquote>
The above code sends the PHP code as an XML image to the server and executes it there.
</p>
<H4>How can I convert a Java object into a PHP value?</H4>
<p>
With <code>java_values()</code>. For example:
<blockquote>
<code>
$s = new Java("java.lang.String");<br>
$c = $chr = $s->toCharArray();<br>
print (java_values($s));<br>
print_r(java_values($c));<br>
</code>
</blockquote>
</p>
<H4> How can I convert a PHP object into a Java object?</H4>
<p>
With <code>java_closure()</code>. For example:
<blockquote>
<code>
class Foo {<br>
function toString() {return "php::foo";}<br>
}<br>
$foo = new Foo();<br>
$jObj = java_closure($foo);<br>
$String = new Java("java.lang.String");<br>
echo $String->valueOf($jObj);<br>
</code>
</blockquote>
</p>
<H4>How do I call JSP tags from PHP?</H4>
<p>
Example:
<blockquote>
<code>
require_once("http://localhost:8080/JavaBridge/java/Java.inc");<br>
java_require("myLibs/baz-taglib.jar");<br>
$tag = new Java("foo.bar.BazTag");<br>
<br>
$session = java_session();<br>
$ctx = java_context();<br>
$servlet = $ctx->getAttribute("php.java.servlet.Servlet");<br>
$response = $ctx->getAttribute("php.java.servlet.HttpServletResponse");<br>
$request = $ctx->getAttribute("php.java.servlet.HttpServletRequest");<br>
$factory = java("javax.servlet.jsp.JspFactory")->getDefaultFactory();<br>
$pc = $factory->getPageContext($servlet, $request, $response, null, true, 8192, false);<br>
<br>
$tag->setPageContext($pc);<br>
$value = $tag->doStartTag();<br>
if(($value != Java("javax.servlet.jsp.tagext.Tag")->SKIP_BODY) {<br>
if($value != Java("javax.servlet.jsp.tagext.Tag")->EVAL_BODY_INCLUDE)) {<br>
$tag->setBodyContent($pc->pushBody());<br>
$tag->doInitBody();<br>
}<br>
do {<br>
...<br>
} while($tag->doAfterBody() == Java("javax.servlet.jsp.tagext.BodyTag")->EVAL_BODY_AGAIN)<br>
}<br>
if($value != Java("javax.servlet.jsp.tagext.Tag")->EVAL_BODY_INCLUDE) $pc->popBody();<br>
$tag->doEndTag();<br>
</blockquote>
</code>
</p><p>
The generated content (if any) can be retrieved from the servlet output stream with:
<blockquote>
<code>
java_values($response->getBufferContents();
</code>
</blockquote>
</p>
<p>Please see the <code>php_java_lib/JspTag.php</code> and <code>tests.php5/tag.php</code> for details.</p>
<H4>How does the bridge support Java generics?</H4>
<p>
You can ignore the parameter type.
</p>
<p>
Java doesn't support real generics on byte-code level. The generics in JDK 1.5 and above are implemented as "erasures"; they are syntactic sugar, useful only for the Java compiler. The generated byte-code is the same as in JDK 1.4.
</p>
<H4>What about Java 5 varargs?</H4>
<p>
Pass them as a PHP array. Example:
</p>
<p>
<blockquote>
<code>
<?php require_once("http://localhost:8080/JavaBridge/java/Java.inc");<br>
$here=getcwd(); java_require("$here/varargs.jar");<br>
$t1 = new java('Varargs$Test', 1);<br>
$t2 = new java('Varargs$Test', 2);<br>
echo java("Varargs")->varargs(array($t1, $t2));<br>
?>
</code>
</blockquote>
</p>
<p>
<blockquote>
<code>
public class Varargs {<br>
public static class Test {<br>
public int i;<br>
public Test (int i) {<br>
this.i = i;<br>
}<br>
public String toString() {<br>
return String.valueOf(i);<br>
}<br>
}<br>
public static String varargs(Test ...tests) {<br>
StringBuffer buf = new StringBuffer();<br>
for (Test test : tests) {<br>
buf.append(test);<br>
}<br>
return buf.toString();<br>
}<br>
}<br>
</code>
</blockquote>
</p>
</BODY>
</HTML>