Skip to main content

PHP on Grizzly with JSR223

Posted by survivant on February 3, 2009 at 4:38 PM PST

This time I will show you how to run PHP applications on GrizzlyWebServer using 100% pure java solutions.
I used Caucho Quercus to handle the PHP and GrizzlyWebServer as my WebServer.

The first implementation that I did was really simple. I used the demo that I did for JSPOnGrizzly and replace the servlet
by Quercus. You can see what the code looks like :


public void launch(){
		GrizzlyWebServer ws = new GrizzlyWebServer(80);
		
		try {
            ServletAdapter sa = new ServletAdapter();
            sa.setContextPath("/");
            
            Servlet servlet = (Servlet)ClassLoaderUtil.load("com.caucho.quercus.servlet.QuercusServlet");
    	      sa.setServletInstance(servlet);
            
    	      ws.addGrizzlyAdapter(sa, new String[]{});
            ws.start();
        } catch (Exception e){
            s_logger.error("Error Launching GrizzlyWebServer",e);
        }
}

I was expecting that would work within a few seconds, but I got few exceptions when I tried to run the application.


2009-02-02 17:28:03 com.sun.grizzly.http.servlet.ServletAdapter service
GRAVE: service exception:
java.lang.UnsupportedOperationException: Not supported yet.
	at com.sun.grizzly.http.servlet.HttpServletRequestImpl.getRealPath(HttpServletRequestImpl.java:624)
	at com.caucho.quercus.servlet.QuercusServletImpl.getPath(QuercusServletImpl.java:244)
	at com.caucho.quercus.servlet.QuercusServletImpl.service(QuercusServletImpl.java:112)
	at com.caucho.quercus.servlet.QuercusServlet.service(QuercusServlet.java:407)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
	at com.sun.grizzly.http.servlet.FilterChainImpl.doFilter(FilterChainImpl.java:174)
	at com.sun.grizzly.http.servlet.FilterChainImpl.invokeFilterChain(FilterChainImpl.java:123)
	at com.sun.grizzly.http.servlet.ServletAdapter.service(ServletAdapter.java:254)
	at com.sun.grizzly.tcp.http11.GrizzlyAdapter.service(GrizzlyAdapter.java:165)
	at com.sun.grizzly.http.DefaultProcessorTask.invokeAdapter(DefaultProcessorTask.java:653)
	at com.sun.grizzly.http.DefaultProcessorTask.doProcess(DefaultProcessorTask.java:570)
	at com.sun.grizzly.http.DefaultProcessorTask.process(DefaultProcessorTask.java:840)
	at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:153)
	at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:136)
	at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:103)
	at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:89)
	at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76)
	at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:67)
	at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57)
	at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
	at java.util.concurrent.FutureTask.run(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)

Grizzly doesn't completely implement HttpServletRequest (yet). The easy way doesn't always work the first time.
BUT there is still a way to do PHP with GrizzlyWebServer. An alternative is to use Quercus's JSR223 implementation.

We will have to replace QuercusServlet by my Servlet that uses Quercus libraries.


Servlet servlet = (Servlet)ClassLoaderUtil.load("ca.sebastiendionne.grizzly.servlet.QuercusHttpServlet");

QuercusHttpServlet

package ca.sebastiendionne.grizzly.servlet;

import java.io.FileReader;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;

import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class QuercusHttpServlet extends HttpServlet {
	private static final long serialVersionUID = -1649882114668014299L;
	
	private static final Logger s_logger = LoggerFactory.getLogger(QuercusHttpServlet.class);
	
	private static ScriptEngineManager scriptManager = new ScriptEngineManager();
	private static ScriptEngine phpEngine = scriptManager.getEngineByExtension("php");
	
	@SuppressWarnings("unchecked")
	protected void doPut (HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		
		ScriptContext context = phpEngine.getContext();

		try {
			context.setWriter(new StringWriter());
			
			//put the parameters and attributes into the engine
			Enumeration attributes = req.getAttributeNames();
			
			while (attributes.hasMoreElements()) {
				String key = (String) attributes.nextElement();
				
				// put the parameters and attributes into the engine
				phpEngine.put(key, req.getAttribute(key));
			}
			
			Map<String, Object> params = req.getParameterMap();
			
			for (Iterator iterator = params.keySet().iterator(); iterator.hasNext();) {
				String key = (String) iterator.next();
				
				// put the parameters and attributes into the engine
				phpEngine.put(key, params.get(key));
			}
			
			// need a better way to find the requested file
			String uri = req.getRequestURI();
			
			if(uri.startsWith("/")){
				uri = uri.substring(1);
			}
			
			phpEngine.eval(new FileReader(uri), context);
			StringWriter writer = (StringWriter) context.getWriter();
			res.getWriter().println(writer.toString());
		} catch (Exception e) {
			s_logger.error("Error Evaluating PHP",e);
		}
		
	}
	
	protected void doGet (HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		doPut(req, res);
	}

}

We need a php page to test our application.
phpinfo.php


<?php

// Show all information, defaults to INFO_ALL
phpinfo();

?>

The output won't be the same as the native php. Here the output from Quercus implementation.

Quercus
PHP Version => 5.2.0
System => Windows XP 5.1 x86
Build Date => 20070628T2777
Configure Command => n/a
Server API => CGI
Virtual Directory Support => disabled
Configuration File (php.ini) Path => WEB-INF/php.ini
PHP API => 20031224
PHP Extension => 20041030
Debug Build => no
Thread Safety => enabled
Registered PHP Streams => php, file, http, https
Variable => Value
....

You can download the source code for this project here.

Related Topics >>

Comments

new String[]{} is ugly, I do prefer new String[0]. However this great blog makes me want to write PHP webapps just to try this, what a performance !

OK I've fixed the Unsupported exception :-)