1 /*
2 * Copyright 2010-2020 Redgate Software Ltd
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.flywaydb.core.internal.scanner.classpath;
17
18 import org.flywaydb.core.api.logging.Log;
19 import org.flywaydb.core.api.logging.LogFactory;
20 import org.flywaydb.core.internal.util.UrlUtils;
21
22 import java.io.File;
23 import java.net.URL;
24 import java.util.Set;
25 import java.util.TreeSet;
26
27 /**
28 * ClassPathLocationScanner for the file system.
29 */
30 public class FileSystemClassPathLocationScanner implements ClassPathLocationScanner {
31 private static final Log LOG = LogFactory.getLog(FileSystemClassPathLocationScanner.class);
32
33 public Set<String> findResourceNames(String location, URL locationUrl) {
34 String filePath = UrlUtils.toFilePath(locationUrl);
35 File folder = new File(filePath);
36 if (!folder.isDirectory()) {
37 LOG.debug("Skipping path as it is not a directory: " + filePath);
38 return new TreeSet<>();
39 }
40
41 String classPathRootOnDisk = filePath.substring(0, filePath.length() - location.length());
42 if (!classPathRootOnDisk.endsWith(File.separator)) {
43 classPathRootOnDisk = classPathRootOnDisk + File.separator;
44 }
45 LOG.debug("Scanning starting at classpath root in filesystem: " + classPathRootOnDisk);
46 return findResourceNamesFromFileSystem(classPathRootOnDisk, location, folder);
47 }
48
49 /**
50 * Finds all the resource names contained in this file system folder.
51 *
52 * @param classPathRootOnDisk The location of the classpath root on disk, with a trailing slash.
53 * @param scanRootLocation The root location of the scan on the classpath, without leading or trailing slashes.
54 * @param folder The folder to look for resources under on disk.
55 * @return The resource names;
56 */
57 /*private -> for testing*/
58 @SuppressWarnings("ConstantConditions")
59 Set<String> findResourceNamesFromFileSystem(String classPathRootOnDisk, String scanRootLocation, File folder) {
60 LOG.debug("Scanning for resources in path: " + folder.getPath() + " (" + scanRootLocation + ")");
61
62 Set<String> resourceNames = new TreeSet<>();
63
64 File[] files = folder.listFiles();
65 for (File file : files) {
66 if (file.canRead()) {
67 if (file.isDirectory()) {
68 resourceNames.addAll(findResourceNamesFromFileSystem(classPathRootOnDisk, scanRootLocation, file));
69 } else {
70 resourceNames.add(toResourceNameOnClasspath(classPathRootOnDisk, file));
71 }
72 }
73 }
74
75 return resourceNames;
76 }
77
78 /**
79 * Converts this file into a resource name on the classpath.
80 *
81 * @param classPathRootOnDisk The location of the classpath root on disk, with a trailing slash.
82 * @param file The file.
83 * @return The resource name on the classpath.
84 */
85 private String toResourceNameOnClasspath(String classPathRootOnDisk, File file) {
86 String fileName = file.getAbsolutePath().replace("\\", "/");
87
88 //Cut off the part on disk leading to the root of the classpath
89 //This leaves a resource name starting with the scanRootLocation,
90 // with no leading slash, containing subDirs and the fileName.
91 return fileName.substring(classPathRootOnDisk.length());
92 }
93 }