From 43cb93c0c4c4bdf7649f32768eeea9563f1d5d80 Mon Sep 17 00:00:00 2001
From: Chris Ridd <chris.ridd@forgerock.com>
Date: Tue, 28 Apr 2015 15:55:24 +0000
Subject: [PATCH] CR-6671 OPENDJ-1915 make DSML servlet threadsafe

---
 opendj-sdk/opendj-server-legacy/src/dsml/org/opends/dsml/protocol/DSMLServlet.java |   95 ++++++++++++++++++++++++++---------------------
 1 files changed, 52 insertions(+), 43 deletions(-)

diff --git a/opendj-sdk/opendj-server-legacy/src/dsml/org/opends/dsml/protocol/DSMLServlet.java b/opendj-sdk/opendj-server-legacy/src/dsml/org/opends/dsml/protocol/DSMLServlet.java
index 34e22d3..90ec1a9 100644
--- a/opendj-sdk/opendj-server-legacy/src/dsml/org/opends/dsml/protocol/DSMLServlet.java
+++ b/opendj-sdk/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

--
Gitblit v1.10.0