/**
   * Fetches a single change package fragment.
   *
   * @param sessionId the {@link SessionId} representing the requesting user
   * @param proxyId the ID that identifies the list of stored fragments
   * @param fragmentIndex allows to request different change package fragments
   * @return a {@link ChangePackageEnvelope} containing the change package fragment
   * @throws ESException in case the mandatory session adapter is missing
   */
  @ESMethod(MethodId.DOWNLOADCHANGEPACKAGEFRAGMENT)
  public ChangePackageEnvelope downloadChangePackageFragment(
      SessionId sessionId, String proxyId, int fragmentIndex) throws ESException {

    final ESSessionId resolvedSession =
        getAccessControl().getSessions().resolveSessionById(sessionId.getId());
    final SessionId session = APIUtil.toInternal(SessionId.class, resolvedSession);
    final Optional<ChangePackageFragmentProviderAdapter> maybeAdapter =
        ESCollections.find(session.eAdapters(), ChangePackageFragmentProviderAdapter.class);

    if (!maybeAdapter.isPresent()) {
      throw new ESException(
          Messages.VersionSubInterfaceImpl_ChangePackageFragmentProviderAdapterMissing + sessionId);
    }

    final ChangePackageFragmentProviderAdapter adapter = maybeAdapter.get();
    final ChangePackageEnvelope envelope =
        VersioningFactory.eINSTANCE.createChangePackageEnvelope();
    final List<String> fragment = adapter.getFragment(proxyId, fragmentIndex);

    envelope.getFragment().addAll(fragment);
    envelope.setFragmentCount(adapter.getFragmentSize(proxyId));
    envelope.setFragmentIndex(fragmentIndex);

    if (envelope.isLast()) {
      adapter.markAsConsumed(proxyId);
    }

    return envelope;
  }
  /**
   * Create a new version.
   *
   * @param sessionId the ID of the session being used to create a new version
   * @param projectId the ID of the project for which a new version is created
   * @param baseVersionSpec the base version
   * @param changePackage the change package containing all changes that make up the new version
   * @param targetBranch the target branch for which to create the new version
   * @param sourceVersion the source version
   * @param logMessage a log message
   * @return the new version
   * @throws ESException in case of failure
   */
  @ESMethod(MethodId.CREATEVERSION)
  public PrimaryVersionSpec createVersion(
      SessionId sessionId,
      ProjectId projectId,
      PrimaryVersionSpec baseVersionSpec,
      AbstractChangePackage changePackage,
      BranchVersionSpec targetBranch,
      PrimaryVersionSpec sourceVersion,
      LogMessage logMessage)
      throws ESException {

    getAccessControl().getSessions().isValid(sessionId.toAPI());
    final ESUser rawUser = getAccessControl().getSessions().getRawUser(sessionId.toAPI());
    final ACUser tmpUser = (ACUser) ESUserImpl.class.cast(rawUser).toInternalAPI();
    final ESUser copyAndResolveUser =
        getAccessControl().getOrgUnitResolverServive().copyAndResolveUser(tmpUser.toAPI());
    final ACUser user = (ACUser) ESUserImpl.class.cast(copyAndResolveUser).toInternalAPI();
    sanityCheckObjects(sessionId, projectId, baseVersionSpec, changePackage, logMessage);

    if (FileBasedChangePackage.class.isInstance(changePackage)
        && !ServerConfiguration.useFileBasedChangePackageOnServer()) {
      // File-based change package should never arrive here in production mode
      throw new ESException(Messages.VersionSubInterfaceImpl_FileBasedChangePackageNotAllowed);
    } else if (ChangePackage.class.isInstance(changePackage)
        && ServerConfiguration.useFileBasedChangePackageOnServer()) {
      // Regular change package should never arrive here in production mode
      throw new ESException(Messages.VersionSubInterfaceImpl_FileBasedChangePackageExpected);
    }

    return internalCreateVersion(
        projectId, baseVersionSpec, changePackage, targetBranch, sourceVersion, logMessage, user);
  }
  /**
   * Given a {@link ChangePackageEnvelope} which contains a change package fragment, stores one or
   * more fragments by attaching them to a session specific adapter.
   *
   * @param sessionId the {@link SessionId} belonging to the calling user
   * @param projectId the {@link ProjectId}
   * @param envelope the {@link ChangePackageEnvelope} containing the fragment
   * @return an ID identifying the stored fragment(s)
   * @throws ESException in case the fragment couldn't be stored
   */
  @ESMethod(MethodId.UPLOADCHANGEPACKAGEFRAGMENT)
  public String uploadChangePackageFragment(
      SessionId sessionId, ProjectId projectId, ChangePackageEnvelope envelope) throws ESException {

    final String proxyId = generateProxyId(projectId.getId());

    final ESSessionId resolvedSession =
        getAccessControl().getSessions().resolveSessionById(sessionId.getId());

    if (resolvedSession == null) {
      throw new ESException(
          MessageFormat.format(Messages.VersionSubInterfaceImpl_0, sessionId.getId()));
    }

    final SessionId session = APIUtil.toInternal(SessionId.class, resolvedSession);
    final Optional<ChangePackageFragmentUploadAdapter> maybeAdapter =
        ESCollections.find(session.eAdapters(), ChangePackageFragmentUploadAdapter.class);
    ChangePackageFragmentUploadAdapter adapter;

    if (!maybeAdapter.isPresent()) {
      adapter = new ChangePackageFragmentUploadAdapter();
      session.eAdapters().add(adapter);
    } else {
      adapter = maybeAdapter.get();
    }

    adapter.addFragment(proxyId, envelope.getFragment());
    if (envelope.isLast()) {
      adapter.markAsComplete(proxyId);
    }

    return proxyId;
  }