/**
   * retrieve a domain from the repo. This does lazy loading of the repo, so it calls
   * reloadDomains() if not already loaded.
   *
   * @param domainId domain to get from the repository
   * @return domain object
   */
  @Override
  public Domain getDomain(final String domainId) {
    if (logger.isDebugEnabled()) {
      logger.debug("getDomain(" + domainId + ")");
    }

    if (StringUtils.isEmpty(domainId)) {
      throw new IllegalArgumentException(
          messages.getErrorString(
              "PentahoMetadataDomainRepository.ERROR_0004_DOMAIN_ID_INVALID", domainId));
    }
    Domain domain = null;
    try {
      // Load the domain file
      final RepositoryFile file = getMetadataRepositoryFile(domainId);
      if (file != null) {
        if (hasAccessFor(file)) {
          SimpleRepositoryFileData data =
              repository.getDataForRead(file.getId(), SimpleRepositoryFileData.class);
          if (data != null) {
            domain = xmiParser.parseXmi(data.getStream());
            domain.setId(domainId);
            logger.debug("loaded domain");
            // Load any I18N bundles
            loadLocaleStrings(domainId, domain);
            logger.debug("loaded I18N bundles");
          } else {
            throw new UnifiedRepositoryException(
                messages.getErrorString(
                    "PentahoMetadataDomainRepository.ERROR_0005_ERROR_RETRIEVING_DOMAIN",
                    domainId,
                    "data not found"));
          }
        } else {
          throw new PentahoAccessControlException(
              messages.getErrorString(
                  "PentahoMetadataDomainRepository.ERROR_0005_ERROR_RETRIEVING_DOMAIN",
                  domainId,
                  "access denied"));
        }
      }
    } catch (Exception e) {
      if (!(e instanceof UnifiedRepositoryException
          || e instanceof PentahoAccessControlException)) {
        throw new UnifiedRepositoryException(
            messages.getErrorString(
                "PentahoMetadataDomainRepository.ERROR_0005_ERROR_RETRIEVING_DOMAIN",
                domainId,
                e.getLocalizedMessage()),
            e);
      }
    }

    // Return
    return domain;
  }
  /**
   * Store a domain to the repository. The domain should persist between JVM restarts.
   *
   * @param domain domain object to store
   * @param overwrite if true, overwrite existing domain
   * @throws DomainIdNullException if domain id is null or empty
   * @throws DomainAlreadyExistsException if a domain with the same Domain ID already exists in the
   *     repository and {@code overwrite == false}
   * @throws DomainStorageException if there is a problem storing the domain
   */
  @Override
  public void storeDomain(final Domain domain, final boolean overwrite)
      throws DomainIdNullException, DomainAlreadyExistsException, DomainStorageException {
    if (logger.isDebugEnabled()) {
      logger.debug(
          "storeDomain(domain(id="
              + (domain != null ? domain.getId() : "")
              + ", "
              + overwrite
              + ")");
    }
    if (null == domain || StringUtils.isEmpty(domain.getId())) {
      throw new DomainIdNullException(
          messages.getErrorString("PentahoMetadataDomainRepository.ERROR_0001_DOMAIN_ID_NULL"));
    }

    String xmi = "";
    try {
      // NOTE - a ByteArrayInputStream doesn't need to be closed ...
      // ... so this is safe AS LONG AS we use a ByteArrayInputStream
      xmi = xmiParser.generateXmi(domain);
      // final InputStream inputStream = new ByteArrayInputStream( xmi.getBytes( DEFAULT_ENCODING )
      // );
      final InputStream inputStream = new ByteArrayInputStream(xmi.getBytes("UTF8"));
      storeDomain(inputStream, domain.getId(), overwrite);
    } catch (DomainStorageException dse) {
      throw dse;
    } catch (DomainAlreadyExistsException dae) {
      throw dae;
    } catch (Exception e) {
      final String errorMessage =
          messages.getErrorString(
              "PentahoMetadataDomainRepository.ERROR_0003_ERROR_STORING_DOMAIN",
              domain.getId(),
              e.getLocalizedMessage());
      logger.error(errorMessage, e);
      throw new DomainStorageException(xmi + errorMessage, e);
    }
  }
  @Override
  public void storeDomain(
      InputStream inputStream, String domainId, boolean overwrite, RepositoryFileAcl acl)
      throws DomainIdNullException, DomainAlreadyExistsException, DomainStorageException {
    if (logger.isDebugEnabled()) {
      logger.debug(String.format("storeDomain(inputStream, %s, %s, %s)", domainId, overwrite, acl));
    }
    if (null == inputStream) {
      throw new IllegalArgumentException();
    }
    if (StringUtils.isEmpty(domainId)) {
      throw new DomainIdNullException(
          messages.getErrorString("PentahoMetadataDomainRepository.ERROR_0001_DOMAIN_ID_NULL"));
    }

    // Check to see if the domain already exists
    final RepositoryFile domainFile = getMetadataRepositoryFile(domainId);
    if (!overwrite && domainFile != null) {
      final String errorString =
          messages.getErrorString(
              "PentahoMetadataDomainRepository.ERROR_0002_DOMAIN_ALREADY_EXISTS", domainId);
      logger.error(errorString);
      throw new DomainAlreadyExistsException(errorString);
    }

    // Check if this is valid xml
    InputStream inputStream2;
    String xmi;
    try {
      // try to see if the xmi can be parsed (ie, check if it's valid xmi)
      // first, convert our input stream to a string
      StringBuilder stringBuilder = new StringBuilder();
      BufferedReader reader =
          new BufferedReader(new InputStreamReader(inputStream, DEFAULT_ENCODING));
      try {
        while ((xmi = reader.readLine()) != null) {
          stringBuilder.append(xmi);
        }
      } finally {
        inputStream.close();
      }

      if (!isDomainIdXmiEqualsOrNotPresent(domainId, getDomainIdFromXmi(stringBuilder))) {
        domainId = replaceDomainId(stringBuilder, domainId);
      }

      xmi = stringBuilder.toString();
      // now, try to see if the xmi can be parsed (ie, check if it's valid xmi)
      byte[] xmiBytes = xmi.getBytes(DEFAULT_ENCODING);
      inputStream2 = new java.io.ByteArrayInputStream(xmiBytes);
      xmiParser.parseXmi(inputStream2);
      // xmi is valid. Create a new inputstream for the actual import action.
      inputStream2.reset();
    } catch (Exception ex) {
      logger.error(ex.getMessage());
      // throw new
      // DomainStorageException(messages.getErrorString("PentahoMetadataDomainRepository.ERROR_0010_ERROR_PARSING_XMI"),
      // ex);
      java.io.ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
      ex.printStackTrace(new java.io.PrintStream(byteArrayOutputStream));
      throw new DomainStorageException(byteArrayOutputStream.toString(), ex);
    }

    final SimpleRepositoryFileData data =
        new SimpleRepositoryFileData(inputStream2, DEFAULT_ENCODING, DOMAIN_MIME_TYPE);
    final RepositoryFile newDomainFile;
    if (domainFile == null) {
      newDomainFile = createUniqueFile(domainId, null, data);
    } else {
      newDomainFile = repository.updateFile(domainFile, data, null);
    }

    // This invalidates any caching
    flushDomains();

    getAclHelper().setAclFor(newDomainFile, acl);
  }