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

Chris Ridd
28.55.2015 738129144d5fee485f79a3078b3ad39b78518b14
CR-6671 OPENDJ-1915 make DSML servlet threadsafe
1 files modified
95 ■■■■ changed files
opendj-server-legacy/src/dsml/org/opends/dsml/protocol/DSMLServlet.java 95 ●●●● patch | view | raw | blame | history
opendj-server-legacy/src/dsml/org/opends/dsml/protocol/DSMLServlet.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2006-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011-2015 ForgeRock AS
 *      Portions Copyright 2011-2015 ForgeRock AS.
 */
package org.opends.dsml.protocol;
@@ -140,19 +140,8 @@
  private static final String ON_ERROR_EXIT = "exit";
  private static JAXBContext jaxbContext;
  private ObjectFactory objFactory;
  private static Schema schema;
  private MessageFactory messageFactory;
  private MessageFactory messageFactorySOAP_1_1;
  private MessageFactory messageFactorySOAP_1_2;
  private String contentType;
  /**
   * 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;
  /** Prevent multiple logging when trying to set unavailable/unsupported parser features */
  private static AtomicBoolean logFeatureWarnings = new AtomicBoolean(false);
@@ -167,7 +156,6 @@
  private Boolean trustAll;
  private Boolean useHTTPAuthzID;
  private HashSet<String> exopStrings = new HashSet<String>();
  private org.opends.server.types.Control proxyAuthzControl;
  /**
   * This method will be called by the Servlet Container when
@@ -239,12 +227,6 @@
        }
      }
      objFactory = new ObjectFactory();
      messageFactorySOAP_1_1 = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
      messageFactorySOAP_1_2 = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
      this.contentHandler = new DSMLContentHandler();
      DirectoryServer.bootstrapClient();
    } catch (Exception je) {
      je.printStackTrace();
@@ -353,10 +335,16 @@
    // Create response in the beginning as it might be used if the parsing
    // fails.
    ObjectFactory objFactory = new ObjectFactory();
    BatchResponse batchResponse = objFactory.createBatchResponse();
    List<JAXBElement<?>> batchResponses = batchResponse.getBatchResponses();
    // Thi sis only used for building the response
    Document doc = createSafeDocument();
    MessageFactory messageFactory = null;
    String messageContentType = null;
    if (useSSL || useStartTLS)
    {
      SSLConnectionFactory sslConnectionFactory = new SSLConnectionFactory();
@@ -368,7 +356,7 @@
      catch(SSLConnectionException e)
      {
        batchResponses.add(
          createErrorResponse(
          createErrorResponse(objFactory,
            new LDAPException(LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR,
              LocalizableMessage.raw(
              "Invalid SSL or TLS configuration to connect to LDAP server."))));
@@ -388,14 +376,25 @@
      String headerName = (String) en.nextElement();
      String headerVal = req.getHeader(headerName);
      if (headerName.equalsIgnoreCase("content-type")) {
        if (headerVal.startsWith(SOAPConstants.SOAP_1_1_CONTENT_TYPE)) {
          messageFactory = messageFactorySOAP_1_1;
          contentType = SOAPConstants.SOAP_1_1_CONTENT_TYPE;
        } else if (headerVal.startsWith(SOAPConstants.SOAP_1_2_CONTENT_TYPE)) {
          messageFactory = messageFactorySOAP_1_2;
          contentType = SOAPConstants.SOAP_1_2_CONTENT_TYPE;
        } else {
          throw new ServletException("Content-Type does not match SOAP 1.1 or SOAP 1.2");
        try
        {
          if (headerVal.startsWith(SOAPConstants.SOAP_1_1_CONTENT_TYPE))
          {
            messageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
            messageContentType = SOAPConstants.SOAP_1_1_CONTENT_TYPE;
          }
          else if (headerVal.startsWith(SOAPConstants.SOAP_1_2_CONTENT_TYPE))
          {
            MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
            messageContentType = SOAPConstants.SOAP_1_2_CONTENT_TYPE;
          }
          else {
            throw new ServletException("Content-Type does not match SOAP 1.1 or SOAP 1.2");
          }
        }
        catch (SOAPException e)
        {
          throw new ServletException(e.getmessage());
        }
      } else if (headerName.equalsIgnoreCase("authorization") && headerVal.startsWith("Basic "))
      {
@@ -421,7 +420,7 @@
        } catch (ParseException ex) {
          // user/DN:password parsing error
          batchResponses.add(
            createErrorResponse(
            createErrorResponse(objFactory,
                  new LDAPException(LDAPResultCode.INVALID_CREDENTIALS,
                  LocalizableMessage.raw(ex.getMessage()))));
          break;
@@ -445,7 +444,7 @@
        else
        {
          batchResponses.add(
              createErrorResponse(
              createErrorResponse(objFactory,
                    new LDAPException(LDAPResultCode.INVALID_CREDENTIALS,
                    LocalizableMessage.raw("Invalid configured credentials."))));
        }
@@ -460,7 +459,7 @@
      if (((!authenticationIsID && (bindDN == null)) || bindPassword == null)
         && batchResponses.isEmpty()) {
        batchResponses.add(
              createErrorResponse(
              createErrorResponse(objFactory,
                    new LDAPException(LDAPResultCode.INVALID_CREDENTIALS,
                    LocalizableMessage.raw("Unable to retrieve credentials."))));
      }
@@ -475,6 +474,7 @@
        // SOAP was unable to parse XML successfully
        batchResponses.add(
          createXMLParsingErrorResponse(is,
                                        objFactory,
                                        batchResponse,
                                        String.valueOf(ex.getCause())));
      }
@@ -499,6 +499,7 @@
        } catch (JAXBException e) {
          // schema validation failed
          batchResponses.add(createXMLParsingErrorResponse(is,
                                                       objFactory,
                                                       batchResponse,
                                                       String.valueOf(e)));
        }
@@ -524,6 +525,7 @@
          }
          // set requestID in response
          batchResponse.setRequestID(batchRequest.getRequestID());
          org.opends.server.types.Control proxyAuthzControl = null;
          boolean connected = false;
@@ -549,14 +551,14 @@
              connected = true;
            } catch (LDAPConnectionException e) {
              // if connection failed, return appropriate error response
              batchResponses.add(createErrorResponse(e));
              batchResponses.add(createErrorResponse(objFactory, e));
            }
          }
          if ( connected ) {
            List<DsmlMessage> list = batchRequest.getBatchRequests();
            for (DsmlMessage request : list) {
              JAXBElement<?> result = performLDAPRequest(connection, request);
              JAXBElement<?> result = performLDAPRequest(connection, objFactory, proxyAuthzControl, request);
              if ( result != null ) {
                batchResponses.add(result);
              }
@@ -588,7 +590,7 @@
    try {
      Marshaller marshaller = jaxbContext.createMarshaller();
      marshaller.marshal(objFactory.createBatchResponse(batchResponse), doc);
      sendResponse(doc, res);
      sendResponse(doc, messageFactory, messageContentType, res);
    } catch (Exception e) {
      e.printStackTrace();
    }
@@ -634,6 +636,7 @@
   * exception message and the type 'malformed request'.
   *
   * @param is the XML InputStream to parse
   * @param objFactory the object factory
   * @param batchResponse the JAXB object to fill in
   * @param parserErrorMessage the parsing error message
   *
@@ -641,18 +644,17 @@
   */
  private JAXBElement<ErrorResponse> createXMLParsingErrorResponse(
                                                    InputStream is,
                                                    ObjectFactory objFactory,
                                                    BatchResponse batchResponse,
                                                    String parserErrorMessage) {
    ErrorResponse errorResponse = objFactory.createErrorResponse();
    DSMLContentHandler contentHandler = new DSMLContentHandler();
    try
    {
      // try alternative XML parsing using SAX to retrieve requestID value
      final XMLReader xmlReader = createSafeXMLReader();
      // clear previous match
      this.contentHandler.requestID = null;
      xmlReader.setContentHandler(this.contentHandler);
      xmlReader.setContentHandler(contentHandler);
      is.reset();
      xmlReader.parse(new InputSource(is));
@@ -672,7 +674,7 @@
    if ( parserErrorMessage!= null ) {
      errorResponse.setMessage(parserErrorMessage);
    }
    batchResponse.setRequestID(this.contentHandler.requestID);
    batchResponse.setRequestID(contentHandler.requestID);
    errorResponse.setType(MALFORMED_REQUEST);
@@ -683,11 +685,12 @@
   * Returns an error response with attributes set according to the exception
   * provided as argument.
   *
   * @param objFactory the object factory
   * @param t the exception that occurred
   *
   * @return a JAXBElement that contains an ErrorResponse
   */
  private JAXBElement<ErrorResponse> createErrorResponse(Throwable t) {
  private JAXBElement<ErrorResponse> createErrorResponse(ObjectFactory objFactory, Throwable t) {
    // potential exceptions are IOException, LDAPException, DecodeException
    ErrorResponse errorResponse = objFactory.createErrorResponse();
@@ -730,12 +733,16 @@
   * of error, an error response is returned.
   *
   * @param connection a connected connection
   * @param objFactory the object factory
   * @param proxyAuthzControl a proxy authz control, or null
   * @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,
                                            ObjectFactory objFactory,
                                            org.opends.server.types.Control proxyAuthzControl,
                                            DsmlMessage request) {
    ArrayList<org.opends.server.types.Control> controls =
        new ArrayList<org.opends.server.types.Control>(1);
@@ -812,7 +819,7 @@
        return objFactory.createBatchResponseAuthResponse(ldapResult);
      }
    } catch (Throwable t) {
      return createErrorResponse(t);
      return createErrorResponse(objFactory, t);
    }
    // should never happen as the schema was validated
    return null;
@@ -824,12 +831,14 @@
   * or a correct DSML response.
   *
   * @param doc   The document to include in the response.
   * @param messageFactory  The SOAP message factory.
   * @param contentType  The MIME content type to send appropriate for the MessageFactory
   * @param res   Information about the HTTP response to the client.
   *
   * @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, HttpServletResponse res)
  private void sendResponse(Document doc, MessageFactory messageFactory, String contentType, HttpServletResponse res)
    throws IOException, SOAPException {
    SOAPMessage reply = messageFactory.createMessage();
@@ -959,7 +968,7 @@
   * This class is used when an XML request is malformed to retrieve the
   * requestID value using an event XML parser.
   */
  private static class DSMLContentHandler extends DefaultHandler {
  private class DSMLContentHandler extends DefaultHandler {
    private String requestID;
    /**
     * This function fetches the requestID value of the batchRequest xml