/** Validates and persists a document. */
  @Override
  public Document validateAndPersistDocument(Document document, KualiDocumentEvent event)
      throws ValidationException {
    if (document == null) {
      LOG.error("document passed to validateAndPersist was null");
      throw new IllegalArgumentException("invalid (null) document");
    }
    if (LOG.isDebugEnabled()) {
      LOG.debug("validating and preparing to persist document " + document.getDocumentNumber());
    }

    document.validateBusinessRules(event);
    document.prepareForSave(event);

    // save the document
    Document savedDocument = null;
    try {
      if (LOG.isInfoEnabled()) {
        LOG.info("storing document " + document.getDocumentNumber());
      }
      savedDocument = getLegacyDataAdapter().saveDocument(document);
      savedDocument.processAfterRetrieve();
      // Need to preserve the workflow document header, which just got left behind
      savedDocument
          .getDocumentHeader()
          .setWorkflowDocument(document.getDocumentHeader().getWorkflowDocument());
    } catch (OptimisticLockingFailureException e) {
      LOG.error("exception encountered on store of document " + e.getMessage());
      throw e;
    }

    boolean notesSaved = saveDocumentNotes(savedDocument);
    if (!notesSaved) {
      if (LOG.isInfoEnabled()) {
        LOG.info(
            "Notes not saved during validateAndPersistDocument, likely means that note save needs to be deferred because note target is not ready.");
      }
    }

    savedDocument.postProcessSave(event);

    return savedDocument;
  }
  /**
   * Adds more detailed logging for unhandled exceptions
   *
   * @see org.apache.struts.action.RequestProcessor#processException(HttpServletRequest,
   *     HttpServletResponse, Exception, ActionForm, ActionMapping)
   */
  @Override
  protected ActionForward processException(
      HttpServletRequest request,
      HttpServletResponse response,
      Exception exception,
      ActionForm form,
      ActionMapping mapping)
      throws IOException, ServletException {
    ActionForward actionForward = null;

    try {
      actionForward = super.processException(request, response, exception, form, mapping);
    } catch (IOException e) {
      logException(e);
      throw e;
    } catch (ServletException e) {
      // special case, to make OptimisticLockExceptions easier to read
      Throwable rootCause = e.getRootCause();
      if (rootCause instanceof OjbOperationException) {
        OjbOperationException ooe = (OjbOperationException) rootCause;

        Throwable subcause = ooe.getCause();
        if (subcause != null) {
          Object sourceObject = null;
          boolean optLockException = false;
          if (subcause instanceof javax.persistence.OptimisticLockException) {
            javax.persistence.OptimisticLockException ole =
                (javax.persistence.OptimisticLockException) subcause;
            sourceObject = ole.getEntity();
            optLockException = true;
          } else if (subcause instanceof OptimisticLockingFailureException) {
            OptimisticLockingFailureException ole = (OptimisticLockingFailureException) subcause;
            sourceObject = ole.getMessage();
            optLockException = true;
          } else {
            if (subcause
                .getClass()
                .getName()
                .equals("org.apache.ojb.broker.OptimisticLockException")) {
              try {
                sourceObject = PropertyUtils.getSimpleProperty(subcause, "sourceObject");
              } catch (Exception ex) {
                LOG.warn("Unable to retrieve source object from OJB OptimisticLockException", ex);
              }
              optLockException = true;
            }
          }
          if (optLockException) {
            StringBuilder message = new StringBuilder(e.getMessage());

            if (sourceObject != null) {
              if (sourceObject instanceof String) {
                message.append(" Embedded Message: ").append(sourceObject);
              } else {
                message.append(" (sourceObject is ");
                message.append(sourceObject.getClass().getName());
                message.append(")");
              }
            }

            e = new ServletException(message.toString(), rootCause);
          }
        }
      }

      logException(e);
      throw e;
    }
    return actionForward;
  }