All Downloads are FREE. Search and download functionalities are using the official Maven repository.

migratedb.v1.core.api.Location Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) Red Gate Software Ltd 2010-2021
 * Copyright 2022-2024 The MigrateDB contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package migratedb.v1.core.api;

import migratedb.v1.core.internal.resource.classpath.ClassPathResourceProvider;
import migratedb.v1.core.internal.resource.filesystem.FileSystemResourceProvider;
import migratedb.v1.core.internal.util.ClassUtils;
import migratedb.v1.core.internal.util.StringUtils;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * A location to load migrations from.
 * 

* Note: Although this class is declared abstract, it is not meant to be subclassed outside its compilation unit. To * provide a custom location type, use {@link CustomLocation}. */ public abstract class Location { // Prevent subclassing outside this compilation unit private Location() { } public static Location parse(String locationString, @Nullable ClassLoader classLoader) { if (locationString.startsWith(FileSystemLocation.PREFIX)) { var baseDirectory = Paths.get(locationString.substring(FileSystemLocation.PREFIX.length())); return new FileSystemLocation(baseDirectory); } else if (locationString.startsWith(CustomLocation.PREFIX)) { var providerClass = locationString.substring(CustomLocation.PREFIX.length()); return CustomLocation.fromClass(providerClass, classLoader); } else { String packageName; if (locationString.startsWith(ClassPathLocation.PREFIX)) { packageName = locationString.substring(ClassPathLocation.PREFIX.length()); } else { packageName = locationString; } return new ClassPathLocation(packageName, classLoader); } } public abstract ResourceProvider resourceProvider(); public abstract ClassProvider classProvider(); public abstract boolean exists(); /** * The inverse of {@link #parse(String, ClassLoader)}. */ @Override public abstract String toString(); public static final class CustomLocation extends Location { /** * The prefix for custom location implementations. */ public static final String PREFIX = "custom:"; private final ClassProvider classProvider; private final ResourceProvider resourceProvider; public static CustomLocation fromClass(String className, @Nullable ClassLoader classLoader) { var providerInstance = ClassUtils.instantiate(className, classLoader); var errorPrefix = "Location '" + CustomLocation.PREFIX + className + "' must implement "; if (!(providerInstance instanceof ClassProvider)) { throw new MigrateDbException(errorPrefix + ClassProvider.class.getName()); } if (!(providerInstance instanceof ResourceProvider)) { throw new MigrateDbException(errorPrefix + ResourceProvider.class.getName()); } return new CustomLocation((ClassProvider) providerInstance, (ResourceProvider) providerInstance); } public CustomLocation(ClassProvider classProvider, ResourceProvider resourceProvider) { this.classProvider = classProvider; this.resourceProvider = resourceProvider; } @Override public ResourceProvider resourceProvider() { return resourceProvider; } @Override public ClassProvider classProvider() { return classProvider; } @Override public boolean exists() { return true; } @Override public boolean equals(Object o) { if (!(o instanceof CustomLocation)) { return false; } CustomLocation other = (CustomLocation) o; return resourceProvider.equals(other.resourceProvider) && classProvider.equals(other.classProvider); } @Override public int hashCode() { return Objects.hash(resourceProvider, classProvider); } @Override public String toString() { return PREFIX + classProvider.getClass().getName(); } } public static final class ClassPathLocation extends Location { /** * The prefix for classpath locations. */ public static final String PREFIX = "classpath:"; /** * The resource that contains the names of resources to provide. One line per resource. */ public static final String RESOURCE_LIST_RESOURCE_NAME = "migratedb-resources.index"; /** * The resource that contains the names of classes to provide. One line per class. */ public static final String CLASS_LIST_RESOURCE_NAME = "migratedb-classes.index"; private final String namePrefixWithTrailingSlash; private final ClassLoader classLoader; public ClassPathLocation(String namePrefix, @Nullable ClassLoader classLoader) { var trimmed = StringUtils.trimChar(namePrefix, '/'); this.namePrefixWithTrailingSlash = trimmed + "/"; this.classLoader = classLoader == null ? ClassUtils.defaultClassLoader() : classLoader; } /** * @return The resource name prefix of the index files without leading or trailing slashes, e.g. {@code db/migration}. */ public String namePrefix() { return StringUtils.trimChar(namePrefixWithTrailingSlash, '/'); } @Override public ResourceProvider resourceProvider() { return new ClassPathResourceProvider(classLoader, readLines(RESOURCE_LIST_RESOURCE_NAME)); } @Override public ClassProvider classProvider() { return new ClassProvider<>() { private final List> classes = readLines(CLASS_LIST_RESOURCE_NAME) .stream() .map(it -> ClassUtils.loadClass(it, classLoader)) .filter(Objects::nonNull) .collect(Collectors.toUnmodifiableList()); @Override public Collection> getClasses() { return classes; } }; } @Override public boolean exists() { return classLoader.getResource(namePrefixWithTrailingSlash + RESOURCE_LIST_RESOURCE_NAME) != null || classLoader.getResource(namePrefixWithTrailingSlash + CLASS_LIST_RESOURCE_NAME) != null; } private List readLines(String relativeResourceName) { var result = new ArrayList(); try { for (var resource : Collections.list(classLoader.getResources(namePrefixWithTrailingSlash + relativeResourceName))) { try (var reader = new BufferedReader(new InputStreamReader(resource.openStream(), UTF_8))) { reader.lines().sequential().forEach(result::add); } } return result; } catch (IOException e) { throw new MigrateDbException(e); } } @Override public boolean equals(Object o) { if (!(o instanceof ClassPathLocation)) { return false; } ClassPathLocation other = (ClassPathLocation) o; return namePrefixWithTrailingSlash.equals(other.namePrefixWithTrailingSlash) && classLoader.equals(other.classLoader); } @Override public int hashCode() { return Objects.hash(namePrefixWithTrailingSlash, classLoader); } @Override public String toString() { return PREFIX + namePrefix(); } } public static final class FileSystemLocation extends Location { /** * The prefix for filesystem locations. */ public static final String PREFIX = "filesystem:"; private final Path baseDirectory; public FileSystemLocation(Path baseDirectory) { this.baseDirectory = baseDirectory.toAbsolutePath().normalize(); } @Override public ResourceProvider resourceProvider() { return new FileSystemResourceProvider(baseDirectory); } @Override public ClassProvider classProvider() { return ClassProvider.noClasses(); } @Override public boolean exists() { return Files.isDirectory(baseDirectory); } /** * @return The absolute, normalized base directory path. */ public Path getBaseDirectory() { return baseDirectory; } @Override public boolean equals(Object o) { if (!(o instanceof FileSystemLocation)) { return false; } FileSystemLocation other = (FileSystemLocation) o; return baseDirectory.equals(other.baseDirectory); } @Override public int hashCode() { return baseDirectory.hashCode(); } @Override public String toString() { return PREFIX + baseDirectory; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy