/** Test that an empty dir will be removed during cleaning. */
 @Test
 public void testCleanEmptyParentDir() throws Exception {
   File emptyGrandParent = new File(rootDir, "ae");
   emptyGrandParent.mkdir();
   File emptyParent = new File(emptyGrandParent, "ffacd15b0f66d5081a93407d3ff5e3c65a71");
   emptyParent.mkdir();
   assertThat(emptyGrandParent.exists(), is(true));
   assertThat(emptyParent.exists(), is(true));
   Map<String, Set<String>> result =
       repository.cleanObsoleteContent(); // To mark content for deletion
   assertThat(result.get(ContentRepository.MARKED_CONTENT).size(), is(1));
   assertThat(result.get(ContentRepository.DELETED_CONTENT).size(), is(0));
   assertThat(
       result.get(ContentRepository.MARKED_CONTENT).contains(emptyParent.getAbsolutePath()),
       is(true));
   Thread.sleep(10);
   result = repository.cleanObsoleteContent();
   assertThat(emptyGrandParent.exists(), is(false));
   assertThat(emptyParent.exists(), is(false));
   assertThat(result.get(ContentRepository.MARKED_CONTENT).size(), is(0));
   assertThat(result.get(ContentRepository.DELETED_CONTENT).size(), is(1));
   assertThat(
       result.get(ContentRepository.DELETED_CONTENT).contains(emptyParent.getAbsolutePath()),
       is(true));
 }
 /**
  * Test that an empty dir with a system metadata file .DS_Store will be removed during cleaning.
  */
 @Test
 public void testCleanEmptyParentDirWithSystemMetaDataFile() throws Exception {
   File emptyGrandParent = new File(rootDir, "ae");
   emptyGrandParent.mkdir();
   File metaDataFile = new File(emptyGrandParent, ".DS_Store");
   metaDataFile.createNewFile();
   assertThat(emptyGrandParent.exists(), is(true));
   assertThat(metaDataFile.exists(), is(true));
   Map<String, Set<String>> result =
       repository.cleanObsoleteContent(); // To mark content for deletion
   assertThat(result.get(ContentRepository.MARKED_CONTENT).size(), is(1));
   assertThat(result.get(ContentRepository.DELETED_CONTENT).size(), is(0));
   assertThat(
       result.get(ContentRepository.MARKED_CONTENT).contains(metaDataFile.getAbsolutePath()),
       is(true));
   Thread.sleep(10);
   result = repository.cleanObsoleteContent();
   assertThat(emptyGrandParent.exists(), is(false));
   assertThat(metaDataFile.exists(), is(false));
   assertThat(result.get(ContentRepository.MARKED_CONTENT).size(), is(0));
   assertThat(result.get(ContentRepository.DELETED_CONTENT).size(), is(1));
   assertThat(
       result.get(ContentRepository.DELETED_CONTENT).contains(metaDataFile.getAbsolutePath()),
       is(true));
 }
 /** Test of hasContent method, of class ContentRepository. */
 @Test
 public void testHasContent() throws Exception {
   String expResult = "0c40ffacd15b0f66d5081a93407d3ff5e3c65a71";
   try (InputStream stream = getResourceAsStream("overlay.xhtml")) {
     assertThat(repository.hasContent(HashUtil.hexStringToByteArray(expResult)), is(false));
     byte[] result = repository.addContent(stream);
     assertThat(result, is(notNullValue()));
     assertThat(HashUtil.bytesToHexString(result), is(expResult));
     assertThat(repository.hasContent(HashUtil.hexStringToByteArray(expResult)), is(true));
   }
 }
 /** Test of addContentReference method, of class ContentRepository. */
 @Test
 public void testAddContentReference() throws Exception {
   try (InputStream stream = getResourceAsStream("overlay.xhtml")) {
     String expResult = "0c40ffacd15b0f66d5081a93407d3ff5e3c65a71";
     byte[] result = repository.addContent(stream);
     assertThat(result, is(notNullValue()));
     assertThat(HashUtil.bytesToHexString(result), is(expResult));
     ContentReference reference = new ContentReference("contentReferenceIdentifier", result);
     repository.addContentReference(reference);
   }
 }
 /** Test of getContent method, of class ContentRepository. */
 @Test
 public void testGetContent() throws Exception {
   try (InputStream stream = getResourceAsStream("overlay.xhtml")) {
     String expResult = "0c40ffacd15b0f66d5081a93407d3ff5e3c65a71";
     byte[] result = repository.addContent(stream);
     assertThat(result, is(notNullValue()));
     assertThat(HashUtil.bytesToHexString(result), is(expResult));
     Path content = repository.getContent(result).getPhysicalFile().toPath();
     String contentHtml = readFileContent(content);
     String expectedContentHtml = readFileContent(getResourceAsStream("overlay.xhtml"));
     assertThat(contentHtml, is(expectedContentHtml));
   }
 }
 /** Test of explodeContent method, of class ContentRepository. */
 @Test
 public void testExplodeContent() throws Exception {
   byte[] archive = createArchive(Collections.singletonList("overlay.xhtml"));
   try (ByteArrayInputStream stream = new ByteArrayInputStream(archive)) {
     byte[] hash = repository.explodeContent(repository.addContent(stream));
     String expResult = "b1f18e286615dda0643633ec31f1a17d90e48875";
     // hash is different from the simple overlay.xhtml as we add the content folder name in the
     // computation
     assertThat(hash, is(notNullValue()));
     Path content = repository.getContent(hash).getPhysicalFile().toPath();
     String contentHtml = readFileContent(content.resolve("overlay.xhtml"));
     String expectedContentHtml = readFileContent(getResourceAsStream("overlay.xhtml"));
     assertThat(contentHtml, is(expectedContentHtml));
     assertThat(HashUtil.bytesToHexString(hash), is(expResult));
   }
 }
 /** Test that an empty dir will be removed during cleaning. */
 @Test
 public void testCleanNotEmptyGrandParentDir() throws Exception {
   String expResult = "0c40ffacd15b0f66d5081a93407d3ff5e3c65a71";
   Path grandparent = rootDir.toPath().resolve("0c");
   Path parent = grandparent.resolve("40ffacd15b0f66d5081a93407d3ff5e3c65a71");
   Path other = grandparent.resolve("40ffacd15b0f66d5081a93407d3ff5e3c65a81");
   Files.createDirectories(other);
   Path expectedContent = parent.resolve("content");
   assertFalse(expectedContent + " shouldn't exist", Files.exists(expectedContent));
   assertFalse(parent + " shouldn't exist", Files.exists(parent));
   byte[] result;
   try (InputStream stream = getResourceAsStream("overlay.xhtml")) {
     assertThat(repository.hasContent(HashUtil.hexStringToByteArray(expResult)), is(false));
     result = repository.addContent(stream);
   }
   assertThat(result, is(notNullValue()));
   assertThat(HashUtil.bytesToHexString(result), is(expResult));
   assertThat(repository.hasContent(HashUtil.hexStringToByteArray(expResult)), is(true));
   assertTrue(expectedContent + " should have been created", Files.exists(expectedContent));
   assertTrue(parent + " should have been created", Files.exists(parent));
   repository.removeContent(new ContentReference("overlay.xhtml", expResult));
   assertFalse(repository.hasContent(HashUtil.hexStringToByteArray(expResult)));
   assertFalse(expectedContent + " should have been deleted", Files.exists(expectedContent));
   assertFalse(parent.toAbsolutePath() + " should have been deleted", Files.exists(parent));
   assertTrue(other + " should not have been deleted", Files.exists(other));
   assertTrue(grandparent + " should not have been deleted", Files.exists(grandparent));
   Path content = repository.getContent(result).getPhysicalFile().toPath();
   assertFalse(Files.exists(content));
 }
 /** Test that an dir not empty with no content will not be removed during cleaning. */
 @Test
 public void testNotEmptyDir() throws Exception {
   Path parentDir = rootDir.toPath().resolve("ae").resolve("ffacd15b0f66d5081a93407d3ff5e3c65a71");
   Path overlay = parentDir.resolve("overlay.xhtml");
   Path content = parentDir.resolve("content");
   Files.createDirectories(overlay.getParent());
   try (InputStream stream = getResourceAsStream("overlay.xhtml")) {
     Files.copy(stream, overlay);
     Files.copy(overlay, content);
     assertThat(Files.exists(content), is(true));
     assertThat(Files.exists(overlay), is(true));
     Map<String, Set<String>> result =
         repository.cleanObsoleteContent(); // Mark content for deletion
     assertThat(result.get(ContentRepository.MARKED_CONTENT).size(), is(1));
     assertThat(result.get(ContentRepository.DELETED_CONTENT).size(), is(0));
     assertThat(
         result
             .get(ContentRepository.MARKED_CONTENT)
             .contains(parentDir.toFile().getAbsolutePath()),
         is(true));
     Thread.sleep(10);
     result = repository.cleanObsoleteContent();
     assertThat(Files.exists(content), is(false));
     assertThat(Files.exists(overlay), is(true));
     assertThat(result.get(ContentRepository.MARKED_CONTENT).size(), is(0));
     assertThat(result.get(ContentRepository.DELETED_CONTENT).size(), is(1));
     assertThat(
         result
             .get(ContentRepository.DELETED_CONTENT)
             .contains(parentDir.toFile().getAbsolutePath()),
         is(true));
   } finally {
     Files.deleteIfExists(overlay);
     Files.deleteIfExists(overlay.getParent());
     Files.deleteIfExists(overlay.getParent().getParent());
   }
 }
 @Test
 public void testListArchiveContents() throws Exception {
   byte[] archive = createContentArchive();
   try (ByteArrayInputStream stream = new ByteArrayInputStream(archive)) {
     byte[] hash = repository.addContent(stream);
     // hash is different from the simple overlay.xhtml as we add the content folder name in the
     // computation
     assertThat(hash, is(notNullValue()));
     List<String> contents =
         repository
             .listContent(hash, "", ContentFilter.Factory.createContentFilter(-1, false))
             .stream()
             .map(ContentRepositoryElement::getPath)
             .collect(Collectors.toList());
     assertThat(contents.size(), is(5));
     assertThat(
         contents,
         CoreMatchers.hasItems(
             "test.jsp", "overlay.xhtml", "test/empty-file.txt", "test/", "empty-dir/"));
     hash =
         repository.addContentToExploded(
             hash,
             Collections.singletonList(new ExplodedContent("test/empty-file.txt", emptyStream())),
             true);
     hash =
         repository.addContentToExploded(
             hash, Collections.singletonList(new ExplodedContent("empty-dir", null)), true);
     contents =
         repository
             .listContent(hash, "", ContentFilter.Factory.createContentFilter(1, false))
             .stream()
             .map(ContentRepositoryElement::getPath)
             .collect(Collectors.toList());
     assertThat(contents, is(notNullValue()));
     assertThat(contents.size(), is(4));
     assertThat(
         contents, CoreMatchers.hasItems("test.jsp", "overlay.xhtml", "test/", "empty-dir/"));
     contents =
         repository
             .listContent(hash, "", ContentFilter.Factory.createFileFilter(-1, false))
             .stream()
             .map(ContentRepositoryElement::getPath)
             .collect(Collectors.toList());
     assertThat(contents, is(notNullValue()));
     assertThat(contents.size(), is(3));
     assertThat(
         contents, CoreMatchers.hasItems("test.jsp", "overlay.xhtml", "test/empty-file.txt"));
   }
 }
 @Override
 public void execute(OperationContext context, ModelNode operation)
     throws OperationFailedException {
   if (context.getProcessType() == ProcessType.SELF_CONTAINED) {
     throw DomainControllerLogger.ROOT_LOGGER.cannotReadContentFromSelfContainedServer();
   }
   final Resource deploymentResource = context.readResource(PathAddress.EMPTY_ADDRESS);
   ModelNode contentItemNode = getContentItem(deploymentResource);
   // Validate this op is available
   if (!isManaged(contentItemNode)) {
     throw DomainControllerLogger.ROOT_LOGGER.cannotReadContentFromUnmanagedDeployment();
   }
   final byte[] deploymentHash =
       CONTENT_HASH.resolveModelAttribute(context, contentItemNode).asBytes();
   final ModelNode pathNode = DEPLOYMENT_CONTENT_PATH.resolveModelAttribute(context, operation);
   final String path;
   if (pathNode.isDefined()) {
     path = pathNode.asString();
   } else {
     path = "";
   }
   int depth = DEPTH.resolveModelAttribute(context, operation).asInt();
   boolean explodable = ARCHIVE.resolveModelAttribute(context, operation).asBoolean();
   try {
     for (ContentRepositoryElement content :
         contentRepository.listContent(
             deploymentHash, path, ContentFilter.Factory.createContentFilter(depth, explodable))) {
       ModelNode contentNode = new ModelNode();
       contentNode.get(PATH).set(content.getPath());
       contentNode.get(DIRECTORY).set(content.isFolder());
       if (!content.isFolder()) {
         contentNode.get(FILE_SIZE).set(content.getSize());
       }
       context.getResult().add(contentNode);
     }
   } catch (ExplodedContentException ex) {
     throw new OperationFailedException(ex.getMessage());
   }
 }
 /** Test of explodeContent method, of class ContentRepository. */
 @Test
 public void testExplodeSubContent() throws Exception {
   byte[] archive =
       createMultiLevelArchive(Collections.singletonList("overlay.xhtml"), "test/archive.zip");
   try (ByteArrayInputStream stream = new ByteArrayInputStream(archive)) {
     byte[] originalHash = repository.addContent(stream);
     assertThat(originalHash, is(notNullValue()));
     assertThat(
         HashUtil.bytesToHexString(originalHash), is("f11be1883895957b06f7e46d784cad60dd015d71"));
     try {
       repository.explodeSubContent(originalHash, "test/archive.zip");
       fail("Shouldn't be able to explode sub content of unexploded content");
     } catch (ExplodedContentException ex) {
     }
     byte[] hash = repository.explodeContent(originalHash);
     // hash is different from the simple overlay.xhtml as we add the content folder name in the
     // computation
     assertThat(hash, is(notNullValue()));
     assertThat(HashUtil.bytesToHexString(hash), is("5ab326c763fadad903d0e9bbfecbb42e69a1b8b4"));
     Path content = repository.getContent(hash).getPhysicalFile().toPath();
     String contentHtml = readFileContent(content.resolve("overlay.xhtml"));
     String expectedContentHtml = readFileContent(getResourceAsStream("overlay.xhtml"));
     assertThat(contentHtml, is(expectedContentHtml));
     Path archiveFile = content.resolve("test").resolve("archive.zip");
     assertTrue(Files.exists(archiveFile));
     assertTrue(PathUtil.isArchive(archiveFile));
     byte[] fullyExplodedHash = repository.explodeSubContent(hash, "test/archive.zip");
     assertThat(fullyExplodedHash, is(notNullValue()));
     assertThat(
         HashUtil.bytesToHexString(fullyExplodedHash),
         is("231f4d042711f017d7f8c45aa4affcccbd4d67f4"));
     content =
         repository
             .getContent(repository.explodeSubContent(hash, "test/archive.zip"))
             .getPhysicalFile()
             .toPath();
     Path directory = content.resolve("test").resolve("archive.zip");
     assertTrue("Should not be a zip file", Files.isDirectory(directory));
     assertThat(contentHtml, is(expectedContentHtml));
   }
 }
 @Test
 public void testListContents() throws Exception {
   byte[] archive = createArchive(Collections.singletonList("overlay.xhtml"));
   try (ByteArrayInputStream stream = new ByteArrayInputStream(archive)) {
     byte[] hash = repository.explodeContent(repository.addContent(stream));
     String expResult = "b1f18e286615dda0643633ec31f1a17d90e48875";
     // hash is different from the simple overlay.xhtml as we add the content folder name in the
     // computation
     assertThat(hash, is(notNullValue()));
     Path content = repository.getContent(hash).getPhysicalFile().toPath();
     String contentHtml = readFileContent(content.resolve("overlay.xhtml"));
     String expectedContentHtml = readFileContent(getResourceAsStream("overlay.xhtml"));
     assertThat(contentHtml, is(expectedContentHtml));
     assertThat(HashUtil.bytesToHexString(hash), is(expResult));
     String updatedExpectedResult = "161a2c95b16d5ffede0721c2cec984ca51009082";
     hash =
         repository.addContentToExploded(
             hash,
             Collections.singletonList(
                 new ExplodedContent(
                     "test.jsp",
                     new ByteArrayInputStream("this is a test".getBytes(StandardCharsets.UTF_8)))),
             true);
     assertThat(hash, is(notNullValue()));
     assertThat(HashUtil.bytesToHexString(hash), is(updatedExpectedResult));
     List<String> contents =
         repository
             .listContent(hash, "", ContentFilter.Factory.createContentFilter(-1, false))
             .stream()
             .map(ContentRepositoryElement::getPath)
             .collect(Collectors.toList());
     assertThat(contents.size(), is(2));
     assertThat(contents, CoreMatchers.hasItems("test.jsp", "overlay.xhtml"));
     hash =
         repository.addContentToExploded(
             hash,
             Collections.singletonList(new ExplodedContent("test/empty-file.txt", emptyStream())),
             true);
     hash =
         repository.addContentToExploded(
             hash, Collections.singletonList(new ExplodedContent("empty-dir", null)), true);
     contents =
         repository
             .listContent(hash, "", ContentFilter.Factory.createContentFilter(-1, false))
             .stream()
             .map(ContentRepositoryElement::getPath)
             .collect(Collectors.toList());
     assertThat(contents, is(notNullValue()));
     assertThat(contents.size(), is(5));
     assertThat(
         contents,
         CoreMatchers.hasItems(
             "test.jsp", "overlay.xhtml", "test/empty-file.txt", "test/", "empty-dir/"));
     hash = repository.removeContentFromExploded(hash, Collections.singletonList("test.jsp"));
     contents =
         repository
             .listContent(hash, "", ContentFilter.Factory.createFileFilter(-1, false))
             .stream()
             .map(ContentRepositoryElement::getPath)
             .collect(Collectors.toList());
     assertThat(contents, is(notNullValue()));
     assertThat(contents.size(), is(2));
     assertThat(contents, CoreMatchers.hasItems("overlay.xhtml", "test/empty-file.txt"));
   }
 }
 /** Test of explodeContent method, of class ContentRepository. */
 @Test
 public void testChangeExplodedContent() throws Exception {
   byte[] archive = createArchive(Collections.singletonList("overlay.xhtml"));
   try (ByteArrayInputStream stream = new ByteArrayInputStream(archive)) {
     byte[] hash = repository.explodeContent(repository.addContent(stream));
     String expResult = "b1f18e286615dda0643633ec31f1a17d90e48875";
     // hash is different from the simple overlay.xhtml as we add the content folder name in the
     // computation
     assertThat(hash, is(notNullValue()));
     Path content = repository.getContent(hash).getPhysicalFile().toPath();
     String contentHtml = readFileContent(content.resolve("overlay.xhtml"));
     String expectedContentHtml = readFileContent(getResourceAsStream("overlay.xhtml"));
     assertThat(contentHtml, is(expectedContentHtml));
     assertThat(HashUtil.bytesToHexString(hash), is(expResult));
     String updatedExpectedResult = "161a2c95b16d5ffede0721c2cec984ca51009082";
     hash =
         repository.addContentToExploded(
             hash,
             Collections.singletonList(
                 new ExplodedContent(
                     "test.jsp",
                     new ByteArrayInputStream("this is a test".getBytes(StandardCharsets.UTF_8)))),
             true);
     assertThat(hash, is(notNullValue()));
     assertThat(HashUtil.bytesToHexString(hash), is(updatedExpectedResult));
     try (InputStream addedContent = repository.readContent(hash, "test.jsp")) {
       assertThat(addedContent, is(notNullValue()));
       assertThat(readFileContent(addedContent), is("this is a test"));
     }
     content = repository.getContent(hash).getPhysicalFile().toPath();
     assertThat(content.toFile().list().length, is(2));
     hash = repository.removeContentFromExploded(hash, Collections.singletonList("test.jsp"));
     assertThat(hash, is(notNullValue()));
     assertThat(HashUtil.bytesToHexString(hash), is(expResult));
     updatedExpectedResult = "a44921155d75009d885db3357005b85b435cf59f";
     hash =
         repository.addContentToExploded(
             hash,
             Collections.singletonList(
                 new ExplodedContent(
                     "test.jsp",
                     new ByteArrayInputStream(
                         "this is an overwrite test".getBytes(StandardCharsets.UTF_8)))),
             true);
     assertThat(hash, is(notNullValue()));
     assertThat(HashUtil.bytesToHexString(hash), is(updatedExpectedResult));
     try (InputStream addedContent = repository.readContent(hash, "test.jsp")) {
       assertThat(addedContent, is(notNullValue()));
       assertThat(readFileContent(addedContent), is("this is an overwrite test"));
     }
     try {
       hash =
           repository.addContentToExploded(
               hash,
               Collections.singletonList(
                   new ExplodedContent(
                       "test.jsp",
                       new ByteArrayInputStream(
                           "this is a failure test".getBytes(StandardCharsets.UTF_8)))),
               false);
       fail("Overwritting shouldn't work");
     } catch (ExplodedContentException ex) {
     }
   }
 }
 @Override
 public void execute(OperationContext context, ModelNode operation)
     throws OperationFailedException {
   if (context.getProcessType() == ProcessType.SELF_CONTAINED) {
     throw ServerLogger.ROOT_LOGGER.cannotAddContentToSelfContainedServer();
   }
   final Resource deploymentResource = context.readResourceForUpdate(PathAddress.EMPTY_ADDRESS);
   ModelNode contentItemNode = getContentItem(deploymentResource);
   // Validate this op is available
   if (!isManaged(contentItemNode)) {
     throw ServerLogger.ROOT_LOGGER.cannotAddContentToUnmanagedDeployment();
   } else if (isArchive(contentItemNode)) {
     throw ServerLogger.ROOT_LOGGER.cannotAddContentToUnexplodedDeployment();
   }
   final String managementName = context.getCurrentAddress().getLastElement().getValue();
   final PathAddress address = PathAddress.pathAddress(DEPLOYMENT, managementName);
   final byte[] oldHash = CONTENT_HASH.resolveModelAttribute(context, contentItemNode).asBytes();
   final boolean overwrite = OVERWRITE.resolveModelAttribute(context, operation).asBoolean(true);
   List<ModelNode> contents = EXPLODED_CONTENT.resolveModelAttribute(context, operation).asList();
   final List<ExplodedContent> addedFiles = new ArrayList<>(contents.size());
   final byte[] newHash;
   if (contents.size() == 1 && contents.get(0).hasDefined(HASH)) {
     newHash =
         DeploymentHandlerUtil.addFromHash(
             contentRepository, contents.get(0), managementName, address, context);
     if (operation.hasDefined(DeploymentAttributes.UPDATED_PATHS.getName())) {
       for (ModelNode addedFile :
           DeploymentAttributes.UPDATED_PATHS.resolveModelAttribute(context, operation).asList()) {
         addedFiles.add(new ExplodedContent(addedFile.asString()));
       }
     }
   } else {
     for (ModelNode content : contents) {
       InputStream in;
       if (DeploymentHandlerUtils.hasValidContentAdditionParameterDefined(content)) {
         in = DeploymentHandlerUtils.getInputStream(context, content);
       } else {
         in = null;
       }
       String path = TARGET_PATH.resolveModelAttribute(context, content).asString();
       addedFiles.add(new ExplodedContent(path, in));
     }
     try {
       newHash = contentRepository.addContentToExploded(oldHash, addedFiles, overwrite);
     } catch (ExplodedContentException e) {
       throw createFailureException(e.toString());
     }
   }
   final List<String> relativePaths =
       addedFiles.stream().map(ExplodedContent::getRelativePath).collect(Collectors.toList());
   contentItemNode.get(CONTENT_HASH.getName()).set(newHash);
   contentItemNode.get(CONTENT_ARCHIVE.getName()).set(false);
   if (!addedFiles.isEmpty()
       && ENABLED.resolveModelAttribute(context, deploymentResource.getModel()).asBoolean()) {
     context.addStep(
         new OperationStepHandler() {
           @Override
           public void execute(OperationContext context, ModelNode operation)
               throws OperationFailedException {
             try {
               ExecutorService executor =
                   (ExecutorService)
                       context
                           .getServiceRegistry(false)
                           .getRequiredService(JBOSS_SERVER_EXECUTOR)
                           .getValue();
               CountDownLatch latch = copy(executor, relativePaths, managementName, newHash);
               if (latch != null) {
                 try {
                   if (!latch.await(60, TimeUnit.SECONDS)) {
                     return;
                   }
                 } catch (InterruptedException e) {
                   Thread.currentThread().interrupt();
                   throw createFailureException(e.toString());
                 }
               }
             } catch (IOException e) {
               throw createFailureException(e.toString());
             }
           }
         },
         OperationContext.Stage.RUNTIME);
   }
   context.completeStep(
       new OperationContext.ResultHandler() {
         @Override
         public void handleResult(
             ResultAction resultAction, OperationContext context, ModelNode operation) {
           if (resultAction == ResultAction.KEEP) {
             if (oldHash != null && (newHash == null || !Arrays.equals(oldHash, newHash))) {
               // The old content is no longer used; clean from repos
               contentRepository.removeContent(
                   ModelContentReference.fromModelAddress(address, oldHash));
             }
             if (newHash != null) {
               contentRepository.addContentReference(
                   ModelContentReference.fromModelAddress(address, newHash));
             }
           } else if (newHash != null && (oldHash == null || !Arrays.equals(oldHash, newHash))) {
             // Due to rollback, the new content isn't used; clean from repos
             contentRepository.removeContent(
                 ModelContentReference.fromModelAddress(address, newHash));
           }
         }
       });
 }