private File getSessionStateFile(final CandidateEvent candidateEvent) {
   final CandidateSession candidateSession = candidateEvent.getCandidateSession();
   final AssessmentObjectType assessmentType =
       candidateSession.getDelivery().getAssessment().getAssessmentType();
   final String stateFileBaseName =
       assessmentType == AssessmentObjectType.ASSESSMENT_ITEM
           ? "itemSessionState"
           : "testSessionState";
   final File sessionFolder = filespaceManager.obtainCandidateSessionStateStore(candidateSession);
   final String stateFileName = stateFileBaseName + candidateEvent.getId() + ".xml";
   return new File(sessionFolder, stateFileName);
 }
 public AssessmentResult computeTestAssessmentResult(
     final CandidateSession candidateSession, final TestSessionController testSessionController) {
   final URI sessionIdentifierSourceId = URI.create(qtiWorksDeploymentSettings.getBaseUrl());
   final String sessionIdentifier = "testsession/" + candidateSession.getId();
   return testSessionController.computeAssessmentResult(
       requestTimestampContext.getCurrentRequestTimestamp(),
       sessionIdentifier,
       sessionIdentifierSourceId);
 }
  /**
   * Wraps the given {@link ItemSessionState} in a {@link ItemSessionController}.
   *
   * <p>It is assumed that the item was runnable, so this will never return null.
   */
  public ItemSessionController createItemSessionController(
      final CandidateSession candidateSession,
      final ItemSessionState itemSessionState,
      final NotificationRecorder notificationRecorder) {
    final User candidate = candidateSession.getCandidate();
    final Delivery delivery = candidateSession.getDelivery();
    ensureItemDelivery(delivery);
    Assert.notNull(itemSessionState, "itemSessionState");

    /* Try to resolve the underlying JQTI+ object */
    final AssessmentPackage assessmentPackage =
        assessmentDataService.ensureSelectedAssessmentPackage(delivery);
    final ItemProcessingMap itemProcessingMap =
        assessmentObjectManagementService.getItemProcessingMap(assessmentPackage);
    if (itemProcessingMap == null) {
      throw new QtiWorksLogicException("Expected this item to be runnable");
    }

    /* Create config for ItemSessionController */
    final ItemDeliverySettings itemDeliverySettings =
        (ItemDeliverySettings)
            assessmentDataService.getEffectiveDeliverySettings(candidate, delivery);
    final ItemSessionControllerSettings itemSessionControllerSettings =
        new ItemSessionControllerSettings();
    itemSessionControllerSettings.setTemplateProcessingLimit(
        computeTemplateProcessingLimit(itemDeliverySettings));
    itemSessionControllerSettings.setMaxAttempts(itemDeliverySettings.getMaxAttempts());

    /* Create controller and wire up notification recorder (if passed) */
    final ItemSessionController result =
        new ItemSessionController(
            jqtiExtensionManager,
            itemSessionControllerSettings,
            itemProcessingMap,
            itemSessionState);
    if (notificationRecorder != null) {
      result.addNotificationListener(notificationRecorder);
    }

    return result;
  }
  /**
   * Wraps the given {@link TestSessionState} in a {@link TestSessionController}.
   *
   * <p>It is assumed that the test was runnable, so this will never return null.
   */
  public TestSessionController createTestSessionController(
      final CandidateSession candidateSession,
      final TestSessionState testSessionState,
      final NotificationRecorder notificationRecorder) {
    final User candidate = candidateSession.getCandidate();
    final Delivery delivery = candidateSession.getDelivery();
    ensureTestDelivery(delivery);
    Assert.notNull(testSessionState, "testSessionState");

    /* Try to resolve the underlying JQTI+ object */
    final AssessmentPackage assessmentPackage =
        assessmentDataService.ensureSelectedAssessmentPackage(delivery);
    final TestProcessingMap testProcessingMap =
        assessmentObjectManagementService.getTestProcessingMap(assessmentPackage);
    if (testProcessingMap == null) {
      return null;
    }

    /* Create config for TestSessionController */
    final TestDeliverySettings testDeliverySettings =
        (TestDeliverySettings)
            assessmentDataService.getEffectiveDeliverySettings(candidate, delivery);
    final TestSessionControllerSettings testSessionControllerSettings =
        new TestSessionControllerSettings();
    testSessionControllerSettings.setTemplateProcessingLimit(
        computeTemplateProcessingLimit(testDeliverySettings));

    /* Create controller and wire up notification recorder (if passed) */
    final TestSessionController result =
        new TestSessionController(
            jqtiExtensionManager,
            testSessionControllerSettings,
            testProcessingMap,
            testSessionState);
    if (notificationRecorder != null) {
      result.addNotificationListener(notificationRecorder);
    }

    return result;
  }
  /**
   * Starts new {@link CandidateSession} for the given {@link User} on the given {@link Delivery}
   *
   * <p>NB: No checks are made on whether the {@link User} should be allowed to start a session on
   * this {@link Delivery}.
   */
  public CandidateSession launchCandidateSession(
      final User candidate,
      final Delivery delivery,
      final boolean authorMode,
      final String lisOutcomeServiceUrl,
      final String lisResultSourcedid)
      throws CandidateException {
    Assert.notNull(candidate, "candidate");
    Assert.notNull(delivery, "delivery");

    /* Make sure Candidate's account is not disabled */
    if (candidate.isLoginDisabled()) {
      logAndThrowLaunchException(
          candidate, delivery, CandidateExceptionReason.USER_ACCOUNT_DISABLED);
    }

    /* Make sure Delivery is runnable */
    if (delivery.getAssessment() == null) {
      logAndThrowLaunchException(
          candidate, delivery, CandidateExceptionReason.LAUNCH_INCOMPLETE_DELIVERY);
    }

    /* If the candidate already has any non-terminated sessions open for this Delivery,
     * then we shall reconnect to the (most recent) session instead of creating a new one.
     */
    final List<CandidateSession> existingSessions =
        candidateSessionDao.getNonTerminatedForDeliveryAndCandidate(delivery, candidate);
    if (!existingSessions.isEmpty()) {
      final CandidateSession mostRecent = existingSessions.get(existingSessions.size() - 1);
      auditLogger.recordEvent(
          "Reconnected to existing CandidateSession #"
              + mostRecent.getId()
              + " on Delivery #"
              + delivery.getId());
      return mostRecent;
    }

    /* No existing session to reconnect to, so create a new session.
     *
     * (NB: The session will later need to be explicitly entered before anything can be done
     * with it.)
     */
    final CandidateSession candidateSession = new CandidateSession();
    candidateSession.setLisOutcomeServiceUrl(lisOutcomeServiceUrl);
    candidateSession.setLisResultSourcedid(lisResultSourcedid);
    candidateSession.setCandidate(candidate);
    candidateSession.setDelivery(delivery);
    candidateSession.setAuthorMode(authorMode);
    candidateSession.setFinishTime(null);
    candidateSession.setTerminationTime(null);
    candidateSession.setExploded(false);
    candidateSessionDao.persist(candidateSession);
    auditLogger.recordEvent(
        "Created and initialised new CandidateSession #"
            + candidateSession.getId()
            + " on Delivery #"
            + delivery.getId());
    return candidateSession;
  }