/** Unwraps the WebSphere JDBC connection wrapping using the WebLogic API reflectively. */
  @Override
  public Connection unwrapConnection(Connection connection) {
    if (getWebsphereConnectionClass().isInstance(connection)
        && getVendorConnectionMethod() != null) {
      try {
        return (Connection)
            PrivilegedAccessHelper.invokeMethod(
                getVendorConnectionMethod(), null, new Object[] {connection});
      } catch (IllegalAccessException exception) {
        getDatabaseSession()
            .getSessionLog()
            .logThrowable(SessionLog.WARNING, SessionLog.SERVER, exception);
      } catch (InvocationTargetException exception) {
        getDatabaseSession()
            .getSessionLog()
            .logThrowable(SessionLog.WARNING, SessionLog.SERVER, exception);
      }
    }

    return super.unwrapConnection(connection);
  }
  /**
   * INTERNAL: This method will return the key to be used to store/retrieve the delegates for a
   * given application.
   *
   * <p>OC4J classLoader levels: 0 - APP.web (servlet/jsp) or APP.wrapper (ejb) 1 - APP.root (parent
   * for helperContext) 2 - default.root 3 - system.root 4 - oc4j.10.1.3 (remote EJB) or
   * org.eclipse.persistence:11.1.1.0.0 5 - api:1.4.0 6 - jre.extension:0.0.0 7 -
   * jre.bootstrap:1.5.0_07 (with various J2SE versions)
   *
   * @return Application classloader for OC4J, application name for WebLogic, otherwise
   *     Thread.currentThread().getContextClassLoader()
   */
  private static Object getDelegateMapKey(ClassLoader classLoader) {
    String classLoaderName = classLoader.getClass().getName();

    // Default to Thread.currentThread().getContextClassLoader()
    Object delegateKey = classLoader;

    // Delegates in OC4J server will be keyed on classloader
    if (classLoaderName.startsWith(OC4J_CLASSLOADER_NAME)) {
      // Check to see if we are running in a Servlet container or a local EJB container
      if ((classLoader.getParent() != null) //
          && ((classLoader.toString().indexOf(SDOConstants.CLASSLOADER_WEB_FRAGMENT) != -1) //
              || (classLoader.toString().indexOf(SDOConstants.CLASSLOADER_EJB_FRAGMENT) != -1))) {
        classLoader = classLoader.getParent();
      }
      delegateKey = classLoader;
      // Delegates in WebLogic server will be keyed on application name
    } else if (classLoaderName.contains(WLS_CLASSLOADER_NAME)) {
      Object executeThread = getExecuteThread();
      if (executeThread != null) {
        try {
          Method getMethod =
              PrivilegedAccessHelper.getPublicMethod(
                  executeThread.getClass(),
                  WLS_APPLICATION_NAME_GET_METHOD_NAME,
                  PARAMETER_TYPES,
                  false);
          delegateKey = PrivilegedAccessHelper.invokeMethod(getMethod, executeThread);
          // ExecuteThread returns null
          if (delegateKey == null) {
            delegateKey = classLoader;
          }
        } catch (Exception e) {
          throw SDOException.errorInvokingWLSMethodReflectively(
              WLS_APPLICATION_NAME_GET_METHOD_NAME, WLS_EXECUTE_THREAD, e);
        }
      }
    }
    return delegateKey;
  }
  @Override
  public XMLRecord buildRow(
      XMLRecord record,
      Object object,
      CoreAbstractSession session,
      Marshaller marshaller,
      XPathFragment rootFragment) {
    lazyInitialize();
    XPathNode textNode = rootXPathNode.getTextNode();
    List<XPathNode> nonAttributeChildren = rootXPathNode.getNonAttributeChildren();
    if (null == textNode && null == nonAttributeChildren) {
      return record;
    }

    Descriptor xmlDescriptor = (Descriptor) descriptor;
    XPathNode node = rootXPathNode;
    MarshalRecord marshalRecord = (MarshalRecord) record;
    QName schemaType = null;

    if (marshalRecord.getCycleDetectionStack().contains(object, marshaller.isEqualUsingIdenity())) {
      if (cycleRecoverableClass == null) {
        initCycleRecoverableClasses();
      }
      if (cycleRecoverableClass != null
          && cycleRecoverableClass.isAssignableFrom(object.getClass())) {
        try {
          Object jaxbMarshaller = marshaller.getProperty(Constants.JAXB_MARSHALLER);
          // Create a proxy instance of CycleRecoverable$Context, a parameter to
          // the onCycleDetected method
          Object contextProxy =
              CycleRecoverableContextProxy.getProxy(cycleRecoverableContextClass, jaxbMarshaller);
          // Invoke onCycleDetected method, passing in proxy, and reset
          // 'object' to the returned value
          Method onCycleDetectedMethod =
              object
                  .getClass()
                  .getMethod(ON_CYCLE_DETECTED, new Class[] {cycleRecoverableContextClass});
          object =
              PrivilegedAccessHelper.invokeMethod(
                  onCycleDetectedMethod, object, new Object[] {contextProxy});
        } catch (Exception e) {
          throw XMLMarshalException.marshalException(e);
        }

        // Returned object might have a different descriptor
        xmlDescriptor = (Descriptor) session.getDescriptor(object.getClass());
        if (xmlDescriptor != null) {
          node = ((ObjectBuilder) xmlDescriptor.getObjectBuilder()).getRootXPathNode();
        } else {
          node = null;
        }

        // Push new object
        marshalRecord.getCycleDetectionStack().push(object);

        // Write xsi:type if onCycleDetected returned an object of a type different than the one
        // mapped
        if (xmlDescriptor != descriptor) {
          if (xmlDescriptor == null) {
            schemaType = record.getConversionManager().schemaType(object.getClass());
          } else {
            schemaType = xmlDescriptor.getSchemaReference().getSchemaContextAsQName();
          }
          marshalRecord.writeXsiTypeAttribute(
              xmlDescriptor,
              schemaType.getNamespaceURI(),
              schemaType.getLocalPart(),
              schemaType.getPrefix(),
              false);
        }
      } else {
        // Push the duplicate object anyway, so that we can get the complete cycle string
        marshalRecord.getCycleDetectionStack().push(object);
        throw XMLMarshalException.objectCycleDetected(
            marshalRecord.getCycleDetectionStack().getCycleString());
      }
    } else {
      marshalRecord.getCycleDetectionStack().push(object);
    }

    NamespaceResolver namespaceResolver = null;
    if (xmlDescriptor != null) {
      namespaceResolver = xmlDescriptor.getNamespaceResolver();
    }
    MarshalContext marshalContext = null;
    if (xmlDescriptor != null && xmlDescriptor.isSequencedObject()) {
      SequencedObject sequencedObject = (SequencedObject) object;
      marshalContext = new SequencedMarshalContext(sequencedObject.getSettings());
    } else {
      marshalContext = ObjectMarshalContext.getInstance();
    }
    if (null == nonAttributeChildren) {
      textNode.marshal(
          (MarshalRecord) record,
          object,
          session,
          namespaceResolver,
          marshaller,
          marshalContext,
          rootFragment);
    } else {
      if (node == null) {
        // No descriptor for this object, so manually create a MappingNodeValue and marshal it
        XPathNode n = new XPathNode();
        CompositeObjectMapping m = new XMLCompositeObjectMapping();
        m.setXPath(".");
        XMLCompositeObjectMappingNodeValue nv = new XMLCompositeObjectMappingNodeValue(m);
        n.setMarshalNodeValue(nv);
        nv.marshalSingleValue(
            new XPathFragment("."),
            marshalRecord,
            null,
            object,
            session,
            namespaceResolver,
            marshalContext);
      } else {
        for (int x = 0, size = marshalContext.getNonAttributeChildrenSize(node); x < size; x++) {
          XPathNode xPathNode = (XPathNode) marshalContext.getNonAttributeChild(x, node);
          xPathNode.marshal(
              (MarshalRecord) record,
              object,
              session,
              namespaceResolver,
              marshaller,
              marshalContext.getMarshalContext(x),
              rootFragment);
        }
      }
    }
    marshalRecord.getCycleDetectionStack().pop();
    return record;
  }
  public void persistExample(Session session) {
    Vector allObjects = new Vector();
    allObjects.add(example1());
    allObjects.add(example2());
    allObjects.add(example3());
    allObjects.add(example4());

    // Bug 387491 - Three JUnitJPQLDateTimeTestSuite tests fail with Oracle jdbc 12.1 driver
    // Starting with Oracle jdbc 12.1 Statement.setDate no longer truncates time component of
    // sql.Date.
    // The following code makes Oracle9Platform to do that by setting shouldTruncateDate flag to
    // "true".
    boolean hasSetTruncateDate = false;
    if (session.getPlatform().isOracle9()) {
      try {
        Class clazz =
            PrivilegedAccessHelper.getClassForName(
                "org.eclipse.persistence.platform.database.oracle.Oracle9Platform");
        Method getDriverVersionMethod =
            PrivilegedAccessHelper.getMethod(clazz, "getDriverVersion", null, false);
        String driverVersion =
            (String)
                PrivilegedAccessHelper.invokeMethod(
                    getDriverVersionMethod, session.getPlatform(), null);
        if (Helper.compareVersions(driverVersion, "12.1") >= 0) {
          Method shouldTruncateDateMethod =
              PrivilegedAccessHelper.getMethod(clazz, "shouldTruncateDate", null, false);
          boolean shouldTruncateDate =
              (Boolean)
                  PrivilegedAccessHelper.invokeMethod(
                      shouldTruncateDateMethod, session.getPlatform(), null);
          if (!shouldTruncateDate) {
            Method setShouldTruncateDateMethod =
                PrivilegedAccessHelper.getMethod(
                    clazz, "setShouldTruncateDate", new Class[] {boolean.class}, false);
            PrivilegedAccessHelper.invokeMethod(
                setShouldTruncateDateMethod, session.getPlatform(), new Object[] {true});
            hasSetTruncateDate = true;
          }
        }
      } catch (Exception ex) {
        throw new RuntimeException("Failed oracle9Platform.setShouldTruncateDate(true)", ex);
      }
    }

    UnitOfWork unitOfWork = session.acquireUnitOfWork();
    unitOfWork.registerAllObjects(allObjects);
    unitOfWork.commit();

    if (hasSetTruncateDate) {
      // Now setting shouldTruncateDate flag back to its original value "false".
      try {
        Class clazz =
            PrivilegedAccessHelper.getClassForName(
                "org.eclipse.persistence.platform.database.oracle.Oracle9Platform");
        Method setShouldTruncateDateMethod =
            PrivilegedAccessHelper.getMethod(
                clazz, "setShouldTruncateDate", new Class[] {boolean.class}, false);
        PrivilegedAccessHelper.invokeMethod(
            setShouldTruncateDateMethod, session.getPlatform(), new Object[] {false});
      } catch (Exception ex) {
        throw new RuntimeException("Failed oracle9Platform.setShouldTruncateDate(false)", ex);
      }
    }
  }