/** 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)); } } }); }