diff --git a/flow-server/src/main/java/com/vaadin/flow/server/frontend/TaskCopyLocalFrontendFiles.java b/flow-server/src/main/java/com/vaadin/flow/server/frontend/TaskCopyLocalFrontendFiles.java index 7e522a9e84f..acd6e2cd009 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/frontend/TaskCopyLocalFrontendFiles.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/frontend/TaskCopyLocalFrontendFiles.java @@ -18,7 +18,11 @@ import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Objects; +import java.util.stream.Stream; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; @@ -74,6 +78,12 @@ static void copyLocalResources(File source, File target) { } try { FileUtils.copyDirectory(source, target); + try (Stream fileStream = Files + .walk(Paths.get(target.getPath()))) { + // used with try-with-resources as defined in walk API note + fileStream.filter(file -> !Files.isWritable(file)).forEach( + filePath -> filePath.toFile().setWritable(true)); + } } catch (IOException e) { throw new UncheckedIOException(String.format( "Failed to copy project frontend resources from '%s' to '%s'", diff --git a/flow-server/src/test/java/com/vaadin/flow/server/frontend/TaskCopyLocalFrontendFilesTest.java b/flow-server/src/test/java/com/vaadin/flow/server/frontend/TaskCopyLocalFrontendFilesTest.java new file mode 100644 index 00000000000..19de33381f7 --- /dev/null +++ b/flow-server/src/test/java/com/vaadin/flow/server/frontend/TaskCopyLocalFrontendFilesTest.java @@ -0,0 +1,53 @@ +package com.vaadin.flow.server.frontend; + +import java.io.File; +import java.io.IOException; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class TaskCopyLocalFrontendFilesTest { + + @Rule + public final TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Test + public void directoryWithReadOnlyFile_copyIsNotReadOnly() + throws IOException { + final File sourceFolder = createReadOnlySource(); + + final File outFolder = temporaryFolder.newFolder("out"); + + TaskCopyLocalFrontendFiles.copyLocalResources(sourceFolder, outFolder); + + final File copiedReadOnly = new File(outFolder, "readOnly.txt"); + Assert.assertTrue( + "Copied files should be writable even when source is readOnly", + copiedReadOnly.canWrite()); + + } + + @Test + public void directoryWithReadOnlyFile_canCopyMultipleTimesToSource() + throws IOException { + final File sourceFolder = createReadOnlySource(); + + final File outFolder = temporaryFolder.newFolder("out"); + + TaskCopyLocalFrontendFiles.copyLocalResources(sourceFolder, outFolder); + + TaskCopyLocalFrontendFiles.copyLocalResources(sourceFolder, outFolder); + } + + private File createReadOnlySource() throws IOException { + final File sourceFolder = temporaryFolder.newFolder("source"); + File readOnly = new File(sourceFolder, "readOnly.txt"); + readOnly.createNewFile(); + Assert.assertTrue("Could not make file read-only", + readOnly.setReadOnly()); + + return sourceFolder; + } +}