/*
Wotonomy: OpenStep design patterns for pure Java applications.
Copyright (C) 2001 Intersect Software Corporation
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see http://www.gnu.org
*/
package net.wotonomy.web.xml;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Hashtable;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.wotonomy.foundation.NSSelector;
import net.wotonomy.foundation.internal.WotonomyException;
/**
* A servlet that can make any java object into an XML-RPC server. Simply pass
* in the object to the constructor and XML-RPC requests to this servlet will
* call the appropriate methods and convert the results to an XML-RPC response.
*
*
*
* Depending on your servlet container, it may be necessary to create a simple
* subclass that creates your handler object in its constructor and then calls
* setHandler().
*
*
* Responses are in the specification's standard response format.
*
*
* Faults are returned if any exception is thrown in the method, or if the
* specified method is not found on the object. The fault string is the toString
* value of the exception, and the fault code is the hasCode value of the
* exception.
*
*
* Remember that this servlet only responds to POSTs, per the XML-RPC spec.
*/
public class XMLRPCServlet extends HttpServlet {
protected Object handler;
protected boolean synchronizing;
private Hashtable selectorCache = new Hashtable(); // thread safe
private boolean copyStream = false;
/**
* Default constructor initializes internal state.
*/
public XMLRPCServlet() {
handler = null;
synchronizing = false;
}
/**
* Constructor takes any java object and allows its methods to be invoked via
* XMLRPC requests to this servlet. Simply calls setHandler().
*/
public XMLRPCServlet(Object aHandler) {
this();
setHandler(aHandler);
}
/**
* Gets the object whose methods will be invoked to handle incoming requests.
*/
public Object getHandler() {
return handler;
}
/**
* Sets the object whose methods will be invoked to handle incoming requests.
*/
public void setHandler(Object aHandler) {
handler = aHandler;
}
/**
* Gets whether the servlet should synchonize on the object before invoking
* methods on it. Defaults to false.
*/
public boolean isSynchronizing() {
return synchronizing;
}
/**
* Sets whether the servlet should synchonize on the object before invoking
* methods on it. Defaults to false.
*/
public void setSynchronizing(boolean willSynchronize) {
synchronizing = willSynchronize;
}
/**
* Overridden to service the request.
*/
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if (getHandler() != null) {
InputStream input = req.getInputStream();
byte[] copyOfRequest = null;
if (copyStream) {
ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
int b;
while ((b = input.read()) != -1) {
byteArray.write(b);
}
copyOfRequest = byteArray.toByteArray();
input = new ByteArrayInputStream(copyOfRequest);
}
try {
new XMLRPCDecoder().decode(input, new Receiver(this, resp));
} catch (WotonomyException exc) {
if (copyOfRequest != null) {
System.out.println(new String(copyOfRequest));
exc.printStackTrace();
}
// catches io exceptions thrown in handleRequest.
Throwable t = exc.getWrappedThrowable();
if (t instanceof IOException) {
throw (IOException) t;
}
throw exc;
}
}
}
/**
* Called by doPost after parsing an incoming request, and is responsible for
* invoking the specified method with the specified parameters on the handler
* object. (This implementation calls getOutputStream() on the response.)
* Override to customize the handling of the request.
*/
protected void handleRequest(String aMethodName, Object[] aParameterArray, HttpServletResponse aResponse) {
OutputStream output = null;
try {
output = aResponse.getOutputStream();
aResponse.setStatus(HttpServletResponse.SC_OK); // always 200
aResponse.setContentType("text/xml");
} catch (IOException exc) {
// caught in doPost
throw new WotonomyException(exc);
}
// get the array of types
XMLRPCEncoder encoder = new XMLRPCEncoder();
Class[] types = new Class[aParameterArray.length];
for (int i = 0; i < aParameterArray.length; i++) {
types[i] = aParameterArray[i].getClass();
}
// TODO: selectors should be cached if possible
Object handler = getHandler();
if (isSynchronizing()) {
synchronized (handler) {
execute(encoder, handler, output, new NSSelector(aMethodName, types), aParameterArray);
}
} else {
execute(encoder, handler, output, new NSSelector(aMethodName, types), aParameterArray);
}
}
private void execute(XMLRPCEncoder anEncoder, Object aHandler, OutputStream output, NSSelector aSelector,
Object[] aParameterArray) {
try {
Object result = aSelector.invoke(aHandler, aParameterArray);
anEncoder.encodeResponse(result, output);
} catch (Exception exc) {
anEncoder.encodeFault(exc.hashCode(), exc.toString(), output);
}
}
private class Receiver implements XMLRPCReceiver {
XMLRPCServlet controller;
HttpServletResponse response;
public Receiver(XMLRPCServlet aController, HttpServletResponse aResponse) {
controller = aController;
response = aResponse;
}
public void request(String aMethodName, Object[] aParameterArray) {
controller.handleRequest(aMethodName, aParameterArray, response);
}
public void response(Object aResult) {
// does nothing
}
public void fault(int aFaultCode, String aFaultString) {
// does nothing
}
}
}
/*
* $Log$ Revision 1.1 2006/02/19 01:44:02 cgruber Add xmlrpc files Remove jclark
* and replace with dom4j and javax.xml.sax stuff Re-work dependencies and
* imports so it all compiles.
*
* Revision 1.1 2006/02/16 13:22:22 cgruber Check in all sources in
* eclipse-friendly maven-enabled packages.
*
* Revision 1.1 2001/02/07 19:24:28 mpowers Moved XML classes to separate
* package.
*
*/