mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

ctissot
20.55.2007 c2beb1994315ebd33bbdd186a3c97d62a194a9ec
opends/src/dsml/org/opends/dsml/protocol/DSMLServlet.java
@@ -27,7 +27,14 @@
package org.opends.dsml.protocol;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.text.ParseException;
import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI;
import javax.xml.bind.JAXBException;
import org.opends.messages.Message;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.ldap.LDAPResultCode;
import org.opends.server.tools.LDAPConnection;
import org.opends.server.tools.LDAPConnectionOptions;
import org.opends.server.util.Base64;
@@ -47,11 +54,21 @@
import javax.xml.soap.*;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.concurrent.atomic.AtomicInteger;
import javax.xml.validation.SchemaFactory;
import org.opends.server.tools.LDAPConnectionException;
import org.opends.server.types.LDAPException;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
/**
@@ -67,15 +84,33 @@
  private static final long serialVersionUID = -3748022009593442973L;
  private static final AtomicInteger nextMessageID = new AtomicInteger(1);
  // definitions of return error messages
  private static final String MALFORMED_REQUEST = "malformedRequest";
  private static final String NOT_ATTEMPTED = "notAttempted";
  private static final String AUTHENTICATION_FAILED = "authenticationFailed";
  private static final String COULD_NOT_CONNECT = "couldNotConnect";
  private static final String GATEWAY_INTERNAL_ERROR = "gatewayInternalError";
  private static final String UNKNOWN_ERROR = "Unknown error";
  // definitions of onError values
  private static final String ON_ERROR_RESUME = "resume";
  private static final String ON_ERROR_EXIT = "exit";
  private Unmarshaller unmarshaller;
  private Marshaller marshaller;
  private ObjectFactory objFactory;
  private MessageFactory messageFactory;
  private DocumentBuilder db;
  // this extends the default handler of SAX parser. It helps to retrieve the
  // requestID value when the xml request is malformed and thus unparsable
  // using SOAP or JAXB.
  private DSMLContentHandler contentHandler;
  private String hostName;
  private Integer port;
  /**
   * This method will be called by the Servlet Container when
   * this servlet is being placed into service.
@@ -85,19 +120,22 @@
   * @throws ServletException If an error occurs during processing.
   */
  public void init(ServletConfig config) throws ServletException {
    try {
      hostName = config.getServletContext().getInitParameter(HOST);
      port = new Integer(config.getServletContext().getInitParameter(PORT));
      JAXBContext jaxbContext = JAXBContext.newInstance(PKG_NAME);
      unmarshaller = jaxbContext.createUnmarshaller();
      // assign the DSMLv2 schema for validation
      URL schema = getClass().getResource("/resources/DSMLv2.xsd");
      if ( schema != null ) {
        SchemaFactory sf = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI);
        unmarshaller.setSchema(sf.newSchema(schema));
      }
      marshaller = jaxbContext.createMarshaller();
      marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper",
        new NamespacePrefixMapperImpl());
      objFactory = new ObjectFactory();
      messageFactory = MessageFactory.newInstance();
@@ -105,29 +143,15 @@
      dbf.setNamespaceAware(true);
      db = dbf.newDocumentBuilder();
      DirectoryServer.bootstrapClient();
      this.contentHandler = new DSMLContentHandler();
      DirectoryServer.bootstrapClient();
    } catch (Exception je) {
      je.printStackTrace();
      throw new ServletException(je.getMessage());
    }
  }
  /**
   * The HTTP GET operation.
   *
   * @param req Information about the request received from the client.
   * @param res Information about the response to send to the client.
   * @throws ServletException If an error occurs during servlet processing.
   * @throws IOException      If an error occurs while interacting with the client.
   */
  public void doGet(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException {
    super.doGet(req, res);
  }
  /**
   * The HTTP POST operation. This servlet expects a SOAP message
   * with a DSML request payload.
@@ -135,222 +159,370 @@
   * @param req Information about the request received from the client.
   * @param res Information about the response to send to the client.
   * @throws ServletException If an error occurs during servlet processing.
   * @throws IOException      If an error occurs while interacting with the client.
   * @throws IOException   If an error occurs while interacting with the client.
   */
  public void doPost(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException {
    SOAPMessage reply;
  throws ServletException, IOException {
    LDAPConnectionOptions connOptions = new LDAPConnectionOptions();
    LDAPConnection connection = null;
    BatchRequest batchRequest = null;
    // Keep the Servlet input stream buffered in case the SOAP unmarshalling
    // fails, the SAX parsing will be able to retrieve the requestID even if
    // the XML is malmformed by resetting the input stream.
    BufferedInputStream is = new BufferedInputStream(req.getInputStream(),
                                                     65536);
    if ( is.markSupported() ) {
      is.mark(65536);
    }
    try {
      MimeHeaders mimeHeaders = new MimeHeaders();
      Enumeration en = req.getHeaderNames();
      String bindDN = "";
      String bindPassword = "";
      while (en.hasMoreElements()) {
        String headerName = (String) en.nextElement();
        String headerVal = req.getHeader(headerName);
        if (headerName.equalsIgnoreCase("authorization")) {
          if (headerVal.startsWith("Basic ")) {
            String authorization = headerVal.substring(6).trim();
            // Decode and parse the authorization credentials
            String unencoded =
              new String(Base64.decode(authorization));
    // Create response in the beginning as it might be used if the parsing
    // failes.
    BatchResponse batchResponse = objFactory.createBatchResponse();
    List<JAXBElement<?>> batchResponses = batchResponse.getBatchResponses();
    Document doc = db.newDocument();
    SOAPBody soapBody = null;
    MimeHeaders mimeHeaders = new MimeHeaders();
    Enumeration en = req.getHeaderNames();
    String bindDN = null;
    String bindPassword = null;
    boolean authorizationInHeader = false;
    while (en.hasMoreElements()) {
      String headerName = (String) en.nextElement();
      String headerVal = req.getHeader(headerName);
      if (headerName.equalsIgnoreCase("authorization")) {
        if (headerVal.startsWith("Basic ")) {
          authorizationInHeader = true;
          String authorization = headerVal.substring(6).trim();
          try {
            String unencoded = new String(Base64.decode(authorization));
            int colon = unencoded.indexOf(':');
            if (colon < 0)
              continue;
            bindDN = unencoded.substring(0, colon).trim();
            bindPassword = unencoded.substring(colon + 1);
            if (colon > 0) {
              bindDN = unencoded.substring(0, colon).trim();
              bindPassword = unencoded.substring(colon + 1);
            }
          } catch (ParseException ex) {
            // DN:password parsing error
            batchResponses.add(
              createErrorResponse(
                    new LDAPException(LDAPResultCode.INVALID_CREDENTIALS,
                    Message.raw(ex.getMessage()))));
            break;
          }
        }
        StringTokenizer tk = new StringTokenizer(headerVal, ",");
        while (tk.hasMoreTokens()) {
          mimeHeaders.addHeader(headerName, tk.nextToken().trim());
        }
      }
      StringTokenizer tk = new StringTokenizer(headerVal, ",");
      while (tk.hasMoreTokens()) {
        mimeHeaders.addHeader(headerName, tk.nextToken().trim());
      }
    }
      SOAPMessage message =
        messageFactory.createMessage(mimeHeaders, req.getInputStream());
      message.writeTo(System.out);
    if ( ! authorizationInHeader ) {
      // if no authorization, set default user
      bindDN = "";
      bindPassword = "";
    } else {
      // otherwise if DN or password is null, send back an error
      if ( (bindDN == null || bindPassword == null)
         && batchResponses.size()==0) {
        batchResponses.add(
              createErrorResponse(
                    new LDAPException(LDAPResultCode.INVALID_CREDENTIALS,
                    Message.raw("Unable to retrieve credentials."))));
      }
    }
      Document doc = db.newDocument();
      SOAPBody body = message.getSOAPBody();
    // if an error already occured, the list is not empty
    if ( batchResponses.size() == 0 ) {
      try {
        SOAPMessage message = messageFactory.createMessage(mimeHeaders, is);
        soapBody = message.getSOAPBody();
      } catch (SOAPException ex) {
        // SOAP was unable to parse XML successfully
        batchResponses.add(
          createXMLParsingErrorResponse(is,
                                        batchResponse,
                                        String.valueOf(ex.getCause())));
      }
    }
      Iterator it = body.getChildElements();
    if ( soapBody != null ) {
      Iterator it = soapBody.getChildElements();
      while (it.hasNext()) {
        Object obj = it.next();
        if (!(obj instanceof SOAPElement)) {
          continue;
        }
        SOAPElement se = (SOAPElement) obj;
        JAXBElement<BatchRequest> batchRequestElement =
          unmarshaller.unmarshal(se, BatchRequest.class);
        BatchRequest batchRequest = batchRequestElement.getValue();
        BatchResponse batchResponse = objFactory.createBatchResponse();
        List<JAXBElement<?>> batchResponses = batchResponse.getBatchResponses();
        List<DsmlMessage> list = batchRequest.getBatchRequests();
        for (DsmlMessage nextElement : list) {
          if (nextElement instanceof SearchRequest) {
            // Process the search request.
        JAXBElement<BatchRequest> batchRequestElement = null;
        try {
          batchRequestElement = unmarshaller.unmarshal(se, BatchRequest.class);
        } catch (JAXBException e) {
          // schema validation failed
          batchResponses.add(createXMLParsingErrorResponse(is,
                                                       batchResponse,
                                                       String.valueOf(e)));
        }
        if ( batchRequestElement != null ) {
          batchRequest = batchRequestElement.getValue();
          // set requestID in response
          batchResponse.setRequestID(batchRequest.getRequestID());
          boolean connected = false;
          if ( connection == null ) {
            connection = new LDAPConnection(hostName, port, connOptions);
            connection.connectToHost(bindDN, bindPassword);
            SearchRequest sr = (SearchRequest) nextElement;
            DSMLSearchOperation ds = new DSMLSearchOperation(connection);
            SearchResponse searchResponse = ds.doSearch(objFactory, sr);
            JAXBElement<SearchResponse> searchResponseEl =
              objFactory.createBatchResponseSearchResponse(searchResponse);
            batchResponses.add(searchResponseEl);
          } else if (nextElement instanceof AddRequest) {
            // Process the add request.
            connection = new LDAPConnection(hostName, port, connOptions);
            connection.connectToHost(bindDN, bindPassword);
            AddRequest ar = (AddRequest) nextElement;
            DSMLAddOperation addOp = new DSMLAddOperation(connection);
            LDAPResult addResponse = addOp.doOperation(objFactory, ar);
            JAXBElement<LDAPResult> addResponseEl =
              objFactory.createBatchResponseAddResponse(addResponse);
            batchResponses.add(addResponseEl);
          } else if (nextElement instanceof AbandonRequest) {
            // Process the abandon request.
            connection = new LDAPConnection(hostName, port, connOptions);
            connection.connectToHost(bindDN, bindPassword);
            AbandonRequest ar = (AbandonRequest) nextElement;
            DSMLAbandonOperation ao = new DSMLAbandonOperation(connection);
            LDAPResult abandonResponse = ao.doOperation(objFactory, ar);
          } else if (nextElement instanceof ExtendedRequest) {
            // Process the extended request.
            connection = new LDAPConnection(hostName, port, connOptions);
            connection.connectToHost(bindDN, bindPassword);
            ExtendedRequest er = (ExtendedRequest) nextElement;
            DSMLExtendedOperation eo = new DSMLExtendedOperation(connection);
            ExtendedResponse extendedResponse = eo.doOperation(objFactory, er);
            JAXBElement<ExtendedResponse> extendedResponseEl =
              objFactory.createBatchResponseExtendedResponse(extendedResponse);
            batchResponses.add(extendedResponseEl);
          } else if (nextElement instanceof DelRequest) {
            // Process the delete request.
            connection = new LDAPConnection(hostName, port, connOptions);
            connection.connectToHost(bindDN, bindPassword);
            DelRequest dr = (DelRequest) nextElement;
            DSMLDeleteOperation delOp = new DSMLDeleteOperation(connection);
            LDAPResult delResponse = delOp.doOperation(objFactory, dr);
            JAXBElement<LDAPResult> delResponseEl =
              objFactory.createBatchResponseDelResponse(delResponse);
            batchResponses.add(delResponseEl);
          } else if (nextElement instanceof CompareRequest) {
            // Process the compare request.
            connection = new LDAPConnection(hostName, port, connOptions);
            connection.connectToHost(bindDN, bindPassword);
            CompareRequest cr = (CompareRequest) nextElement;
            DSMLCompareOperation compareOp =
              new DSMLCompareOperation(connection);
            LDAPResult compareResponse = compareOp.doOperation(objFactory, cr);
            JAXBElement<LDAPResult> compareResponseEl =
              objFactory.createBatchResponseCompareResponse(compareResponse);
            batchResponses.add(compareResponseEl);
          } else if (nextElement instanceof ModifyDNRequest) {
            // Process the Modify DN request.
            connection = new LDAPConnection(hostName, port, connOptions);
            connection.connectToHost(bindDN, bindPassword);
            ModifyDNRequest mr = (ModifyDNRequest) nextElement;
            DSMLModifyDNOperation moddnOp =
              new DSMLModifyDNOperation(connection);
            LDAPResult moddnResponse = moddnOp.doOperation(objFactory, mr);
            JAXBElement<LDAPResult> moddnResponseEl =
              objFactory.createBatchResponseModDNResponse(moddnResponse);
            batchResponses.add(moddnResponseEl);
          } else if (nextElement instanceof ModifyRequest) {
            // Process the Modify request.
            connection = new LDAPConnection(hostName, port, connOptions);
            connection.connectToHost(bindDN, bindPassword);
            ModifyRequest modr = (ModifyRequest) nextElement;
            DSMLModifyOperation modOp = new DSMLModifyOperation(connection);
            LDAPResult modResponse = modOp.doOperation(objFactory, modr);
            JAXBElement<LDAPResult> modResponseEl =
              objFactory.createBatchResponseModifyResponse(modResponse);
            batchResponses.add(modResponseEl);
          } else {
            String msg = "Invalid DSML request:" + nextElement;
            throw new IOException(msg);
            try {
              connection.connectToHost(bindDN, bindPassword);
              connected = true;
            } catch (LDAPConnectionException e) {
              // if connection failed, return appropriate error response
              batchResponses.add(createErrorResponse(e));
            }
          }
          if ( connected ) {
            List<DsmlMessage> list = batchRequest.getBatchRequests();
            for (DsmlMessage request : list) {
              JAXBElement<?> result = performLDAPRequest(connection, request);
              if ( result != null ) {
                batchResponses.add(result);
              }
              // evaluate response to check if an error occured
              Object o = result.getValue();
              if ( o instanceof ErrorResponse ) {
                if ( ON_ERROR_EXIT.equals(batchRequest.getOnError()) ) {
                  break;
                }
              } else if ( o instanceof LDAPResult ) {
                int code = ((LDAPResult)o).getResultCode().getCode();
                if ( code != LDAPResultCode.SUCCESS
                  && code != LDAPResultCode.REFERRAL
                  && code != LDAPResultCode.COMPARE_TRUE
                  && code != LDAPResultCode.COMPARE_FALSE ) {
                  if ( ON_ERROR_EXIT.equals(batchRequest.getOnError()) ) {
                    break;
                  }
                }
              }
            }
          }
          // close connection to LDAP server
          if ( connection != null ) {
            connection.close(nextMessageID);
          }
        }
        JAXBElement<BatchResponse> batchResponseElement =
          objFactory.createBatchResponse(batchResponse);
        marshaller.marshal(batchResponseElement, System.out);
        marshaller.marshal(batchResponseElement, doc);
      }
      // Send the DSML response back to the client.
      reply = messageFactory.createMessage();
      sendResponse(doc, false, reply, res, null);
    } catch (Exception se) {
      se.printStackTrace();
      // send SOAP fault
      try {
        reply = messageFactory.createMessage();
        sendResponse(null, true, reply, res, se);
      } catch (Exception e) {
      }
    } finally {
      if (connection != null) {
        connection.close(nextMessageID);
      }
    }
    try {
      marshaller.marshal(objFactory.createBatchResponse(batchResponse), doc);
      sendResponse(doc, res);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
  /**
   * Returns an error response after a parsing error. The response has the
   * requestID of the batch request, the error response message of the parsing
   * exception message and the type 'malformed request'.
   *
   * @param is the xml InputStream to parse
   * @param batchResponse the JAXB object to fill in
   * @param parserErrorMessage the parsing error message
   *
   * @return a JAXBElement that contains an ErrorResponse
   */
  private JAXBElement<ErrorResponse> createXMLParsingErrorResponse(
                                                    InputStream is,
                                                    BatchResponse batchResponse,
                                                    String parserErrorMessage) {
    ErrorResponse errorResponse = objFactory.createErrorResponse();
    try {
      // try alternative XML parsing using SAX to retrieve requestID value
      XMLReader xmlReader = XMLReaderFactory.createXMLReader();
      // clear previous match
      this.contentHandler.requestID = null;
      xmlReader.setContentHandler(this.contentHandler);
      is.reset();
      xmlReader.parse(new InputSource(is));
    } catch (Throwable e) {
      // document is unparsable so will jump here
    }
    if ( parserErrorMessage!= null ) {
      errorResponse.setMessage(parserErrorMessage);
    }
    batchResponse.setRequestID(this.contentHandler.requestID);
    errorResponse.setType(MALFORMED_REQUEST);
    return objFactory.createBatchResponseErrorResponse(errorResponse);
  }
  /**
   * Returns an error response with attributes set according to the exception
   * provided as argument.
   *
   * @param t the exception that occured
   *
   * @return a JAXBElement that contains an ErrorResponse
   */
  private JAXBElement<ErrorResponse> createErrorResponse(Throwable t) {
    // potential exceptions are IOException, LDAPException, ASN1Exception
    ErrorResponse errorResponse = objFactory.createErrorResponse();
    errorResponse.setMessage(String.valueOf(t));
    if ( t instanceof LDAPException ) {
      switch(((LDAPException)t).getResultCode()) {
        case LDAPResultCode.AUTHORIZATION_DENIED:
        case LDAPResultCode.INAPPROPRIATE_AUTHENTICATION:
        case LDAPResultCode.INVALID_CREDENTIALS:
        case LDAPResultCode.STRONG_AUTH_REQUIRED:
          errorResponse.setType(AUTHENTICATION_FAILED);
          break;
        case LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR:
          errorResponse.setType(COULD_NOT_CONNECT);
          break;
        case LDAPResultCode.UNWILLING_TO_PERFORM:
          errorResponse.setType(NOT_ATTEMPTED);
          break;
        default:
          errorResponse.setType(UNKNOWN_ERROR);
          break;
      }
    } else if ( t instanceof LDAPConnectionException ) {
      errorResponse.setType(COULD_NOT_CONNECT);
    } else {
      errorResponse.setType(GATEWAY_INTERNAL_ERROR);
    }
    return objFactory.createBatchResponseErrorResponse(errorResponse);
  }
  /**
   * Performs the LDAP operation and sends back the result (if any). In case
   * of error, an error reponse is returned.
   *
   * @param connection a connected connection
   * @param request the JAXB request to perform
   *
   * @return null for an abandon request, the expect result for all other
   *         requests or an error in case of unexpected behaviour.
   */
  private JAXBElement<?> performLDAPRequest(LDAPConnection connection,
                                            DsmlMessage request) {
    try {
      if (request instanceof SearchRequest) {
        // Process the search request.
        SearchRequest sr = (SearchRequest) request;
        DSMLSearchOperation ds = new DSMLSearchOperation(connection);
        SearchResponse searchResponse = ds.doSearch(objFactory, sr);
        return objFactory.createBatchResponseSearchResponse(searchResponse);
      } else if (request instanceof AddRequest) {
        // Process the add request.
        AddRequest ar = (AddRequest) request;
        DSMLAddOperation addOp = new DSMLAddOperation(connection);
        LDAPResult addResponse = addOp.doOperation(objFactory, ar);
        return objFactory.createBatchResponseAddResponse(addResponse);
      } else if (request instanceof AbandonRequest) {
        // Process the abandon request.
        AbandonRequest ar = (AbandonRequest) request;
        DSMLAbandonOperation ao = new DSMLAbandonOperation(connection);
        LDAPResult abandonResponse = ao.doOperation(objFactory, ar);
        return null;
      } else if (request instanceof ExtendedRequest) {
        // Process the extended request.
        ExtendedRequest er = (ExtendedRequest) request;
        DSMLExtendedOperation eo = new DSMLExtendedOperation(connection);
        ExtendedResponse extendedResponse = eo.doOperation(objFactory, er);
        return objFactory.createBatchResponseExtendedResponse(extendedResponse);
      } else if (request instanceof DelRequest) {
        // Process the delete request.
        DelRequest dr = (DelRequest) request;
        DSMLDeleteOperation delOp = new DSMLDeleteOperation(connection);
        LDAPResult delResponse = delOp.doOperation(objFactory, dr);
        return objFactory.createBatchResponseDelResponse(delResponse);
      } else if (request instanceof CompareRequest) {
        // Process the compare request.
        CompareRequest cr = (CompareRequest) request;
        DSMLCompareOperation compareOp =
                new DSMLCompareOperation(connection);
        LDAPResult compareResponse = compareOp.doOperation(objFactory, cr);
        return objFactory.createBatchResponseCompareResponse(compareResponse);
      } else if (request instanceof ModifyDNRequest) {
        // Process the Modify DN request.
        ModifyDNRequest mr = (ModifyDNRequest) request;
        DSMLModifyDNOperation moddnOp =
                new DSMLModifyDNOperation(connection);
        LDAPResult moddnResponse = moddnOp.doOperation(objFactory, mr);
        return objFactory.createBatchResponseModDNResponse(moddnResponse);
      } else if (request instanceof ModifyRequest) {
        // Process the Modify request.
        ModifyRequest modr = (ModifyRequest) request;
        DSMLModifyOperation modOp = new DSMLModifyOperation(connection);
        LDAPResult modResponse = modOp.doOperation(objFactory, modr);
        return objFactory.createBatchResponseModifyResponse(modResponse);
      } else if (request instanceof AuthRequest) {
        // Process the Auth request.
        // Only returns an BatchReponse with an AuthResponse containing the
        // LDAP result code AUTH_METHOD_NOT_SUPPORTED
        ResultCode resultCode = objFactory.createResultCode();
        resultCode.setCode(LDAPResultCode.AUTH_METHOD_NOT_SUPPORTED);
        LDAPResult ldapResult = objFactory.createLDAPResult();
        ldapResult.setResultCode(resultCode);
        return objFactory.createBatchResponseAuthResponse(ldapResult);
      }
    } catch (Throwable t) {
      return createErrorResponse(t);
    }
    // should never happen as the schema was validated
    return null;
  }
  /**
   * Send a response back to the client. This could be either a SOAP fault
   * or a correct DSML response.
   *
   * @param doc   The document to include in the response.
   * @param error Indicates whether an error occurred.
   * @param reply The reply to send to the client.
   * @param res   Information about the HTTP response to the client.
   * @param e     Information about any exception that was thrown.
   *
   * @throws IOException   If an error occurs while interacting with the client.
   * @throws SOAPException If an encoding or decoding error occurs.
   */
  private void sendResponse(Document doc, boolean error, SOAPMessage reply,
                            HttpServletResponse res, Exception e)
  private void sendResponse(Document doc, HttpServletResponse res)
    throws IOException, SOAPException {
    SOAPMessage reply = messageFactory.createMessage();
    SOAPHeader header = reply.getSOAPHeader();
    header.detachNode();
    SOAPBody replyBody = reply.getSOAPBody();
    res.setHeader("Content-Type", "text/xml");
    if (error) {
      SOAPFault fault = replyBody.addFault();
      Name faultName = SOAPFactory.newInstance().createName("Server",
        "", SOAPConstants.URI_NS_SOAP_ENVELOPE);
      fault.setFaultCode(faultName);
      fault.setFaultString("Server Error: " + e.getMessage());
      // FIXME - Set correct fault actor
      fault.setFaultActor("http://localhost:8080");
    } else {
      SOAPElement bodyElement = replyBody.addDocument(doc);
    }
    SOAPElement bodyElement = replyBody.addDocument(doc);
    reply.saveChanges();
    OutputStream os = res.getOutputStream();
    reply.writeTo(os);
    os.flush();
  }
  /**
   * Retrieves a message ID that may be used for the next LDAP message sent to
   * the Directory Server.
@@ -358,15 +530,32 @@
   * @return  A message ID that may be used for the next LDAP message sent to
   *          the Directory Server.
   */
  public static int nextMessageID()
  {
  public static int nextMessageID() {
    int nextID = nextMessageID.getAndIncrement();
    if (nextID == Integer.MAX_VALUE)
    {
    if (nextID == Integer.MAX_VALUE) {
      nextMessageID.set(1);
    }
    return nextID;
  }
  /**
   * This class is used when a xml request is malformed to retrieve the
   * requestID value using an event xml parser.
   */
  private static class DSMLContentHandler extends DefaultHandler {
    private String requestID;
    /*
     * This function fetches the requestID value of the batchRequest xml
     * element and call the default implementation (super).
     */
    public void startElement(String uri, String localName, String qName,
                             Attributes attributes) throws SAXException {
      if ( requestID==null && localName.equals("batchRequest") ) {
        requestID = attributes.getValue("requestID");
      }
      super.startElement(uri, localName, qName, attributes);
    }
  }
}