/**
   * Convert the SOAP XML we extract from the DIME message into our local object. Here Axis2 is not
   * parsing the SOAP for us. I tried to use the Amazon PutObject parser but it keep throwing
   * exceptions.
   *
   * @param putObjectInline
   * @return
   * @throws Exception
   */
  public static S3PutObjectRequest toEnginePutObjectRequest(InputStream is) throws Exception {
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    dbf.setNamespaceAware(true);

    DocumentBuilder db = dbf.newDocumentBuilder();
    Document doc = db.parse(is);
    Node parent = null;
    Node contents = null;
    NodeList children = null;
    String temp = null;
    String element = null;
    int count = 0;

    S3PutObjectRequest request = new S3PutObjectRequest();

    // [A] Pull out the simple nodes first
    NodeList part = getElement(doc, "http://s3.amazonaws.com/doc/2006-03-01/", "Bucket");
    if (null != part) {
      if (null != (contents = part.item(0)))
        request.setBucketName(contents.getFirstChild().getNodeValue());
    }
    part = getElement(doc, "http://s3.amazonaws.com/doc/2006-03-01/", "Key");
    if (null != part) {
      if (null != (contents = part.item(0)))
        request.setKey(contents.getFirstChild().getNodeValue());
    }
    part = getElement(doc, "http://s3.amazonaws.com/doc/2006-03-01/", "ContentLength");
    if (null != part) {
      if (null != (contents = part.item(0))) {
        String length = contents.getFirstChild().getNodeValue();
        if (null != length) request.setContentLength(Long.decode(length));
      }
    }
    part = getElement(doc, "http://s3.amazonaws.com/doc/2006-03-01/", "AWSAccessKeyId");
    if (null != part) {
      if (null != (contents = part.item(0)))
        request.setAccessKey(contents.getFirstChild().getNodeValue());
    }
    part = getElement(doc, "http://s3.amazonaws.com/doc/2006-03-01/", "Signature");
    if (null != part) {
      if (null != (contents = part.item(0)))
        request.setSignature(contents.getFirstChild().getNodeValue());
    }
    part = getElement(doc, "http://s3.amazonaws.com/doc/2006-03-01/", "Timestamp");
    if (null != part) {
      if (null != (contents = part.item(0)))
        request.setRawTimestamp(contents.getFirstChild().getNodeValue());
    }
    part = getElement(doc, "http://s3.amazonaws.com/doc/2006-03-01/", "StorageClass");
    if (null != part) {
      if (null != (contents = part.item(0)))
        request.setStorageClass(contents.getFirstChild().getNodeValue());
    }
    part = getElement(doc, "http://s3.amazonaws.com/doc/2006-03-01/", "Credential");
    if (null != part) {
      if (null != (contents = part.item(0)))
        request.setCredential(contents.getFirstChild().getNodeValue());
    }

    // [B] Get a list of all 'Metadata' elements
    part = getElement(doc, "http://s3.amazonaws.com/doc/2006-03-01/", "Metadata");
    if (null != part) {
      count = part.getLength();
      S3MetaDataEntry[] metaEntry = new S3MetaDataEntry[count];

      for (int i = 0; i < count; i++) {
        parent = part.item(i);
        metaEntry[i] = new S3MetaDataEntry();

        // -> get a list of all the children elements of the 'Metadata' parent element
        if (null != (children = parent.getChildNodes())) {
          int numChildren = children.getLength();
          for (int j = 0; j < numChildren; j++) {
            contents = children.item(j);
            element = contents.getNodeName().trim();
            if (element.endsWith("Name")) {
              temp = contents.getFirstChild().getNodeValue();
              if (null != temp) metaEntry[i].setName(temp);
            } else if (element.endsWith("Value")) {
              temp = contents.getFirstChild().getNodeValue();
              if (null != temp) metaEntry[i].setValue(temp);
            }
          }
        }
      }
      request.setMetaEntries(metaEntry);
    }

    // [C] Get a list of all Grant elements in an AccessControlList
    part = getElement(doc, "http://s3.amazonaws.com/doc/2006-03-01/", "Grant");
    if (null != part) {
      S3AccessControlList engineAcl = new S3AccessControlList();

      count = part.getLength();
      for (int i = 0; i < count; i++) {
        parent = part.item(i);
        S3Grant engineGrant = new S3Grant();

        // -> get a list of all the children elements of the 'Grant' parent element
        if (null != (children = parent.getChildNodes())) {
          int numChildren = children.getLength();
          for (int j = 0; j < numChildren; j++) {
            contents = children.item(j);
            element = contents.getNodeName().trim();
            if (element.endsWith("Grantee")) {
              NamedNodeMap attbs = contents.getAttributes();
              if (null != attbs) {
                Node type =
                    attbs.getNamedItemNS("http://www.w3.org/2001/XMLSchema-instance", "type");
                if (null != type) temp = type.getFirstChild().getNodeValue().trim();
                else temp = null;

                if (null != temp && temp.equalsIgnoreCase("CanonicalUser")) {
                  engineGrant.setGrantee(SAcl.GRANTEE_USER);
                  engineGrant.setCanonicalUserID(getChildNodeValue(contents, "ID"));
                } else
                  throw new UnsupportedOperationException(
                      "Missing http://www.w3.org/2001/XMLSchema-instance:type value");
              }
            } else if (element.endsWith("Permission")) {
              temp = contents.getFirstChild().getNodeValue().trim();
              if (temp.equalsIgnoreCase("READ")) engineGrant.setPermission(SAcl.PERMISSION_READ);
              else if (temp.equalsIgnoreCase("WRITE"))
                engineGrant.setPermission(SAcl.PERMISSION_WRITE);
              else if (temp.equalsIgnoreCase("READ_ACP"))
                engineGrant.setPermission(SAcl.PERMISSION_READ_ACL);
              else if (temp.equalsIgnoreCase("WRITE_ACP"))
                engineGrant.setPermission(SAcl.PERMISSION_WRITE_ACL);
              else if (temp.equalsIgnoreCase("FULL_CONTROL"))
                engineGrant.setPermission(SAcl.PERMISSION_FULL);
              else throw new UnsupportedOperationException("Unsupported permission: " + temp);
            }
          }
          engineAcl.addGrant(engineGrant);
        }
      }
      request.setAcl(engineAcl);
    }
    return request;
  }
  /**
   * A DIME request is really a SOAP request that we are dealing with, and so its authentication is
   * the SOAP authentication approach. Since Axis2 does not handle DIME messages we deal with them
   * here.
   *
   * @param request
   * @param response
   */
  private void processDimeRequest(HttpServletRequest request, HttpServletResponse response) {
    S3PutObjectRequest putRequest = null;
    S3PutObjectResponse putResponse = null;
    int bytesRead = 0;

    S3Engine engine = new S3Engine();

    try {
      logRequest(request);

      MultiPartDimeInputStream ds = new MultiPartDimeInputStream(request.getInputStream());

      // -> the first stream MUST be the SOAP party
      if (ds.nextInputStream()) {
        // logger.debug( "DIME msg [" + ds.getStreamType() + "," + ds.getStreamTypeFormat() + "," +
        // ds.getStreamId() + "]" );
        byte[] buffer = new byte[8192];
        bytesRead = ds.read(buffer, 0, 8192);
        // logger.debug( "DIME SOAP Bytes read: " + bytesRead );
        ByteArrayInputStream bis = new ByteArrayInputStream(buffer, 0, bytesRead);
        putRequest = toEnginePutObjectRequest(bis);
      }

      // -> we only need to support a DIME message with two bodyparts
      if (null != putRequest && ds.nextInputStream()) {
        InputStream is = ds.getInputStream();
        putRequest.setData(is);
      }

      // -> need to do SOAP level auth here, on failure return the SOAP fault
      StringBuffer xml = new StringBuffer();
      String AWSAccessKey = putRequest.getAccessKey();
      UserInfo info = ServiceProvider.getInstance().getUserInfo(AWSAccessKey);
      try {
        S3SoapAuth.verifySignature(
            putRequest.getSignature(),
            "PutObject",
            putRequest.getRawTimestamp(),
            AWSAccessKey,
            info.getSecretKey());

      } catch (AxisFault e) {
        String reason = e.toString();
        int start = reason.indexOf(".AxisFault:");
        if (-1 != start) reason = reason.substring(start + 11);

        xml.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
        xml.append(
            "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" >\n");
        xml.append("<soap:Body>\n");
        xml.append("<soap:Fault>\n");
        xml.append("<faultcode>").append(e.getFaultCode().toString()).append("</faultcode>\n");
        xml.append("<faultstring>").append(reason).append("</faultstring>\n");
        xml.append("</soap:Fault>\n");
        xml.append("</soap:Body></soap:Envelope>");

        endResponse(response, xml.toString());
        return;
      }

      // -> PutObject S3 Bucket Policy would be done in the engine.handleRequest() call
      UserContext.current()
          .initContext(AWSAccessKey, info.getSecretKey(), AWSAccessKey, "S3 DIME request", request);
      putResponse = engine.handleRequest(putRequest);

      xml.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
      xml.append(
          "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:tns=\"http://s3.amazonaws.com/doc/2006-03-01/\">");
      xml.append("<soap:Body>");
      xml.append("<tns:PutObjectResponse>");
      xml.append("<tns:PutObjectResponse>");
      xml.append("<tns:ETag>\"").append(putResponse.getETag()).append("\"</tns:ETag>");
      xml.append("<tns:LastModified>")
          .append(DatatypeConverter.printDateTime(putResponse.getLastModified()))
          .append("</tns:LastModified>");
      xml.append("</tns:PutObjectResponse></tns:PutObjectResponse>");
      xml.append("</soap:Body></soap:Envelope>");

      endResponse(response, xml.toString());
    } catch (PermissionDeniedException e) {
      logger.error("Unexpected exception " + e.getMessage(), e);
      response.setStatus(403);
      endResponse(response, "Access denied");
    } catch (Throwable e) {
      logger.error("Unexpected exception " + e.getMessage(), e);
    } finally {
    }
  }