-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implemented default scanner for native mode
- Loading branch information
1 parent
35a5498
commit 6c86272
Showing
1 changed file
with
128 additions
and
29 deletions.
There are no files selected for viewing
157 changes: 128 additions & 29 deletions
157
...ns/flyway/runtime/src/main/java/io/quarkus/flyway/runtime/QuarkusPathLocationScanner.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,141 @@ | ||
package io.quarkus.flyway.runtime; | ||
|
||
import java.io.IOException; | ||
import java.net.JarURLConnection; | ||
import java.net.URISyntaxException; | ||
import java.net.URL; | ||
import java.net.URLConnection; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.Enumeration; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
import java.util.TreeSet; | ||
import java.util.jar.JarEntry; | ||
import java.util.jar.JarFile; | ||
|
||
import org.flywaydb.core.api.Location; | ||
import org.flywaydb.core.api.logging.Log; | ||
import org.flywaydb.core.api.logging.LogFactory; | ||
import org.flywaydb.core.internal.resource.LoadableResource; | ||
import org.flywaydb.core.internal.resource.classpath.ClassPathResource; | ||
import org.flywaydb.core.internal.scanner.classpath.ResourceAndClassScanner; | ||
|
||
public final class QuarkusPathLocationScanner implements ResourceAndClassScanner { | ||
private static final Log LOG = LogFactory.getLog( QuarkusPathLocationScanner.class ); | ||
private final Location location; | ||
|
||
public QuarkusPathLocationScanner(Location location) { | ||
this.location = location; | ||
LOG.warn( "LOCATION " + location ); | ||
} | ||
|
||
/** | ||
* Scans the classpath for resources under the configured location. | ||
* | ||
* @return The resources that were found. | ||
*/ | ||
@Override | ||
public Collection<LoadableResource> scanForResources() { | ||
// NEED TO DO SOMETHING TO RETURN THE RESOURCES ADDED TO SVM in FlywayBuildStep | ||
return Collections.emptyList(); | ||
} | ||
|
||
/** | ||
* Scans the classpath for concrete classes under the specified package implementing this interface. | ||
* Non-instantiable abstract classes are filtered out. | ||
* | ||
* @return The non-abstract classes that were found. | ||
*/ | ||
@Override | ||
public Collection<Class<?>> scanForClasses() { | ||
return Collections.emptyList(); | ||
} | ||
private static final Log LOG = LogFactory.getLog(QuarkusPathLocationScanner.class); | ||
// TODO: this should be configurable, using flyway default | ||
private final static String DEFAULT_NATIVE_LOCATION = "db/migration"; | ||
private static Set<String> ALL_SQL_RESOURCES; | ||
|
||
static { | ||
try { | ||
ALL_SQL_RESOURCES = discoverApplicationMigrations(); | ||
} catch (IOException | URISyntaxException e) { | ||
throw new IllegalStateException("Could not discover Flyway migrations", e); | ||
} | ||
} | ||
|
||
public QuarkusPathLocationScanner(Location location) { | ||
if (!location.getPath().equals(DEFAULT_NATIVE_LOCATION)) { | ||
LOG.error("Invalid migrations location: " + location.getPath() + ". Flyway migrations must be located in " | ||
+ DEFAULT_NATIVE_LOCATION); | ||
} | ||
} | ||
|
||
/** | ||
* Scans the classpath for resources under the configured DEFAULT_NATIVE_LOCATION. | ||
* | ||
* @return The resources that were found. | ||
*/ | ||
@Override | ||
public Collection<LoadableResource> scanForResources() { | ||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); | ||
Set<LoadableResource> resources = new HashSet<>(); | ||
Location defaultLocation = new Location(DEFAULT_NATIVE_LOCATION); | ||
for (String file : ALL_SQL_RESOURCES) { | ||
LOG.info("Loading " + file); | ||
resources.add(new ClassPathResource(defaultLocation, file, classLoader, StandardCharsets.UTF_8)); | ||
} | ||
return resources; | ||
} | ||
|
||
private static Set<String> discoverApplicationMigrations() throws IOException, URISyntaxException { | ||
Set<String> resources = new HashSet<>(); | ||
try { | ||
Enumeration<URL> migrations = Thread.currentThread().getContextClassLoader().getResources( | ||
DEFAULT_NATIVE_LOCATION); | ||
while (migrations.hasMoreElements()) { | ||
URL path = migrations.nextElement(); | ||
LOG.info("Adding application migrations in path: " + path); | ||
if ("jar".equals(path.getProtocol())) { | ||
resources = scanInJar(path); | ||
} else { | ||
throw new IllegalStateException("Expecting jar file. Protocol not supported:" + path.getProtocol()); | ||
} | ||
|
||
} | ||
return resources; | ||
} catch (IOException e) { | ||
LOG.error("Error discovering application migrations: " + e.getMessage(), e); | ||
throw e; | ||
} | ||
} | ||
|
||
/** | ||
* Scans the classpath for concrete classes under the specified package implementing this interface. | ||
* Non-instantiable abstract classes are filtered out. | ||
* | ||
* @return The non-abstract classes that were found. | ||
*/ | ||
@Override | ||
public Collection<Class<?>> scanForClasses() { | ||
// Classes are not supported in native mode | ||
return Collections.emptyList(); | ||
} | ||
|
||
public static Set<String> scanInJar(URL locationUrl) { | ||
JarFile jarFile; | ||
try { | ||
jarFile = getJarFromUrl(locationUrl); | ||
} catch (IOException e) { | ||
LOG.warn("Unable to determine jar from url (" + locationUrl + "): " + e.getMessage()); | ||
return Collections.emptySet(); | ||
} | ||
|
||
try { | ||
return findResourceNamesFromJarFile(jarFile); | ||
} finally { | ||
try { | ||
jarFile.close(); | ||
} catch (IOException e) { | ||
// Ignore | ||
} | ||
} | ||
} | ||
|
||
private static JarFile getJarFromUrl(URL locationUrl) throws IOException { | ||
URLConnection con = locationUrl.openConnection(); | ||
if (con instanceof JarURLConnection) { | ||
// Should usually be the case for traditional JAR files. | ||
JarURLConnection jarCon = (JarURLConnection) con; | ||
jarCon.setUseCaches(false); | ||
return jarCon.getJarFile(); | ||
} | ||
throw new IllegalStateException("Could not open the jar file " + locationUrl); | ||
} | ||
|
||
private static Set<String> findResourceNamesFromJarFile(JarFile jarFile) { | ||
Set<String> resourceNames = new TreeSet<>(); | ||
Enumeration<JarEntry> entries = jarFile.entries(); | ||
while (entries.hasMoreElements()) { | ||
String entryName = entries.nextElement().getName(); | ||
if (entryName.startsWith(DEFAULT_NATIVE_LOCATION) && !entryName.endsWith("/")) { | ||
LOG.info("Discovered " + entryName); | ||
resourceNames.add(entryName); | ||
} | ||
} | ||
|
||
return resourceNames; | ||
} | ||
} |