diff --git a/deployment-scanner/src/main/java/org/jboss/as/server/deployment/scanner/FileSystemDeploymentService.java b/deployment-scanner/src/main/java/org/jboss/as/server/deployment/scanner/FileSystemDeploymentService.java index 77217973ba9..4c42cc784f7 100644 --- a/deployment-scanner/src/main/java/org/jboss/as/server/deployment/scanner/FileSystemDeploymentService.java +++ b/deployment-scanner/src/main/java/org/jboss/as/server/deployment/scanner/FileSystemDeploymentService.java @@ -41,6 +41,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -152,6 +153,12 @@ class FileSystemDeploymentService implements DeploymentScanner, NotificationHand @SuppressWarnings("deprecation") private final DeploymentTransformer deploymentTransformer; + /** + * Property have to be guarded by scanLock. + * Reads or writes of this field can be done only in code executing + * between a call to {@link #acquireScanLock()} and {@link #releaseScanLock()} + */ + private ScanContext scanContext; @Override public void handleNotification(Notification notification) { @@ -504,6 +511,23 @@ void scan() { } } + /** + * This method exists only for testing purposes + * + * @return unmodifiable collection of registered deployments, null otherwise + */ + Map getScanContextRegisteredDeployments() { + if (acquireScanLock()) { + try { + assert scanContext != null; + return Collections.unmodifiableMap(scanContext.registeredDeployments); + } finally { + releaseScanLock(); + } + } + return null; + } + /** * Perform a post-boot scan to remove any deployments added during boot that failed to deploy properly. * This method isn't private solely to allow a unit test in the same package to call it. @@ -513,7 +537,7 @@ void forcedUndeployScan() { if (acquireScanLock()) { try { ROOT_LOGGER.tracef("Performing a post-boot forced undeploy scan for scan directory %s", deploymentDir.getAbsolutePath()); - ScanContext scanContext = new ScanContext(deploymentOperations); + scanContext = new ScanContext(deploymentOperations); // Add remove actions to the plan for anything we count as // deployed that we didn't find on the scan @@ -540,6 +564,7 @@ void forcedUndeployScan() { } catch (Exception e) { ROOT_LOGGER.scanException(e, deploymentDir.getAbsolutePath()); } finally { + scanContext = null; releaseScanLock(); } } @@ -581,7 +606,7 @@ private boolean scan(boolean oneOffScan, final DeploymentOperations deploymentOp deployedContentEstablished = true; } - ScanContext scanContext = null; + scanContext = null; try { scanContext = new ScanContext(deploymentOperations); } catch (RuntimeException ex) { @@ -592,7 +617,7 @@ private boolean scan(boolean oneOffScan, final DeploymentOperations deploymentOp throw ex; } - scanDirectory(deploymentDir, relativePath, scanContext); + scanDirectory(deploymentDir, relativePath); // WARN about markers with no associated content. Do this first in case any auto-deploy issue // is due to a file that wasn't meant to be auto-deployed, but has a misspelled marker @@ -628,7 +653,7 @@ private boolean scan(boolean oneOffScan, final DeploymentOperations deploymentOp } // Deal with any incomplete or non-scannable auto-deploy content - ScanStatus status = handleAutoDeployFailures(scanContext); + ScanStatus status = handleAutoDeployFailures(); if (status != ScanStatus.PROCEED) { if (status == ScanStatus.RETRY && scanInterval > 1000) { // schedule a non-repeating task to try again more quickly @@ -837,9 +862,8 @@ else if (!directory.canWrite()) { * Scan the given directory for content changes. * * @param directory the directory to scan - * @param scanContext context of the scan */ - private void scanDirectory(final File directory, final String relativePath, final ScanContext scanContext) { + private void scanDirectory(final File directory, final String relativePath) { final List children = listDirectoryChildren(directory, filter); for (File child : children) { final String fileName = child.getName(); @@ -909,20 +933,30 @@ private void scanDirectory(final File directory, final String relativePath, fina // the // deploymentDir final boolean archive = deploymentFile.isFile(); - addContentAddingTask(path, archive, deploymentName, deploymentFile, timestamp, scanContext); + addContentAddingTask(path, archive, deploymentName, deploymentFile, timestamp); } else if (fileName.endsWith(FAILED_DEPLOY)) { final String deploymentName = fileName.substring(0, fileName.length() - FAILED_DEPLOY.length()); - scanContext.toRemove.remove(deploymentName); - if (!deployed.containsKey(deploymentName) && !(new File(child.getParent(), deploymentName).exists())) { - removeExtraneousMarker(child, fileName); + if (acquireScanLock()) { + try { + assert scanContext != null; + scanContext.toRemove.remove(deploymentName); + if (!deployed.containsKey(deploymentName) && !(new File(child.getParent(), deploymentName).exists())) { + if (scanContext.registeredDeployments.containsKey(deploymentName)) { + scanContext.scannerTasks.add(new UndeployTask(deploymentName, deploymentDir, scanContext.scanStartTime, true)); + } + removeExtraneousMarker(child, fileName); + } + } finally { + releaseScanLock(); + } } } else if (isEEArchive(fileName)) { boolean autoDeployable = child.isDirectory() ? autoDeployExploded : autoDeployZip; if (autoDeployable) { if (!isAutoDeployDisabled(child)) { long timestamp = getDeploymentTimestamp(child); - synchronizeScannerStatus(scanContext, directory, fileName, timestamp); - if (isFailedOrUndeployed(scanContext, directory, fileName, timestamp) || scanContext.firstScanDeployments.contains(fileName)) { + synchronizeScannerStatus(directory, fileName, timestamp); + if (isFailedOrUndeployed(directory, fileName, timestamp) || scanContext.firstScanDeployments.contains(fileName)) { continue; } @@ -935,7 +969,7 @@ private void scanDirectory(final File directory, final String relativePath, fina if(firstScan){ scanContext.firstScanDeployments.add(fileName); } - addContentAddingTask(path, archive, fileName, child, timestamp, scanContext); + addContentAddingTask(path, archive, fileName, child, timestamp); } else { //we need to make sure that the file was not deleted while //the scanner was running @@ -958,7 +992,7 @@ private void scanDirectory(final File directory, final String relativePath, fina if (autoDeployXml) { if (!isAutoDeployDisabled(child)) { long timestamp = getDeploymentTimestamp(child); - if (isFailedOrUndeployed(scanContext, directory, fileName, timestamp) || scanContext.firstScanDeployments.contains(fileName)) { + if (isFailedOrUndeployed(directory, fileName, timestamp) || scanContext.firstScanDeployments.contains(fileName)) { continue; } @@ -969,7 +1003,7 @@ private void scanDirectory(final File directory, final String relativePath, fina if(firstScan){ scanContext.firstScanDeployments.add(fileName); } - addContentAddingTask(path, true, fileName, child, timestamp, scanContext); + addContentAddingTask(path, true, fileName, child, timestamp); } else { //we need to make sure that the file was not deleted while //the scanner was running @@ -1001,7 +1035,7 @@ private void scanDirectory(final File directory, final String relativePath, fina // Track for possible ERROR logging scanContext.illegalDir.add(fileName); } else { - scanDirectory(child, relativePath + child.getName() + File.separator, scanContext); + scanDirectory(child, relativePath + child.getName() + File.separator); } } } @@ -1016,20 +1050,20 @@ private boolean isXmlComplete(final File xmlFile) { } } - private boolean isFailedOrUndeployed(final ScanContext scanContext, final File directory, final String fileName, final long timestamp) { + private boolean isFailedOrUndeployed(final File directory, final String fileName, final long timestamp) { final File failedMarker = new File(directory, fileName + FAILED_DEPLOY); if (failedMarker.exists() && timestamp <= failedMarker.lastModified()) { return true; } final File undeployedMarker = new File(directory, fileName + UNDEPLOYED); - if (isMarkedUndeployed(undeployedMarker, timestamp) && !isRegisteredDeployment(scanContext, fileName)) { + if (isMarkedUndeployed(undeployedMarker, timestamp) && !isRegisteredDeployment(fileName)) { return true; } return false; } - private void synchronizeScannerStatus(ScanContext scanContext, File directory, String fileName, long timestamp) { - if (isRegisteredDeployment(scanContext, fileName)) { + private void synchronizeScannerStatus(File directory, String fileName, long timestamp) { + if (isRegisteredDeployment(fileName)) { final File undeployedMarker = new File(directory, fileName + UNDEPLOYED); if (isMarkedUndeployed(undeployedMarker, timestamp) && !scanContext.persistentDeployments.contains(fileName)) { try { @@ -1058,7 +1092,7 @@ private void synchronizeScannerStatus(ScanContext scanContext, File directory, S } private long addContentAddingTask(final String path, final boolean archive, final String deploymentName, - final File deploymentFile, final long timestamp, final ScanContext scanContext) { + final File deploymentFile, final long timestamp) { if (deploymentTransformer != null) { try { deploymentTransformer.transform(deploymentFile.toPath(), deploymentFile.toPath()); @@ -1076,7 +1110,7 @@ private long addContentAddingTask(final String path, final boolean archive, fina return timestamp; } - private boolean isRegisteredDeployment(final ScanContext scanContext, final String fileName) { + private boolean isRegisteredDeployment(final String fileName) { if(!scanContext.persistentDeployments.contains(fileName)) {//check that we are talking about the deployment in the scanned folder return scanContext.registeredDeployments.get(fileName) == null ? false : scanContext.registeredDeployments.get(fileName); } @@ -1149,7 +1183,7 @@ private void removeExtraneousMarker(File child, final String fileName) { * * @return true if the scan should be aborted */ - private ScanStatus handleAutoDeployFailures(ScanContext scanContext) { + private ScanStatus handleAutoDeployFailures() { ScanStatus result = ScanStatus.PROCEED; boolean warnLogged = false; diff --git a/deployment-scanner/src/test/java/org/jboss/as/server/deployment/scanner/FileSystemDeploymentServiceUnitTestCase.java b/deployment-scanner/src/test/java/org/jboss/as/server/deployment/scanner/FileSystemDeploymentServiceUnitTestCase.java index 680c9ab5240..0a3c58d4c75 100644 --- a/deployment-scanner/src/test/java/org/jboss/as/server/deployment/scanner/FileSystemDeploymentServiceUnitTestCase.java +++ b/deployment-scanner/src/test/java/org/jboss/as/server/deployment/scanner/FileSystemDeploymentServiceUnitTestCase.java @@ -478,6 +478,8 @@ public void testCleanSpuriousMarkers() throws Exception { f1 = createFile("spurious" + FileSystemDeploymentService.DEPLOYED); f2 = createFile(new File(tmpDir, "nested"), "nested" + FileSystemDeploymentService.DEPLOYED); + // WFCORE-6255 added UndeployTask, so here needs to be added mocked response which serves as counter of requests in the processOp() + ts.controller.addCompositeSuccessResponse(1); ts.testee.scan(); // Failed deployments should be cleaned on subsequent scans. @@ -688,6 +690,66 @@ public void testFailedRedeployNoMarker() throws Exception { } + /** + * Test for WFCORE-6255 + * It tests that the failed archives are undeployed + */ + @Test + public void testRemoveFailedDeployment() throws Exception { + File war = createFile("foo.war"); + File dodeploy = createFile("foo.war" + FileSystemDeploymentService.DO_DEPLOY); + File deployed = new File(tmpDir, "foo.war" + FileSystemDeploymentService.DEPLOYED); + File failed = new File(tmpDir, "foo.war" + FileSystemDeploymentService.FAILED_DEPLOY); + TesteeSet ts = createTestee(); + ts.testee.setAutoDeployZippedContent(true); + + // failure of deploying the foo.war (must be ready before scan) + ts.controller.addCompositeFailureResponse(1, 1); + ts.testee.scan(); + assertTrue(war.exists()); + assertFalse(dodeploy.exists()); + assertFalse(deployed.exists()); + assertTrue(failed.exists()); + + assertTrue(war.delete()); + // success of undeploying failed foo.war from the server (must be ready before scan) + ts.controller.addCompositeSuccessResponse(1); + ts.testee.scan(); + assertFalse(war.exists()); + assertFalse(dodeploy.exists()); + assertFalse(deployed.exists()); + assertFalse(failed.exists()); + assertEquals(0, ts.controller.added.size()); + assertEquals(0, ts.controller.deployed.size()); + assertTrue(ts.testee.getScanContextRegisteredDeployments().containsKey("foo.war")); + + // failed deployment is released from registeredDeployments at the beginning of the next scan (schedule rescan) + ts.testee.scan(); + assertEquals(0, ts.testee.getScanContextRegisteredDeployments().size()); + } + + /** + * After WFCORE-6255 we need a test that validates that the deployment not associated with the scanner being tested isn't undeployed because + * a FAILED_DEPLOY marker file is left in the scanner's directory + */ + @Test + public void testIgnoreExternalScannerFailedDeployment() throws Exception { + MockServerController sc = new MockServerController("foo.war"); + TesteeSet ts = createTestee(sc); + ts.controller.externallyDeployed.put("foo.war", null); + assertThat(ts.controller.deployed.size(), is(1)); + ts.testee.bootTimeScan(sc.create()); + File failed = new File(tmpDir, "foo.war" + FileSystemDeploymentService.FAILED_DEPLOY); + + // there is no need for mocked method addCompositeSuccessResponse associated with UndeployTask that means + // that there is no undeployment attempt + ts.testee.scan(); + + assertFalse(failed.exists()); + assertThat(ts.controller.deployed.size(), is(1)); + assertThat(ts.controller.deployed.keySet(), hasItems("foo.war")); + } + @Test public void testSuccessfulRetryRedeploy() throws Exception { File war1 = createFile("bar.war");