PageBox for Java: Web application deployment using Java PageBox

for
 PageBox for Java 
 API 
 Demo 
 Background 
Presentation Install User guide Developer guide Programming Port Repository PageBox Release notes

Porting PageBox for Java

Foreword

In this document we explain how to port the Tomcat/Axis or the JWSDP version of PageBox for Java to another J2EE Application server. We also explain how to write a resource probe for an Operating System different from Linux and Window NT/2000/XP.

Audience

System programmers knowing J2EE.

Assumptions

We assume that the target Application server supports:

  1. JDK 1.4 and above

  2. JavaServer Page (JSP) specification 1.2 and above

  3. Servlet specification 2.3 and above

  4. The Java API for XML Processing (JAXP)

  5. COS, the com.oreilly.servlet package written by Jason Hunter for upload (MultipartRequest class) handling

Older application servers

Nowadays most Application servers support requirements 1 to 3.

To port PageBox for Java to an older Application server or JDK:

  • Try to compile PageBox with your Application server classes

  • Check the comments: we explain what has to be done to downgrade to JDK 1.3

  • Fix the compilation errors

It is not terribly difficult if the Application server at least supports the 2.1 Servlet specification and the JDK 1.3.

COS

PageBox for Java already contains the COS archive. COS should work in your environment. However you may want to use the multipart POST handling embedded in your Application server.

JAXP

In PageBox for Java we use the Xerces/Xalan implementation.

Xalan distributions include the Xerces archives. So the easy way to do the job is to copy the archives defined in $XALAN_ROOT/bin to your Application server shared or common libraries or to add the $XALAN_ROOT/bin archives to your CLASSPATH.

This approach is not applicable if the Application already contains and uses an older and incompatible version of Xalan. In this case try compiling PageBox with your Application server classes and fix the compilation errors. PageBox uses the XSL compilation to minimize the processing time. You can safely remove this optimization if it is not supported.

RPC

The PageBox interface to deployment and query allows using different RPC protocols between the Repositories and the PageBoxes, for instance:

  • The Java API for XML-based RPC (JAX-RPC) or another SOAP implementation

  • XML-RPC

  • RMI

  • Raw TCP

  • Any other protocol

To interoperate with standard Tomcat/Axis and JWSDP 1.2 PageBoxes and Repositories your implementation must support either JAX-RPC/SOAP or raw HTTP.

JAX-RPC

The Tomcat/Axis and the JWSDP versions of PageBox for Java differ only in the implemented JAX-RPC flavor:

  • JWSDP 1.3 / 1.5 for the JWSDP version

  • Axis for the Tomcat/Axis flavor

Other RPC protocols

Both the Tomcat/Axis and the JWSDP versions of PageBox for Java versions support a raw HTTP protocol. This implementation should work on any Java environment.

Application server deployment

Dynamic deployment allows deploying, reloading or undeploying a J2EE Web application on the fly.

Dynamic deployment is Application server dependent. Often Application servers offer a HTTP interface even if the trend is to also offer a SOAP interface.

The PageBox interface to Application server deployment allows supporting different dynamic deployment facilities.

Extensions

PageBox plug-able extensions allow installed archives to access special resources, for instance through the Java Native Interface in a controlled manner. The development of extensions is described in java-extension.html.

License issues

As we have seen PageBox allows developing three kinds of plug-ins:

  • RPC drivers

  • Dynamic deployment drivers

  • Extensions

The Lesser GNU Public License (LGPL) covers the whole PageBox distribution:

  • The core PageBox and Repository

  • The JWSDP JAX-RPC driver

  • The Axis JAX-RPC driver

  • The raw HTTP driver

  • The dynamic deployment driver for Tomcat

  • The serial and parallel port extension

If you modify any of these programs you must license the modified code under LGPL.

You can develop extensions, RPC and dynamic deployment drivers under the license of your choice or as proprietary code.

We would be happy to distribute your code and in any case to hear about your developments.

Plan

The rest of this page is divided in four sections:

  • Classes interfacing with JAXP and COS. This section is especially useful if you want to port PageBox to an Application server with non-JAXP XML and XSL libraries or if you have to use an older version of JAXP. The section is also useful if you want to use a non-COS multipart upload.

  • RPC drivers.

  • Dynamic deployment drivers

  • Resource probes

Classes interfacing with JAXP and COS

JAXP

PageBox and the PageBox Repository use the SAX API of JAXP. Here is an example of use:

SAXParserFactory factory = SAXParserFactory.newInstance();

try {

SAXParser saxParser = factory.newSAXParser();

saxParser.parse(new File(workDir + File.separator + "subscribers.xml"),

handler);

}

catch(Throwable t) {

...

}

SAX handlers extend org.xml.sax.helpers.DefaultHandler and implement the startElement, endElement and characters methods.

PageBox and the PageBox Repository use the XSLT API of JAXP. Here is an example of use:

StreamSource datasource = new StreamSource(bais);

if (templates == null) {

TransformerFactory tFactory = TransformerFactory.newInstance();

File stylesheet = new File(workDir + File.separator + "audit-pagebox.xsl");

StreamSource stylesource = new StreamSource(stylesheet);

templates = tFactory.newTemplates(stylesource);

}

Transformer transformer = templates.newTransformer();

StreamResult result = new StreamResult(out);

transformer.transform(datasource, result);

PageBox and the PageBox Repository use the compiling transformer (XSLTC).

COS

COS is only used in PublishArchive with the following snippet:

MultipartRequest mr = new MultipartRequest(request, uploadDir,

(int)(10 * 1024 * 1024));

if (mr.getParameter("upload") != null) {

String docUrl = mr.getParameter("docURL");

boolean runInstall = false;

if (mr.getParameter("runInstall") != null)

runInstall = true;

Enumeration en = mr.getFileNames();

while(en.hasMoreElements()) {

String arch = mr.getFilesystemName((String)en.nextElement());

resp = RepoArchs.add(workDir, downloadDir, uploadDir, deltaDir, log,

owner, host, arch, docUrl, runInstall, downloadURL, ceiling,

relayTime);

...

}

}

We list below the classes you may need to check or modify.

Repository

Class or Interface

Use

API

Deployer

Calls the PageBox Deploy Web service

RPC driver

RepoSubs

The init method uses the SAX parser

JAXP

RepoASubs

The init method uses the SAX parser

JAXP

RepoArchs

The init method uses the SAX parser

JAXP

ArchiveHandler

SAX handler

JAXP

SubscriberHandler

SAX handler

JAXP

PageBoxAudit

XSLT API

JAXP

PublishArchive

Archive upload

COS

PageBoxLib

Class or Interface

Use

API

Deployer

Calls the PageBox Deploy Web service

RPC driver

Querier

Calls the Repository RepoQuery Web service

RPC driver

APIImpl

The initialize and the restorePbArchs methods use the SAX parser

JAXP

DeployImpl

The initialize and the restorePbArchs methods use the SAX parser

JAXP

DynDns

The constructor and the initialize method use the SAX parser

JAXP

DeployHandler

SAX handler

JAXP

RuleHandler

SAX handler

JAXP

AuditHandler

SAX handler

JAXP

FixHandler

SAX handler

JAXP

PageBox

Class or Interface

Use

API

Audit

XSLT API

JAXP

Reinstall

The initialize and the restorePbArchs methods use the SAX parser

JAXP

Update

XSLT API

JAXP

RPC drivers

A RPC driver is made of four components:

  • A RepoQuery listener (Repository server)

  • A Deploy listener (PageBox server)

  • A RepoQuery service invoker (PageBox client)

  • A Deploy service invoker (PageBox and Repository client)

Server

The function of the server is to process the service requests and call the service implementations.

RepoQueryIF

The service implementation, Repository.RepoQueryImpl implements the Repository.RepoQueryIF interface, which is defined like this:

public interface RepoQueryIF extends RepoTokenIF, Remote {

public class UrlStatus implements java.io.Serializable {

public String url;

public int code;

public String msg = null;

}

public class AuthSub implements java.io.Serializable {

public String url;

public String user;

public String password;

}

public AuthSub[] GetSubscribers(String archive)

throws RemoteException;

public void Notify(String archive, UrlStatus[] subscribers)

throws RemoteException;

public void NotifyFix(String subscriber, FixArch[] archives)

throws RemoteException;

}

RepoTokenIF is defined like this:

public interface RepoTokenIF extends PageBoxLib.TokenIF {

public void tokenRegister(String subscriber) throws RemoteException;

public void tokenUnregister(String subscriber) throws RemoteException;

}

TokenIF is defined like this:

public interface TokenIF {

public DeployIF.Status frameSend(TokenFrame frame) throws RemoteException;

}

RepoQueryIF extends Remote. Therefore you can use it in JAX-RPC and RMI to generate proxies and server stubs. For the same reason the methods can throw RemoteException. When you use a protocol like raw TCP it is useful to be able to easily serialize parameters and answers. For this reason the UrlStatus, AuthSub and frameSend classes implement the Serializable interface.

HTTP-based implementations are especially easy to implement for three reasons:

  • The servlet container is the actual listener. It is usually very scalable and it dispatches the requests to a servlet responsible for the request un-serialization and for the invocation of RepoQueryImpl.

  • The servlet container usually supports SSL for encryption, hashing and server authentication.

  • The servlet container also implement access control with basic authentication

SOAP and XML-RPC implementations are even more convenient because the servlet is part of the implementation and the server stub - when needed - can be generated with a command of the SOAP implementation.

The JAX-RPC implementations we know allow generating the WSDL file from the interface. If you use a SOAP implementation that doesn’t allow it you can use this WSDL (generated from Axis):

<?xml version="1.0" encoding="UTF-8"?>

<wsdl:definitions targetNamespace="http://localhost:8080/axis/RepoQuery.jws" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:apachesoap="http://xml.apache.org/xml-soap" xmlns:impl="http://localhost:8080/axis/RepoQuery.jws" xmlns:intf="http://localhost:8080/axis/RepoQuery.jws" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<wsdl:message name="tokenUnregisterResponse">

</wsdl:message>

<wsdl:message name="tokenRegisterRequest">

<wsdl:part name="subscriber" type="xsd:string"/>

</wsdl:message>

<wsdl:message name="NotifyResponse">

</wsdl:message>

<wsdl:message name="GetSubscribersResponse">

<wsdl:part name="GetSubscribersReturn" type="xsd:base64Binary"/>

</wsdl:message>

<wsdl:message name="frameSendRequest">

<wsdl:part name="frame" type="xsd:base64Binary"/>

</wsdl:message>

<wsdl:message name="NotifyFixResponse">

</wsdl:message>

<wsdl:message name="tokenUnregisterRequest">

<wsdl:part name="subscriber" type="xsd:string"/>

</wsdl:message>

<wsdl:message name="frameSendResponse">

<wsdl:part name="frameSendReturn" type="xsd:base64Binary"/>

</wsdl:message>

<wsdl:message name="NotifyRequest">

<wsdl:part name="archive" type="xsd:string"/>

<wsdl:part name="subscribers" type="xsd:base64Binary"/>

</wsdl:message>

<wsdl:message name="tokenRegisterResponse">

</wsdl:message>

<wsdl:message name="NotifyFixRequest">

<wsdl:part name="subscriber" type="xsd:string"/>

<wsdl:part name="archives" type="xsd:base64Binary"/>

</wsdl:message>

<wsdl:message name="GetSubscribersRequest">

<wsdl:part name="archive" type="xsd:string"/>

</wsdl:message>

<wsdl:portType name="RepoQuery">

<wsdl:operation name="NotifyFix" parameterOrder="subscriber archives">

<wsdl:input message="impl:NotifyFixRequest" name="NotifyFixRequest"/>

<wsdl:output message="impl:NotifyFixResponse" name="NotifyFixResponse"/>

</wsdl:operation>

<wsdl:operation name="GetSubscribers" parameterOrder="archive">

<wsdl:input message="impl:GetSubscribersRequest" name="GetSubscribersRequest"/>

<wsdl:output message="impl:GetSubscribersResponse" name="GetSubscribersResponse"/>

</wsdl:operation>

<wsdl:operation name="frameSend" parameterOrder="frame">

<wsdl:input message="impl:frameSendRequest" name="frameSendRequest"/>

<wsdl:output message="impl:frameSendResponse" name="frameSendResponse"/>

</wsdl:operation>

<wsdl:operation name="Notify" parameterOrder="archive subscribers">

<wsdl:input message="impl:NotifyRequest" name="NotifyRequest"/>

<wsdl:output message="impl:NotifyResponse" name="NotifyResponse"/>

</wsdl:operation>

<wsdl:operation name="tokenRegister" parameterOrder="subscriber">

<wsdl:input message="impl:tokenRegisterRequest" name="tokenRegisterRequest"/>

<wsdl:output message="impl:tokenRegisterResponse" name="tokenRegisterResponse"/>

</wsdl:operation>

<wsdl:operation name="tokenUnregister" parameterOrder="subscriber">

<wsdl:input message="impl:tokenUnregisterRequest" name="tokenUnregisterRequest"/>

<wsdl:output message="impl:tokenUnregisterResponse" name="tokenUnregisterResponse"/>

</wsdl:operation>

</wsdl:portType>

<wsdl:binding name="RepoQuerySoapBinding" type="impl:RepoQuery">

<wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>

<wsdl:operation name="NotifyFix">

<wsdlsoap:operation soapAction=""/>

<wsdl:input name="NotifyFixRequest">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://DefaultNamespace" use="encoded"/>

</wsdl:input>

<wsdl:output name="NotifyFixResponse">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost:8080/axis/RepoQuery.jws" use="encoded"/>

</wsdl:output>

</wsdl:operation>

<wsdl:operation name="GetSubscribers">

<wsdlsoap:operation soapAction=""/>

<wsdl:input name="GetSubscribersRequest">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://DefaultNamespace" use="encoded"/>

</wsdl:input>

<wsdl:output name="GetSubscribersResponse">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost:8080/axis/RepoQuery.jws" use="encoded"/>

</wsdl:output>

</wsdl:operation>

<wsdl:operation name="frameSend">

<wsdlsoap:operation soapAction=""/>

<wsdl:input name="frameSendRequest">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://DefaultNamespace" use="encoded"/>

</wsdl:input>

<wsdl:output name="frameSendResponse">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost:8080/axis/RepoQuery.jws" use="encoded"/>

</wsdl:output>

</wsdl:operation>

<wsdl:operation name="Notify">

<wsdlsoap:operation soapAction=""/>

<wsdl:input name="NotifyRequest">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://DefaultNamespace" use="encoded"/>

</wsdl:input>

<wsdl:output name="NotifyResponse">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost:8080/axis/RepoQuery.jws" use="encoded"/>

</wsdl:output>

</wsdl:operation>

<wsdl:operation name="tokenRegister">

<wsdlsoap:operation soapAction=""/>

<wsdl:input name="tokenRegisterRequest">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://DefaultNamespace" use="encoded"/>

</wsdl:input>

<wsdl:output name="tokenRegisterResponse">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost:8080/axis/RepoQuery.jws" use="encoded"/>

</wsdl:output>

</wsdl:operation>

<wsdl:operation name="tokenUnregister">

<wsdlsoap:operation soapAction=""/>

<wsdl:input name="tokenUnregisterRequest">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://DefaultNamespace" use="encoded"/>

</wsdl:input>

<wsdl:output name="tokenUnregisterResponse">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost:8080/axis/RepoQuery.jws" use="encoded"/>

</wsdl:output>

</wsdl:operation>

</wsdl:binding>

<wsdl:service name="RepoQueryService">

<wsdl:port binding="impl:RepoQuerySoapBinding" name="RepoQuery">

<wsdlsoap:address location="http://localhost:8080/axis/RepoQuery.jws"/>

</wsdl:port>

</wsdl:service>

</wsdl:definitions>

You may need to do minor modifications. Don’t worry about the location: it is overridden anyway by the PageBox implementation. We recommend using the RepoQuery.wsdl stored in the WEB-INF directory of the Repository binary distribution. This document won’t necessarily been updated at each new version.

RepoQueryImpl is somewhat optimized for JAX-RPC. It actually implements two interfaces:

  • RepoQueryIF

  • javax.xml.rpc.server.ServiceLifecycle

RepoQueryImpl extends RepoTokenImpl that implements the token-related methods (RepoTokenIF interface, tokenInit, tokenRegister and frameSend methods.)

javax.xml.rpc.server.ServiceLifecycle is a lifecycle interface for a JAX-RPC service endpoint. It defines two methods, init and destroy. The JAX-RPC runtime invokes the init method after a service endpoint instance is instantiated and the destroy method when it ends the lifecycle of a service endpoint instance.

RepoQueryImpl assumes that the parameter of the init method is of javax.xml.rpc.server.ServletEndpointContext type, which is true when the JAX-RPC runtime system is servlet container based.

RepoQueryImpl uses the javax.xml.rpc.server.ServletEndpointContext object to get

  • The Servlet context. From the context parameter RepoQueryImpl retrieves the initialization parameters.

  • The name of the user who made the request

This approach doesn’t work when the listener is not a JAX-RPC runtime.

To address this issue RepoQueryImpl has:

  • An init2 method that takes a ServletContext parameter

  • Peer web service methods with an extra user parameter

You can look at the HTTPQuery servlet for an example of raw HTTP implementation.

Like the JAX-RPC runtime HTTPQuery is not a listener. It just processes requests received by the servlet container. The RepoQueryImpl object is a member variable of HTTPQuery. The init method of HTTPQuery calls the init2 method of RepoQueryImpl and the destroy method of HTTPQuery calls the destroy method of RepoQueryImpl. The doPost method of HTTPQuery retrieves the user from the HttpServletRequest parameter and calls the peer web service methods.

We can now consider the case of non-HTTP protocol. The listener is responsible to

  1. Retrieve and check the user credentials

  2. Check if the user is allowed to call the service

  3. Pass the user name to the service methods


The listener is started by the Subscribe servlet that is defined in web.xml with load-on-startup, which means that its init method is called when the Repository Web application is loaded.

The listener class is defined in the listener initialization parameter and must implement a default constructor and a Repository.ListenerIF interface defined like this:

public interface ListenerIF {

public void init(javax.servlet.ServletContext ctx);

public void destroy();

}

The init method of the Subscribe servlet instantiates the listener, stores the listener instance as a member variable and calls its init method. The destroy method of the Subscribe servlet calls the destroy method of the listener.


The listener is responsible for:

  1. Instantiating a RepoQueryImpl object

  2. Calling the RepoQueryImpl init2 method with the Servlet context passed in the init method

  3. Calling the RepoQueryImpl peer service methods

  4. Calling the RepoQueryImpl destroy method when its destroy method get called

DeployIF

The service implementation, PageBoxLib.DeployImpl implements the PageBoxLib.DeployIF interface, which is defined like this:

public interface DeployIF extends TokenIF, Remote {

public class UrlUser implements java.io.Serializable {

public String url;

public String user;

public String pbUser;

public String pbPasswd;

}

public class Status {

public int code;

public String msg;

public Status(int code, String msg) {

this.code = code;

this.msg = msg;

}

public Status() {}

}

*/

public Status add(String arch, String downloadURL, String owner,

byte[] archData, String date, String oldDate, String docURL, String user,

boolean isUpdate, boolean runInstall, UrlUser[] relayed)

throws RemoteException;

public Status delete(String arch, String downloadURL, String owner,

String user, boolean runRemove, boolean keepDir) throws RemoteException;

public String rename(String oldDownloadURL, String newDownloadURL,

String user) throws RemoteException;

public String getArchPath(String arch) throws RemoteException;

public String getAudit(String arch, String owner, String user, String downloadURL)

throws RemoteException;

}

We describe TokenIF above.

DeployIF extends Remote. Therefore you can use it in JAX-RPC and RMI to generate proxies and server stubs. For the same reason the methods can throw RemoteException. When you use a protocol like raw TCP it is useful to be able to easily serialize parameters and answers. For this reason the UrlUser and TokenFrame classes implement the Serializable interface.

HTTP-based implementations are especially easy to implement for three reasons:

  • The servlet container is the actual listener. It is usually very scalable and it dispatches the requests to a servlet responsible for the request un-serialization and for the invocation of DeployImpl.

  • The servlet container usually supports SSL for encryption, hashing and server authentication.

  • The servlet container also implement access control with basic authentication

SOAP and XML-RPC implementations are even more convenient because the servlet is part of the implementation and the server stub - when needed - can be generated with a command of the SOAP implementation.

The JAX-RPC implementations we know allow generating the WSDL file from the interface. If you use a SOAP implementation that doesn’t allow it you can use this WSDL (generated from Axis):

<?xml version="1.0" encoding="UTF-8"?>

<wsdl:definitions targetNamespace="http://localhost:8080/axis/Deploy.jws" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:apachesoap="http://xml.apache.org/xml-soap" xmlns:impl="http://localhost:8080/axis/Deploy.jws" xmlns:intf="http://localhost:8080/axis/Deploy.jws" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<wsdl:message name="frameSendResponse">

<wsdl:part name="frameSendReturn" type="xsd:base64Binary"/>

</wsdl:message>

<wsdl:message name="renameRequest">

<wsdl:part name="oldDownloadURL" type="xsd:string"/>

<wsdl:part name="newDownloadURL" type="xsd:string"/>

<wsdl:part name="user" type="xsd:string"/>

</wsdl:message>

<wsdl:message name="addResponse">

<wsdl:part name="addReturn" type="xsd:base64Binary"/>

</wsdl:message>

<wsdl:message name="frameSendRequest">

<wsdl:part name="frame" type="xsd:base64Binary"/>

</wsdl:message>

<wsdl:message name="getArchPathResponse">

<wsdl:part name="getArchPathReturn" type="xsd:string"/>

</wsdl:message>

<wsdl:message name="deleteResponse">

<wsdl:part name="deleteReturn" type="xsd:base64Binary"/>

</wsdl:message>

<wsdl:message name="deleteRequest">

<wsdl:part name="arch" type="xsd:string"/>

<wsdl:part name="downloadURL" type="xsd:string"/>

<wsdl:part name="owner" type="xsd:string"/>

<wsdl:part name="user" type="xsd:string"/>

<wsdl:part name="runRemove" type="xsd:boolean"/>

<wsdl:part name="keepDir" type="xsd:boolean"/>

</wsdl:message>

<wsdl:message name="addRequest">

<wsdl:part name="arch" type="xsd:string"/>

<wsdl:part name="downloadURL" type="xsd:string"/>

<wsdl:part name="owner" type="xsd:string"/>

<wsdl:part name="archData" type="xsd:base64Binary"/>

<wsdl:part name="date" type="xsd:string"/>

<wsdl:part name="oldDate" type="xsd:string"/>

<wsdl:part name="docURL" type="xsd:string"/>

<wsdl:part name="user" type="xsd:string"/>

<wsdl:part name="isUpdate" type="xsd:boolean"/>

<wsdl:part name="runInstall" type="xsd:boolean"/>

<wsdl:part name="relayed" type="xsd:base64Binary"/>

</wsdl:message>

<wsdl:message name="getArchPathRequest">

<wsdl:part name="arch" type="xsd:string"/>

</wsdl:message>

<wsdl:message name="renameResponse">

<wsdl:part name="renameReturn" type="xsd:string"/>

</wsdl:message>

<wsdl:message name="getAuditResponse">

<wsdl:part name="getAuditReturn" type="xsd:string"/>

</wsdl:message>

<wsdl:message name="getAuditRequest">

<wsdl:part name="arch" type="xsd:string"/>

<wsdl:part name="owner" type="xsd:string"/>

<wsdl:part name="user" type="xsd:string"/>

<wsdl:part name="downloadURL" type="xsd:string"/>

</wsdl:message>

<wsdl:portType name="Deploy">

<wsdl:operation name="add" parameterOrder="arch downloadURL owner archData date oldDate docURL user isUpdate runInstall relayed">

<wsdl:input message="impl:addRequest" name="addRequest"/>

<wsdl:output message="impl:addResponse" name="addResponse"/>

</wsdl:operation>

<wsdl:operation name="delete" parameterOrder="arch downloadURL owner user runRemove keepDir">

<wsdl:input message="impl:deleteRequest" name="deleteRequest"/>

<wsdl:output message="impl:deleteResponse" name="deleteResponse"/>

</wsdl:operation>

<wsdl:operation name="rename" parameterOrder="oldDownloadURL newDownloadURL user">

<wsdl:input message="impl:renameRequest" name="renameRequest"/>

<wsdl:output message="impl:renameResponse" name="renameResponse"/>

</wsdl:operation>

<wsdl:operation name="getAudit" parameterOrder="arch owner user downloadURL">

<wsdl:input message="impl:getAuditRequest" name="getAuditRequest"/>

<wsdl:output message="impl:getAuditResponse" name="getAuditResponse"/>

</wsdl:operation>

<wsdl:operation name="getArchPath" parameterOrder="arch">

<wsdl:input message="impl:getArchPathRequest" name="getArchPathRequest"/>

<wsdl:output message="impl:getArchPathResponse" name="getArchPathResponse"/>

</wsdl:operation>

<wsdl:operation name="frameSend" parameterOrder="frame">

<wsdl:input message="impl:frameSendRequest" name="frameSendRequest"/>

<wsdl:output message="impl:frameSendResponse" name="frameSendResponse"/>

</wsdl:operation>

</wsdl:portType>

<wsdl:binding name="DeploySoapBinding" type="impl:Deploy">

<wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>

<wsdl:operation name="add">

<wsdlsoap:operation soapAction=""/>

<wsdl:input name="addRequest">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://DefaultNamespace" use="encoded"/>

</wsdl:input>

<wsdl:output name="addResponse">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost:8080/axis/Deploy.jws" use="encoded"/>

</wsdl:output>

</wsdl:operation>

<wsdl:operation name="delete">

<wsdlsoap:operation soapAction=""/>

<wsdl:input name="deleteRequest">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://DefaultNamespace" use="encoded"/>

</wsdl:input>

<wsdl:output name="deleteResponse">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost:8080/axis/Deploy.jws" use="encoded"/>

</wsdl:output>

</wsdl:operation>

<wsdl:operation name="rename">

<wsdlsoap:operation soapAction=""/>

<wsdl:input name="renameRequest">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://DefaultNamespace" use="encoded"/>

</wsdl:input>

<wsdl:output name="renameResponse">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost:8080/axis/Deploy.jws" use="encoded"/>

</wsdl:output>

</wsdl:operation>

<wsdl:operation name="getAudit">

<wsdlsoap:operation soapAction=""/>

<wsdl:input name="getAuditRequest">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://DefaultNamespace" use="encoded"/>

</wsdl:input>

<wsdl:output name="getAuditResponse">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost:8080/axis/Deploy.jws" use="encoded"/>

</wsdl:output>

</wsdl:operation>

<wsdl:operation name="getArchPath">

<wsdlsoap:operation soapAction=""/>

<wsdl:input name="getArchPathRequest">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://DefaultNamespace" use="encoded"/>

</wsdl:input>

<wsdl:output name="getArchPathResponse">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost:8080/axis/Deploy.jws" use="encoded"/>

</wsdl:output>

</wsdl:operation>

<wsdl:operation name="frameSend">

<wsdlsoap:operation soapAction=""/>

<wsdl:input name="frameSendRequest">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://DefaultNamespace" use="encoded"/>

</wsdl:input>

<wsdl:output name="frameSendResponse">

<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost:8080/axis/Deploy.jws" use="encoded"/>

</wsdl:output>

</wsdl:operation>

</wsdl:binding>

<wsdl:service name="DeployService">

<wsdl:port binding="impl:DeploySoapBinding" name="Deploy">

<wsdlsoap:address location="http://localhost:8080/axis/Deploy.jws"/>

</wsdl:port>

</wsdl:service>

</wsdl:definitions>

You may need to do minor modifications. Don’t worry about the location: it is overridden anyway by the PageBox implementation. We recommend using the Deploy.wsdl stored in the WEB-INF directory of the PageBox binary distribution. This document will not necessarily been updated in each new version.

DeployImpl is somewhat tailored for JAX-RPC. It actually implements two interfaces:

  • DeployIF

  • javax.xml.rpc.server.ServiceLifecycle

DeployImpl extends TokenImpl that implements the token-related methods (TokenIF interface, frameSend method.)

javax.xml.rpc.server.ServiceLifecycle is a lifecycle interface for a JAX-RPC service endpoint. It defines two methods, init and destroy. The JAX-RPC runtime invokes the init method after a service endpoint instance is instantiated and the destroy method when it ends the lifecycle of a service endpoint instance.

DeployImpl assumes that the parameter of the init method is of javax.xml.rpc.server.ServletEndpointContext type, which is true when the JAX-RPC runtime system is servlet container based.

DeployImpl uses the javax.xml.rpc.server.ServletEndpointContext object to get

  • The Servlet context. From the context parameter DeployImpl retrieves the initialization parameters.

  • The name of the user who made the request

This approach doesn’t work when the listener is not a JAX-RPC runtime.

To address this issue RepoQueryImpl has an init2 method that takes a ServletContext parameter

You can look at the HTTPDeploy servlet for an example of raw HTTP implementation.

Like the JAX-RPC runtime HTTPDeploy is not a listener. It just processes requests received by the servlet container. The DeployImpl object is a member variable of HTTPDeploy. The init method of HTTPDeploy calls the init2 method of DeployImpl and the destroy method of HTTPDeploy calls the destroy method of DeployImpl. The doPost method of HTTPDeploy retrieves the user from the HttpServletRequest parameter and calls the web service methods.

We can now consider the case of non-HTTP protocol. The listener is responsible to

  1. Retrieve and check the user credentials

  2. Check if the user is allowed to call the service


The listener is started by the Update servlet that is defined in web.xml with load-on-startup, which means that its init method is called when the PageBox Web application is loaded.

The listener class is defined in the listener initialization parameter and must implement a default constructor and a PageBoxLib.ListenerIF interface defined like this:

public interface ListenerIF {

public void init(javax.servlet.ServletContext ctx);

public void destroy();

}

The init method of the Update servlet instantiates the listener, stores the listener instance as a member variable and calls its init method. The destroy method of the Update servlet calls the destroy method of the listener.


The listener is responsible for:

  1. Instantiating a DeployImpl object

  2. Calling the DeployImpl init2 method with the Servlet context passed in the init method

  3. Calling the DeployImpl service methods

  4. Calling the DeployImpl destroy method when its destroy method get called

Client

RepoQuery invoker

PageBoxLib calls a Querier class. This class implements a QuerierIF interface defined like this:

public interface QuerierIF extends RepoQueryIF {

public void setUrl(String url, String user, String passwd, Log log);

}

The setUrl method setup the remote end point address and the user/password to authenticate on the end point. It has three parameters:

  1. url: remote end point address

  2. user: user id used to authenticate on the end point

  3. passwd: password used to authenticate on the end point

  4. log: instance of the PageBox logging class that we present below


The url parameter is originally computed by the Repository and contains the concatenation:

  • Of "http://" or "https://"

  • Of the host name

  • Optionally of ":" port number, port being the port where the servlet container of the Repository listens

  • Of the context path (where the Repository Web application is accessible)

  • Of the RepoQueryIF web service name as defined in web.xml by RepoQuery-ws


In case of implementation that doesn’t use HTTP you can

  1. Use RepoQuery-ws to specify the port and other needed parameters

  2. Parse the url to extract the host name and the RepoQuery-ws data

The Querier class instantiates the actual invoker of the RepoQuery service whose name is given by the most specific deployer-class element in rules.xml. The actual invoker must have a default constructor and also implement the QuerierIF interface.

Querier calls the setUrl and the RepoQueryIF methods of the actual invoker.

setUrl may be called more than once.

Deploy invoker

The Repository and PageboxLib call a Deployer class. In both cases this class implements a DeployerIF interface defined like this:

public interface DeployerIF extends DeployIF {

public void setUrl(String url, String user, String passwd, Log log);

}

The setUrl method setup the remote end point address and the user/password to authenticate on the end point. It has three parameters:

  1. url: remote end point address

  2. user: user id used to authenticate on the end point

  3. passwd: password used to authenticate on the end point

  4. log: an instance of the PageBox logging class that we present below


The url is originally computed by the PageBox and contains the concatenation:

  • Of "http://" or "https://"

  • Of the host name

  • Optionally of ":" port number, port being the port where the servlet container of the PageBox listens

  • Of the context path (where the PageBox Web application is accessible)

  • Of the DeployIF web service name as defined in web.xml by Deploy-ws


In case of implementation that doesn’t use HTTP you can

  1. Use Deploy-ws to specify the port and other needed parameters

  2. Parse the url to extract the host name and the Deploy-ws data


The Deployer class instantiates the actual invoker of the Deploy service whose name is given

  • For the Repository requests by the deployer-class initialization parameter of web.xml

  • For the PageBox requests by the most specific deployer-class element in rules.xml

The actual invoker must have a default constructor and also implement the DeployerIF interface.

Deployer calls the setUrl and the DeployIF methods of the actual invoker.

setUrl may be called more than once.

Dynamic deployment drivers

Configuration

PageBox calls the Application server to perform dynamic deployment, redeployment and undeployment if the following context parameters are set in the PageBox web.xml:

Context parameter

Meaning

getInstaller parameter

install-class

Name of a class that handles dynamic deployment, redeployment and undeployment

iname

install-url

URL of the Application server configuration facility

url

install-user

User ID to use to connect to the configuration facility

user

install-password

Password to use to connect to the configuration facility

password

Here is an example:

<context-param>

<param-name>install-url</param-name>

<param-value>http://localhost:8080/manager</param-value>

</context-param>

<context-param>

<param-name>install-class</param-name>

<param-value>PageBoxLib.TomcatInstall</param-value>

</context-param>

<context-param>

<param-name>install-user</param-name>

<param-value>pagebox</param-value>

</context-param>

<context-param>

<param-name>install-password</param-name>

<param-value>pagebox</param-value>

</context-param>

Implementation

Classes and interfaces used by dynamic deployment drivers are defined in the PageBoxLib package. For simplicity we recommend defining dynamic deployment drivers in the PageBoxLib package.

Instantiation

The init method of DeployImpl (implementation class of the Deploy Web service) retrieves the context parameters and calls ASInstallFactory.getInstaller.

The ASInstallFactory is implemented like this:

public class ASInstallFactory {

public static ASInstallIF getInstaller(String url, String iname, Log log,

String user, String password) {

ASInstallIF ai = null;

try {

Class iclass = Class.forName(iname);

ai = (ASInstallIF)iclass.newInstance();

}

catch(Exception e) {

...

return null;

}

ai.init(url, log, user, password);

return ai;

}

}

getInstaller instantiates the class specified by the install-class, which must implement the ASInstallIF interface and calls its init method.

Use

ASInstallIF is defined like this:

interface ASInstallIF {

void init(String url, Log log, String rootPath, String user, String password , boolean isRelativeTarget);

String install(String arch, String archPath);

String reload(String arch);

String remove(String arch);

}

Where:

  • url is the URL of the Application server configuration facility

  • rootPath is the root deployment path

  • user is the user ID to use to connect to the configuration facility

  • password is the password to use to connect to the configuration facility

  • arch is the name of the Web archive to deploy, redeploy or undeploy

  • archPath is the directory where the Web archive has been inflated

  • isRelativeTarget is true when the archPath in the install method is relative

  • log is an instance of the PageBox logging class

The Log class is implemented like this:

public class Log {

public void info(String msg) {

...

}

public void warn(String msg) {

...

}

public void error(String msg) {

...

}

}

error messages are displayed with a red background whereas warning messages are displayed with a yellow background. Information messages are recorded only when the tracing mode is set to info. All messages are displayed with a timestamp.

DeployImpl calls ASInstallIF methods in the following contexts:

Method

Calling method in DeployImpl

Purpose

install

install

Install a new Web archive

reload

install

Install a new version of a Web archive

remove

uninstall

Delete a Web archive

Writing a deployer

An Application server deployer must implement the PageBoxLib.ASInstallIF interface. The deployer should also use the Log object to report error, warning and information messages.

To test and use the deployer you must:

  • Make it available in the context of the PageBox Web archive in such a way that Class.forName(deployer) returns a deployer class

  • Specify the deployer class name in the install-class context parameter

Example

PageBox includes an example of deployer: the Tomcat 4 deployer, called TomcatInstall. This deployer supports JWSDP 1.2 to 1.3 and Tomcat 4.1 to 5.

In TomcatInstall the install, reload and remove methods build the corresponding query string and call a run method. The run method builds and issues the HTTP request and analyzes the response.

Resource probes

Classes and interfaces used by resource probes are defined in the PageBoxLib package. For simplicity we recommend defining resource probes in the PageBoxLib package.

A resource probe is a java class that implements the UsageIF interface:

interface UsageIF {

ResourceUsage collect();

}

The resource returns a ResourceUsage object. The ResourceUsage class is defined like this:

class ResourceUsage extends MemUsage implements Serializable {

public float perUse = Float.NEGATIVE_INFINITY;

public float perIdle = Float.NEGATIVE_INFINITY;

public float perSystem = Float.NEGATIVE_INFINITY;

public float perUser = Float.NEGATIVE_INFINITY;

ResourceUsage(String msg) {

this.msg = msg;

}

ResourceUsage() {}

}

Where:

  • perUse is the percentage of CPU used

  • perIdle is the percentage of time where CPU was idle, 100 - perUse

  • perSystem is the percentage of time spent is system mode

  • perUser is the percentage of time spent is user mode


If it is not able to measure perSystem or perUser the probe should leave the default value, Float.NEGATIVE_INFINITY. If the probe has a major error collect should return null. However in some circumstances data may be partially invalid. Then collect may return a ResourceUsage with a diagnosis message in msg. An example is "first measure" returned when the first measure is not reliable.

ResourceUsage extends MemUsage, which is defined like this:

class MemUsage implements Serializable {

public long free = -1;

public long total = -1;

public long max = -1;

public long netTime = -1;

public String msg = null;

void init(long netTime) {

...

}

}

You can override the init method that sets up the MemUsage members:

  • free is the amount of free memory in bytes. The default implementation of init uses RunTime.freeMemory and returns the amount of free memory in the Java Virtual Machine.

  • total is the total amount of memory in bytes. The default implementation of init uses RunTime.totalMemory and returns the total amount of memory in the Java Virtual Machine.

  • max is the maximum amount of memory that can be used in bytes. The default implementation of init uses RunTime.maxMemory and returns the maximum amount of memory that the Java virtual machine will attempt to use.

  • netTime is the network service time in millisecond. The default implementation of init uses the time passed in parameter, which is the time to send a frame to the next station on the ring (token API).


An overridden implementation of init could measure the Operating System memory. init can also use a different mean for measuring the network performance. If it is not able to measure one value init should leave the default value.

You must also modify UsageFactory.getUsageProbe to call your probe. For instance:

class UsageFactory {

static UsageIF ui = null;

static synchronized UsageIF getUsageProbe(Log log) {

if (ui != null)

return ui;

String os = System.getProperty("os.name").toLowerCase();

if (os.equals("linux"))

ui = new LinuxUsageProbe(log);

else

if (os.equals("windows 2000") || os.equals("windows nt") ||

os.equals("windows xp"))

ui = new WindowsUsageProbe(log);

else

if (os.equals("myOS"))

ui = new MyUsageProbe(log);

if (ui == null) {

log.warn("PageBox",

"UsageFactory.getUsageProbe Operating system not supported");

return null;

}

return ui;

}

}

Note:

  • The PageBox implementation calls the collect method of ResourceUsage and then the init method of MemUsage. The probe implementation doesn’t have to and should not call the init method.

  • A resource probe is called in the PageBoxLib context, which has all permissions. Therefore like WindowsUsageProbe custom resource probes can use a native library and JNI for measurement.

Contact:support@pagebox.net
©2002-2004 Alexis Grandemange. Last modified .