Monday, July 02, 2007

Achieve J2EE-PHP Interoperability

Achieve J2EE-PHP Interoperability
Employ two methods of interoperability to instantiate and manipulate Java objects from within PHP scripts as if they were native PHP objects.
by Andi Gutmans, Stew Nickolas, and Zeev Suraski

November 1, 2006

PHP adoption in enterprises is increasing rapidly, and as this adoption is happening an increasing amount of projects are reusing existing J2EE investments on the back end with PHP as a front-end glue technology. Let's take a look at an interoperability scenario between the J2EE platform and PHP.

Although the scenario here is generic, for the sake of this discussion we'll be using WebSphere Application Server as the J2EE back-end system and the Zend Platform for its PHP/Java Bridge, which is an efficient way of calling Java from within PHP scripts. PHP is used to implement a Web-based front end for a set of existing WebSphere assets that provide the implementation of a catalog and ordering management system. The sample application is a fictitious, supply-chain management application in which a retailer offers consumer electronic goods.

The two methods of interoperability between WebSphere and PHP are Web services and Zend Platform's PHP/Java Bridge. With Web services PHP supports creating and consuming them using its native SOAP extension. PHP's native SOAP extension takes advantage of PHP 5's new object-oriented overloading capabilities to automatically map class attributes to the datatypes defined in the WSDL file. The ability to dynamically overload the object-oriented syntax is one of PHP's key strengths and is because of its dynamic nature.

The Zend Platform's PHP/Java Bridge provides the ability for PHP developers to create applications that interact with Java objects. Using the overloading capabilities just mentioned, Java objects can be instantiated and manipulated from within PHP scripts as if they were native PHP objects.

This scenario uses the PHP/Java Bridge to access the back-end catalog and ordering systems running in the WebSphere application and acts as a single J2EE application client (see Figure 1).

Retailer Implementation
In the context of the interoperability scenario presented here, we'll discuss these topics:

  • Sample description – A detailed discussion of the sample scenario including page flow, Web services, Enterprise JavaBeans (EJB), and prerequisites for installing the technologies.
  • Setting up the PHP/Java Bridge to work with WebSphere – A detailed description of the J2EE assets used including setting up and testing the PHP/Java Bridge with WebSphere and tips and techniques for developing and debugging.
  • Web services – In the sample application, Web services are used as the communication channel between the retailer client and server. PHP's dynamic nature is ideal for implementing Web services layers, and both the client and server parts are implemented in PHP. PHP 5 allows for very elegant separation between PHP's object-oriented syntax and semantics.
  • Using the PHP/Java Bridge to interoperate in the example – The PHP/Java Bridge module provides PHP-centric companies with a well-rounded environment that ensures that the organization benefits from the "best of both worlds," be it existing investments in J2EE application servers that require this solution, to provide a means for organizations if they choose, or to complement the PHP environment with Java. The PHP/Java Bridge is not limited to interactions strictly with J2EE and legacy systems; it also provides the ability to interact with plain Java objects.

The fictitious, supply-chain management application consists of a retailer that offers electronic goods to consumers. A Retailer component is implemented using a Web service that is based on the WS-I.org base profile sample application. It provides methods for listing the contents of the retailer’s catalog and placing an order for a collection of goods. To fulfill orders the Retailer component makes a request to a Warehouse client to ship the items. In this example, only one Warehouse is used to restock an item. The Retailer will call the Warehouse requesting the item for shipment. Each Warehouse maintains stock levels for each item. The high-level architecture of the sample application is shown in Figure 2.

Message flow is accomplished through a standard browser client using HTTP/HTML and is used to communicate with the Web tier, after which the message flow proceeds this way:

  1. The PHP catalog display page, order.php, creates a client-side proxy for the Retailer Web service and requests the catalog for display in the page using the PHP SoapClient class.
  2. The Retailer Web service proxy uses SOAP to communicate with the service implementation. In this sample the service is implemented using a PHP SOAP server abstraction; however, the endpoint could be implemented by any compliant SOAP server.
  3. The PHP Retailer implementation uses the PHP/Java Bridge to locate, create, and request the catalog for a Warehouse using a J2EE session bean.
  4. The PHP/Java Bridge proxies created in step 3 are used to communicate with the WebSphere Application Server, which contains the business logic for the catalog and ordering systems. In this respect, the Zend Platform acts as a J2EE application client.
  5. The Warehouse session bean is responsible for managing the catalog items and collecting them from the entity bean data store.
  6. The Warehouse session bean also manipulates the stock level of the Warehouse entity beans when processing shipments.

Simple Flow
The sample application consists of four main pages with a simple, sequential flow (see Figure 3):

  • index.php gathers customer information (for example, name, number, street, and so on) and stores it in the session data.
  • configure.php displays the list of Retailer and Warehouse implementations offered for use in the scenario; in this sample the PHP Retailer implementation is used, and the WebSphere Warehouse is used on the business tier.
  • order.php displays the list of catalog items offered by the Retailer and Warehouse services in a simple table form enabling users to enter the quantity of each good to be purchased.
  • submitOrder.php collects the order requests and submits the order to the Retailer server and displays the result of processing the order—either the items will be shipped or are not in stock.

The sample application will need software installed and configured. Configuring WebSphere will be discussed in more detail shortly. The specific software is either PHP 5.0.4 or Zend Core for IBM; Zend Platform 2.1.2; IBM Rational Software Development Platform version 6.0.1 was used to develop the J2EE components and test the application (note that this is software optional; only WebSphere 6.0 Server and Application Client are required); WebSphere Application Server 6.0; and WebSphere Application Client for WebSphere Application Server 6.0.

To set up the PHP-WebSphere Bridge, use these steps to install and configure the WebSphere Application Server and the Zend Platform:

  1. Install the WebSphere Application Client on the Zend Platform Node that will interoperate with the WebSphere Application Server. (Note: make sure this software is installed, even if the application server is on the same machine. The PHP/Java Bridge script uses the application client files to interoperate with the application server.)
  2. Install the Zend Platform and make sure the bridge's Java path points to the WebSphere Application Client Java—for example: /opt/IBM/WebSphere/AppServer/java/bin/../jre/bin/java
  3. Ensure the …/ZendPlatform/client directory of the Zend Platform has write permissions before starting the Java Bridge. A log file will be written to this location by the WebSphere Application Client when the Object Request Broker (ORB) has problems communicating to the application server. To change the location where this file is written, start the javamw.rc (Java middleware) screen in the location.
  4. The javamw.rc script file that is used to start the PHP/Java Bridge must be modified to include the WebSphere Application Client settings. These settings are located in the launchClient.sh script file in the AppClient/bin directory—for example: /opt/IBM/WebSphere/AppClient/bin/launchclient.sh. These modifications are necessary because launchClient.sh is not used to start the PHP/Java Bridge. The modifications to the javamw.rc file are shown in Listing 1 (note: the modifications have been formatted for display).

The two most important lines are lines 2 and 25. Line 2 defines a directory that will contain the client JAR files necessary to run a client application (for example, WarehouseClient.jar for this sample):

/opt/IBM/WebSphere/AppClient/
clientapps:

The second change (in line 25 in Listing 1) simply passes the WebSphere configuration parameters to the Java runtime on the command line. To test the PHP/Java Bridge use the code shown in Listing 2.

EJB
The sample application consists of EJB. Here is the warehouse session bean that carries out querying the catalog and handling order submission:

public interface Warehouse extends
javax.ejb.EJBObject {
public boolean resetStock()
throws CreateException,
RemoteException,
RemoveException,
FinderException,
NamingException;
public Collection shipGoods(
Collection itemList)
throws RemoteException;
public Collection getCatalog()
throws RemoteException;

}

The CatalogItem entity bean simply maintains a list of persistent consumer goods offered by the warehouse. The fields of the CatalogItem contain the name, description, category, brand, and price of the catalog item. The WarehouseItem entity bean, which is linked to CatalogItem through the primary key, represents the stock level of a CatalogItem along with the minimum and maximum level of the item contained in the warehouse.

To install and configure the sample application perform these steps:

  1. Install the sample.ear file on the WebSphere Application Server using the administration console, or import the sample into the IBM Rational Software Development Platform 6.0.1. As part of the deployment, make sure the database tables are created for the WarehouseItem and CatalogItem entity beans.
  2. Locate and copy the client JAR files, WarehouseClient.jar and Warehouse.jar, into the directory specified as the client application directory in javamw.rc. In the example shown in Listing 1 the directory is /opt/IBM/WebSphere/AppClient/clientapps.
  3. Restart the javamw.rc script using the Zend Platform administration console.

In our sample application Web services are used as the communication channel between the Retailer client and server. PHP's dynamic nature is ideal for implementing Web services layers, and both the client and server parts are implemented in PHP. PHP 5 allows for very elegant separation between PHP's object-oriented syntax and the semantics. This separation is how many of PHP5's key features such as SOAP and SimpleXML provide easy-to-use and natural interfaces.

From the PHP developer's perspective they just instantiate a SOAP or a SimpleXML object, treat it like a regular PHP object (from the syntax perspective), and automatically the relevant extensions jump into action and make sure that "the right thing" happens. For example, you'll see that when using the SoapClient class, creating the SOAP request, making the remote HTTP connection, submitting the request, and then receiving and parsing the response is completely transparent to the PHP developer.

To develop a Web service using PHP, the first step is to have a class that implements the functionality we want to expose. The class can be any PHP class, with no special requirements or limitations. In our case, the class that implements the service is Retailer, which is included in the Retailer.php file.

The second step is to obtain or create a WSDL file that describes the Web service. The Retailer Web service interface is described in the Retailer_Impl.wsdl file. That file points to the implementation URL of the Web service:

http://localhost/wsi/Retailer/
RetailerImpl.php

and points to numerous other WSDL and XSD files (both directly and indirectly), which contain the details of the published services and the definitions of the datatypes used. Note that while PHP's Web services support doesn't strictly require WSDL files, it's highly recommended to use them, to unlock the full potential of PHP's dynamic nature and syntax. Even though writing a WSDL file can be quite an irritating task, WSDL generation tools can be used to automate it.

Data Object Storage
The third step, which is optional, is to implement PHP classes for some or all of the complex types that are used throughout the Web service. In our sample, the classes are defined in datum.php. While the implementations in this file are quite generic, it's possible to implement business logic in these classes, if this logic makes sense in the context of your application. If there's no custom code you want to add to these classes at all, then you don't have to implement them. PHP will use generic data objects to store the data instead.

The last step binds everything together and turns our regular class into a full-fledged Web service. As odd as it may look, this approach is done using just three lines of code in RetailerImpl.php:

$server = new SoapServer(
"http://localhost/new/wsdl/
Retailer_Impl.wsdl"
,
array('classmap' => array(...));

$server->setClass("Retailer");
$server->handle();

The built-in SoapServer class is instantiated, referencing the aforementioned WSDL file, and with a reference to the PHP classes that implement the SOAP types. Next, we instruct our SoapServer object $server to use a Retailer object as the implementation for the Web service. Finally, we call the SoapServer::handle() method, which is responsible for doing all the dirty work. To be precise, this dirty work comprises parsing the SOAP request, figuring out which method is being called and with what arguments, relaying it to the Retailer object, and packing the result in a SOAP envelope and printing it as a result.

While it's common to mistake this small script for a daemon waiting for connections, it's in fact a regular, stateless PHP script, which is only executed when a client tries to consume the Web service. SoapServer::handle() never waits for an incoming connection; by the time it's called the SOAP request is already available for processing. Thanks to PHP's built-in WSDL caching, this stateless strategy is not only extremely scalable, but is also quite fast.

Although after seeing the implementation of the SoapServer you may think it can hardly get any simpler than that, you're in for a surprise. Consumption of Web services in PHP is fully transparent, to the degree that it looks, feels, and behaves exactly like regular function calls. In model.php, the getRetailer() function instantiates a SoapClient object, passing it the same WSDL file, RetailerImpl.wsdl, that the server side uses, and returns it:

function getRetailer($configData) {
...
$result = new SoapClient(
$configData['wsdl'], array(
'trace' => 1, 'location' =>
$endpoint, 'classmap' =>
array(...)));

...

return($result);
}

The SoapClient object that is returned can then be used by the application code as if it was a standard, local object:

$retailer = getRetailer(
$RETAILERS[$config[
'retailer']]);
...
try {
$cat = $retailer->getCatalog();
$items = $cat->getItem();
...

} catch(Exception $ex) { die($ex);
}

Any error that happens during the call, whether it happens on the server side (for example, the service has thrown an exception) or the client side (for example, the server was unreachable) results in a SoapException being thrown. This means that an exception thrown in one of the methods of the Retailer class (on the server side), will propagate over the wire and will affect (or can be caught by) the application code on the client side. Exception propagation is done transparently and automatically.

Consume EJB
In our sample application, the PHP/Java Bridge (from here on Java Bridge) is used to locate, create, and request the catalog for a warehouse using a J2EE session bean. Java Bridge uses the previously mentioned PHP object syntax overloading capabilities to expose Java objects in a very natural way to PHP. The main benefits of this bridge is for companies who have existing investments in Java and J2EE, and can therefore continue to leverage those investments while at the same time taking advantage of the rapid Web development that PHP offers. We have found that the main reasons for using Java Bridge are to either leverage existing back-end EJB or leverage Java libraries and code.

Back in the PHP 4 days, the first Java Bridge was written using JNI. Unfortunately, this early proof of concept was not suitable for production use. The main reason was because of most PHP deployments being on multiprocess Apache as a result of the majority of PHP users preferring the multiprocess Apache paradigm thanks to the architecture's stability and ease of deployment.

Unfortunately, this approach is also the main reason why this early incarnation of a Java Bridge was not suitable for production use. Its use of JNI resulted in the architecture dictating that each Apache process required its own Java Virtual Machine (JVM) instance. With typical Apache configurations running hundreds of Apache child processes, the servers would quickly run out of memory. With the typical JVM taking a few megabytes of memory, it is clear that even without running any Java or PHP business logic, the server would quickly take up gigabytes of memory just for spawning the needed JVMs.

The Java Bridge resolves this problem by using only one JVM per physical Web server. It uses a high-performance binary protocol to proxy through sockets to the JVM. The JVM itself runs on an efficient, event-driven model that can easily handle hundreds, or even thousands, of concurrent Java Bridge connections and still perform adequately. By only requiring one JVM per machine, memory use is kept at a minimum.

As demonstrated previously, instantiating a new Java object is as easy as writing this PHP code:

$obj = new Java('MyClass');

From this point on, the Java object $obj is treated just like a PHP object. Calling a method on it is straightforward and just as expected:

$ret = $obj->method();

Accessing properties is just as straightforward:

$prop = $obj->prop;
$obj->prop = 4;

The Java Bridge also propagates exceptions that are thrown in the Java code. These exceptions are thrown as JavaExceptions and can be caught using the standard try/catch exception-handling constructs of PHP 5:

try {
$obj->method();
} catch {JavaException $je) {
var_dump($obj);
}

Now let's go back to our real-world example. As mentioned previously, setting up the warehouse catalog, submitting an order, and retrieving the catalog are achieved by calling EJB from PHP. This code snippet is taken from the Retailer class, which implements all of this functionality, and it demonstrates how an EJB can be called easily from PHP and should look very familiar for those of you who have called EJB directly from Java:

$context = new Java(
'javax.naming.InitialContext');
$homeObject = $context->lookup(
"ejb/ejbs/WarehouseHome");
$rmi = new Java(
"javax.rmi.
PortableRemoteObject");
$home = $rmi->narrow(
$homeObject, new Java(
"ejbs.WarehouseHome"));
$bean = $home->create();
$catalog = $bean->getCatalog();

Note that in the real world, you would probably not call the EJB in this way. Rather you would likely wrap the code for calling the EJB in one Java method, add that to the Java Bridge class path, and call that method from PHP. This approach would save you from jumping in and out of languages and would probably be more efficient. However, our examples use the more verbose approach to better demonstrate the Java Bridge.

Web Adhesive
PHP plays very well into the world of service-oriented architecture (SOA), and does so at various levels. Foremost, PHP is a great Web glue language and makes it very easy to aggregate services into a uniform Web interface or Web service. By easily consuming Java, SOAP, REST, and many other data sources, plus having the advantage of being an extremely rapid Web development platform, the example discussed here offers a very common use case. In addition, PHP is also extremely suitable for exposing services that we demonstrated mainly using the SoapServer class. We believe that PHP is uniquely positioned to capitalize on the growth in adoption of dynamic languages because of their ease of use, short time to market, and the increased uptake of SOAs.

About the Authors
Andi Gutmans is cofounder and vice president of technology at Zend Technologies, Stew Nickolas is senior technical staff member at IBM Emerging Internet Technologies, and Zeev Suraski is cofounder and CTO at Zend Technologies.

reference: http://www.ftponline.com/channels/java/2006_11/agutsman/default_pf.aspx

No comments: