From 19f80967d511bfff7399290ccbb6c98304e6e2b2 Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Thu, 9 Oct 2014 17:04:20 +0200 Subject: SONAR-5715 Fix preview mode when source file contains space in its path --- .../org/sonar/batch/bootstrap/ServerClient.java | 11 ++++++++ .../DefaultProjectReferentialsLoader.java | 9 +------ .../java/org/sonar/batch/scan/LastSnapshots.java | 3 ++- .../sonar/batch/bootstrap/ServerClientTest.java | 5 ++++ .../org/sonar/batch/scan/LastSnapshotsTest.java | 31 +++++++++++++++++++--- 5 files changed, 46 insertions(+), 13 deletions(-) (limited to 'sonar-batch') diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java index d43ef8e0e71..0fcc09bcf3d 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java @@ -36,7 +36,9 @@ import javax.annotation.Nullable; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.UnsupportedEncodingException; import java.net.URI; +import java.net.URLEncoder; /** * Replace the deprecated org.sonar.batch.ServerMetadata @@ -135,4 +137,13 @@ public class ServerClient implements BatchComponent { private String getPassword() { return props.property(CoreProperties.PASSWORD); } + + public static String encodeForUrl(String url) { + try { + return URLEncoder.encode(url, "UTF-8"); + + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException("Encoding not supported", e); + } + } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/referential/DefaultProjectReferentialsLoader.java b/sonar-batch/src/main/java/org/sonar/batch/referential/DefaultProjectReferentialsLoader.java index 8340c63a681..7ff216c4280 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/referential/DefaultProjectReferentialsLoader.java +++ b/sonar-batch/src/main/java/org/sonar/batch/referential/DefaultProjectReferentialsLoader.java @@ -28,9 +28,6 @@ import org.sonar.batch.bootstrap.TaskProperties; import org.sonar.batch.protocol.input.ProjectReferentials; import org.sonar.batch.rule.ModuleQProfiles; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; - public class DefaultProjectReferentialsLoader implements ProjectReferentialsLoader { private static final Logger LOG = LoggerFactory.getLogger(DefaultProjectReferentialsLoader.class); @@ -51,11 +48,7 @@ public class DefaultProjectReferentialsLoader implements ProjectReferentialsLoad if (taskProperties.properties().containsKey(ModuleQProfiles.SONAR_PROFILE_PROP)) { LOG.warn("Ability to set quality profile from command line using '" + ModuleQProfiles.SONAR_PROFILE_PROP + "' is deprecated and will be dropped in a future SonarQube version. Please configure quality profile used by your project on SonarQube server."); - try { - url += "&profile=" + URLEncoder.encode(taskProperties.properties().get(ModuleQProfiles.SONAR_PROFILE_PROP), "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException("Unable to encode URL", e); - } + url += "&profile=" + ServerClient.encodeForUrl(taskProperties.properties().get(ModuleQProfiles.SONAR_PROFILE_PROP)); } url += "&preview=" + analysisMode.isPreview(); return ProjectReferentials.fromJson(serverClient.request(url)); diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/LastSnapshots.java b/sonar-batch/src/main/java/org/sonar/batch/scan/LastSnapshots.java index be876cbc0c9..7bea9e46216 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/LastSnapshots.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/LastSnapshots.java @@ -58,7 +58,8 @@ public class LastSnapshots implements BatchComponent { @CheckForNull private String loadSourceFromWs(Resource resource) { try { - return server.request("/api/sources?resource=" + resource.getEffectiveKey() + "&format=txt", false, analysisMode.getPreviewReadTimeoutSec() * 1000); + return server + .request("/api/sources?resource=" + ServerClient.encodeForUrl(resource.getEffectiveKey()) + "&format=txt", false, analysisMode.getPreviewReadTimeoutSec() * 1000); } catch (HttpDownloader.HttpException he) { if (he.getResponseCode() == 404) { return ""; diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ServerClientTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ServerClientTest.java index 34060a4ee30..9c9b930f99d 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ServerClientTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/ServerClientTest.java @@ -135,6 +135,11 @@ public class ServerClientTest { newServerClient().request("/foo"); } + @Test + public void testEncode() { + assertThat(ServerClient.encodeForUrl("my value")).isEqualTo("my+value"); + } + private ServerClient newServerClient() { when(bootstrapProps.property("sonar.host.url")).thenReturn("http://localhost:" + server.getPort()); return new ServerClient(bootstrapProps, new EnvironmentInformation("Junit", "4")); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/LastSnapshotsTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/LastSnapshotsTest.java index 505b644d98e..1c3746408ab 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/LastSnapshotsTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/LastSnapshotsTest.java @@ -37,7 +37,10 @@ import java.net.URISyntaxException; import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; public class LastSnapshotsTest { @@ -88,7 +91,21 @@ public class LastSnapshotsTest { String source = lastSnapshots.getSource(newFile()); assertThat(source).isEqualTo("downloaded source of Bar.c"); - verify(server).request("/api/sources?resource=myproject:org/foo/Bar.c&format=txt", false, 30 * 1000); + verify(server).request("/api/sources?resource=myproject%3Aorg%2Ffoo%2FBar.c&format=txt", false, 30 * 1000); + } + + @Test + public void should_download_source_with_space_from_ws_if_preview_mode() { + db.prepareDbUnit(getClass(), "last_snapshot.xml"); + ServerClient server = mock(ServerClient.class); + when(server.request(anyString(), eq(false), eq(30 * 1000))).thenReturn("downloaded source of Foo Bar.c"); + + when(mode.isPreview()).thenReturn(true); + LastSnapshots lastSnapshots = new LastSnapshots(mode, new SnapshotSourceDao(db.myBatis()), server); + + String source = lastSnapshots.getSource(newFile()); + assertThat(source).isEqualTo("downloaded source of Foo Bar.c"); + verify(server).request("/api/sources?resource=myproject%3Aorg%2Ffoo%2FBar.c&format=txt", false, 30 * 1000); } @Test @@ -113,9 +130,9 @@ public class LastSnapshotsTest { when(mode.isPreview()).thenReturn(true); LastSnapshots lastSnapshots = new LastSnapshots(mode, new SnapshotSourceDao(db.myBatis()), server); - String source = lastSnapshots.getSource(newFile()); + String source = lastSnapshots.getSource(newFileWithSpace()); assertThat(source).isEqualTo(""); - verify(server).request("/api/sources?resource=myproject:org/foo/Bar.c&format=txt", false, 30 * 1000); + verify(server).request("/api/sources?resource=myproject%3Aorg%2Ffoo%2FFoo+Bar.c&format=txt", false, 30 * 1000); } @Test @@ -134,4 +151,10 @@ public class LastSnapshotsTest { file.setEffectiveKey("myproject:org/foo/Bar.c"); return file; } + + private File newFileWithSpace() { + File file = new File("org/foo", "Foo Bar.c"); + file.setEffectiveKey("myproject:org/foo/Foo Bar.c"); + return file; + } } -- cgit v1.2.3 From 4e3edd5a810a59bf307e548ecbc14fd7de7eb690 Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Thu, 9 Oct 2014 17:10:01 +0200 Subject: SONAR-5068 Deprecate binaries and libraries related methods --- .../filesystem/ModuleFileSystemInitializer.java | 4 ++++ .../api/batch/bootstrap/ProjectDefinition.java | 25 +++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) (limited to 'sonar-batch') diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleFileSystemInitializer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleFileSystemInitializer.java index fa79c8e682d..3d6aec8934c 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleFileSystemInitializer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleFileSystemInitializer.java @@ -106,6 +106,10 @@ public class ModuleFileSystemInitializer implements BatchComponent { return testDirsOrFiles; } + /** + * @deprecated since 4.5.1 use SonarQube Java specific API + */ + @Deprecated List binaryDirs() { return binaryDirs; } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectDefinition.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectDefinition.java index a5fa7ab0d93..ec3bb5efa07 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectDefinition.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/bootstrap/ProjectDefinition.java @@ -62,7 +62,15 @@ public class ProjectDefinition { */ @Deprecated public static final String TEST_FILES_PROPERTY = "sonar.testFiles"; + /** + * @deprecated since 4.5.1 use SonarQube Java specific API + */ + @Deprecated public static final String BINARIES_PROPERTY = "sonar.binaries"; + /** + * @deprecated since 4.5.1 use SonarQube Java specific API + */ + @Deprecated public static final String LIBRARIES_PROPERTY = "sonar.libraries"; public static final String BUILD_DIR_PROPERTY = "sonar.buildDir"; @@ -450,6 +458,10 @@ public class ProjectDefinition { return tests(); } + /** + * @deprecated since 4.5.1 use SonarQube Java specific API + */ + @Deprecated public List getBinaries() { String sources = properties.getProperty(BINARIES_PROPERTY, ""); return trim(StringUtils.split(sources, SEPARATOR)); @@ -458,17 +470,26 @@ public class ProjectDefinition { /** * @param path path to directory with compiled source. In case of Java this is directory with class files. * It can be absolute or relative to project directory. - * TODO currently Sonar supports only one such directory due to dependency on MavenProject + * @deprecated since 4.5.1 use SonarQube Java specific API */ + @Deprecated public ProjectDefinition addBinaryDir(String path) { appendProperty(BINARIES_PROPERTY, path); return this; } + /** + * @deprecated since 4.5.1 use SonarQube Java specific API + */ + @Deprecated public ProjectDefinition addBinaryDir(File f) { return addBinaryDir(f.getAbsolutePath()); } + /** + * @deprecated since 4.5.1 use SonarQube Java specific API + */ + @Deprecated public List getLibraries() { String sources = properties.getProperty(LIBRARIES_PROPERTY, ""); return trim(StringUtils.split(sources, SEPARATOR)); @@ -477,7 +498,9 @@ public class ProjectDefinition { /** * @param path path to file with third-party library. In case of Java this is path to jar file. * It can be absolute or relative to project directory. + * @deprecated since 4.5.1 use SonarQube Java specific API */ + @Deprecated public void addLibrary(String path) { appendProperty(LIBRARIES_PROPERTY, path); } -- cgit v1.2.3 From 0d2a019b933e3a90de9c8365bfa4037b9d44d943 Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Fri, 10 Oct 2014 11:17:24 +0200 Subject: SONAR-5256 Fix NPE and add warning when sonar.importSources=false --- .../org/sonar/plugins/core/issue/SourceHashHolder.java | 14 ++++++-------- .../org/sonar/batch/scan/filesystem/ComponentIndexer.java | 7 +++++++ .../src/main/java/org/sonar/api/batch/SonarIndex.java | 4 +++- 3 files changed, 16 insertions(+), 9 deletions(-) (limited to 'sonar-batch') diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/SourceHashHolder.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/SourceHashHolder.java index 8caec63784f..c10586fda05 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/SourceHashHolder.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/SourceHashHolder.java @@ -19,8 +19,7 @@ */ package org.sonar.plugins.core.issue; -import java.util.Collection; - +import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.SonarIndex; import org.sonar.api.resources.Resource; import org.sonar.batch.scan.LastSnapshots; @@ -28,7 +27,7 @@ import org.sonar.plugins.core.issue.tracking.HashedSequence; import org.sonar.plugins.core.issue.tracking.StringText; import org.sonar.plugins.core.issue.tracking.StringTextComparator; - +import java.util.Collection; public class SourceHashHolder { @@ -66,15 +65,15 @@ public class SourceHashHolder { } public String getSource() { - if (! sourceInitialized) { - source = index.getSource(resource); + if (!sourceInitialized) { + source = StringUtils.defaultString(index.getSource(resource), ""); sourceInitialized = true; } return source; } public String getReferenceSource() { - if (! referenceSourceInitialized) { + if (!referenceSourceInitialized) { if (resource != null) { referenceSource = lastSnapshots.getSource(resource); } @@ -88,7 +87,7 @@ public class SourceHashHolder { } private void initHashesIfNull(Object required) { - if(required == null) { + if (required == null) { initHashes(); } } @@ -97,4 +96,3 @@ public class SourceHashHolder { return getHashedSource().getLinesForHash(getHashedReference().getHash(originLine)); } } - diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ComponentIndexer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ComponentIndexer.java index 1e185487a05..65a7585555e 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ComponentIndexer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ComponentIndexer.java @@ -22,6 +22,8 @@ package org.sonar.batch.scan.filesystem; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.CharMatcher; import com.google.common.io.Files; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.sonar.api.BatchComponent; import org.sonar.api.CoreProperties; import org.sonar.api.batch.SonarIndex; @@ -44,6 +46,8 @@ import org.sonar.batch.util.DeprecatedKeyUtils; */ public class ComponentIndexer implements BatchComponent { + private static final Logger LOG = LoggerFactory.getLogger(ComponentIndexer.class); + private final Languages languages; private final Settings settings; private final SonarIndex sonarIndex; @@ -62,6 +66,9 @@ public class ComponentIndexer implements BatchComponent { migration.migrateIfNeeded(module, fs); boolean shouldImportSource = settings.getBoolean(CoreProperties.CORE_IMPORT_SOURCES_PROPERTY); + if (!shouldImportSource) { + LOG.warn("Not importing source will prevent issues to be properly tracked between consecutive analyses"); + } for (InputFile inputFile : fs.inputFiles(fs.predicates().all())) { String languageKey = inputFile.language(); boolean unitTest = InputFile.Type.TEST == inputFile.type(); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java index f52fcb98f0a..cbe29014b2f 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java @@ -100,9 +100,11 @@ public abstract class SonarIndex implements DirectedGraphAccessornull if not available + * @return source code associated with a specified resource, null if not available + * (for example when sonar.importSources=false) * @since 2.9 */ + @CheckForNull public abstract String getSource(Resource resource); public abstract Project getProject(); -- cgit v1.2.3 From 5e7744ae006b41948359fe60841e8e66701fc5f9 Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Fri, 10 Oct 2014 13:27:20 +0200 Subject: SONAR-5330 Fix issue when source dirs are symbolic links --- .../java/org/sonar/batch/bootstrap/TempFolderProvider.java | 2 +- .../sonar/batch/mediumtest/fs/FileSystemMediumTest.java | 14 ++++++++++++++ .../mediumtest/xoo/sample-with-symlink/.gitignore | 1 + .../xoo/sample-with-symlink/sonar-project.properties | 6 ++++++ .../resources/mediumtest/xoo/sample-with-symlink/testx | 1 + .../resources/mediumtest/xoo/sample-with-symlink/xources | 1 + .../java/org/sonar/api/scan/filesystem/PathResolver.java | 3 ++- 7 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 sonar-batch/src/test/resources/mediumtest/xoo/sample-with-symlink/.gitignore create mode 100644 sonar-batch/src/test/resources/mediumtest/xoo/sample-with-symlink/sonar-project.properties create mode 120000 sonar-batch/src/test/resources/mediumtest/xoo/sample-with-symlink/testx create mode 120000 sonar-batch/src/test/resources/mediumtest/xoo/sample-with-symlink/xources (limited to 'sonar-batch') diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TempFolderProvider.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TempFolderProvider.java index fbf973640ed..b665d9d3279 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TempFolderProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TempFolderProvider.java @@ -36,7 +36,7 @@ public class TempFolderProvider extends ProviderAdapter { public TempFolder provide(BootstrapProperties bootstrapProps) { if (tempFolder == null) { String workingDirPath = StringUtils.defaultIfBlank(bootstrapProps.property(CoreProperties.WORKING_DIRECTORY), CoreProperties.WORKING_DIRECTORY_DEFAULT_VALUE); - File workingDir = new File(workingDirPath); + File workingDir = new File(workingDirPath).getAbsoluteFile(); File tempDir = new File(workingDir, ".sonartmp"); try { FileUtils.forceMkdir(tempDir); diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java index 19d7aea020a..0d171bcfc09 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java @@ -29,6 +29,7 @@ import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.utils.MessageException; +import org.sonar.api.utils.System2; import org.sonar.batch.mediumtest.BatchMediumTester; import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult; import org.sonar.batch.protocol.input.ActiveRule; @@ -38,6 +39,7 @@ import java.io.File; import java.io.IOException; import static org.fest.assertions.Assertions.assertThat; +import static org.junit.Assume.assumeFalse; public class FileSystemMediumTest { @@ -169,4 +171,16 @@ public class FileSystemMediumTest { } + // SONAR-5330 + @Test + public void scanProjectWithSourceSymlink() throws Exception { + assumeFalse(System2.INSTANCE.isOsWindows()); + File projectDir = new File("src/test/resources/mediumtest/xoo/sample-with-symlink"); + TaskResult result = tester + .newScanTask(new File(projectDir, "sonar-project.properties")) + .start(); + + assertThat(result.inputFiles()).hasSize(3); + } + } diff --git a/sonar-batch/src/test/resources/mediumtest/xoo/sample-with-symlink/.gitignore b/sonar-batch/src/test/resources/mediumtest/xoo/sample-with-symlink/.gitignore new file mode 100644 index 00000000000..ecbefd4f19d --- /dev/null +++ b/sonar-batch/src/test/resources/mediumtest/xoo/sample-with-symlink/.gitignore @@ -0,0 +1 @@ +.sonar diff --git a/sonar-batch/src/test/resources/mediumtest/xoo/sample-with-symlink/sonar-project.properties b/sonar-batch/src/test/resources/mediumtest/xoo/sample-with-symlink/sonar-project.properties new file mode 100644 index 00000000000..8810e376701 --- /dev/null +++ b/sonar-batch/src/test/resources/mediumtest/xoo/sample-with-symlink/sonar-project.properties @@ -0,0 +1,6 @@ +sonar.projectKey=sample +sonar.projectName=Sample +sonar.projectVersion=0.1-SNAPSHOT +sonar.sources=xources +sonar.tests=testx +sonar.language=xoo diff --git a/sonar-batch/src/test/resources/mediumtest/xoo/sample-with-symlink/testx b/sonar-batch/src/test/resources/mediumtest/xoo/sample-with-symlink/testx new file mode 120000 index 00000000000..7385ebd51cf --- /dev/null +++ b/sonar-batch/src/test/resources/mediumtest/xoo/sample-with-symlink/testx @@ -0,0 +1 @@ +../sample/testx/ \ No newline at end of file diff --git a/sonar-batch/src/test/resources/mediumtest/xoo/sample-with-symlink/xources b/sonar-batch/src/test/resources/mediumtest/xoo/sample-with-symlink/xources new file mode 120000 index 00000000000..15dca9d90d2 --- /dev/null +++ b/sonar-batch/src/test/resources/mediumtest/xoo/sample-with-symlink/xources @@ -0,0 +1 @@ +../sample/xources/ \ No newline at end of file diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/PathResolver.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/PathResolver.java index 1dbaeaeca11..6da18e4a049 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/PathResolver.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/PathResolver.java @@ -26,6 +26,7 @@ import org.sonar.api.BatchComponent; import org.sonar.api.utils.PathUtils; import javax.annotation.CheckForNull; + import java.io.File; import java.util.Collection; import java.util.List; @@ -40,7 +41,7 @@ public class PathResolver implements BatchComponent { File file = new File(path); if (!file.isAbsolute()) { try { - file = new File(dir, path).getCanonicalFile(); + file = new File(dir, path).getAbsoluteFile(); } catch (Exception e) { throw new IllegalStateException("Fail to resolve path '" + path + "' relative to: " + dir.getAbsolutePath(), e); } -- cgit v1.2.3