/** * Copyright 2010-2015 Axel Fontaine * <p/> * 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 * <p/> * http://www.apache.org/licenses/LICENSE-2.0 * <p/> * 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 com.contrastsecurity.cassandra.migration.resolver; import com.contrastsecurity.cassandra.migration.CassandraMigrationException; import com.contrastsecurity.cassandra.migration.config.ScriptsLocation; import com.contrastsecurity.cassandra.migration.config.ScriptsLocations; import com.contrastsecurity.cassandra.migration.info.ResolvedMigration; import com.contrastsecurity.cassandra.migration.resolver.cql.CqlMigrationResolver; import com.contrastsecurity.cassandra.migration.resolver.java.JavaMigrationResolver; import java.util.*; /** * Facility for retrieving and sorting the available migrations from the classpath through the various migration * resolvers. */ public class CompositeMigrationResolver implements MigrationResolver { /** * The migration resolvers to use internally. */ private Collection<MigrationResolver> migrationResolvers = new ArrayList<MigrationResolver>(); /** * The available migrations, sorted by version, newest first. An empty list is returned when no migrations can be * found. */ private List<ResolvedMigration> availableMigrations; /** * Creates a new CompositeMigrationResolver. * * @param classLoader The ClassLoader for loading migrations on the classpath. * @param locations The locations where migrations are located. * @param encoding The encoding of Cql migrations. * @param customMigrationResolvers Custom Migration Resolvers. */ public CompositeMigrationResolver(ClassLoader classLoader, ScriptsLocations locations, String encoding, MigrationResolver... customMigrationResolvers) { for (ScriptsLocation location : locations.getLocations()) { migrationResolvers.add(new CqlMigrationResolver(classLoader, location, encoding)); migrationResolvers.add(new JavaMigrationResolver(classLoader, location)); } migrationResolvers.addAll(Arrays.asList(customMigrationResolvers)); } /** * Finds all available migrations using all migration resolvers (cql, java, ...). * * @return The available migrations, sorted by version, oldest first. An empty list is returned when no migrations * can be found. * @throws CassandraMigrationException when the available migrations have overlapping versions. */ public List<ResolvedMigration> resolveMigrations() { if (availableMigrations == null) { availableMigrations = doFindAvailableMigrations(); } return availableMigrations; } /** * Finds all available migrations using all migration resolvers (cql, java, ...). * * @return The available migrations, sorted by version, oldest first. An empty list is returned when no migrations * can be found. * @throws CassandraMigrationException when the available migrations have overlapping versions. */ private List<ResolvedMigration> doFindAvailableMigrations() throws CassandraMigrationException { List<ResolvedMigration> migrations = new ArrayList<ResolvedMigration>(collectMigrations(migrationResolvers)); Collections.sort(migrations, new ResolvedMigrationComparator()); checkForIncompatibilities(migrations); return migrations; } /** * Collects all the migrations for all migration resolvers. * * @param migrationResolvers The migration resolvers to check. * @return All migrations. */ /* private -> for testing */ static Collection<ResolvedMigration> collectMigrations(Collection<MigrationResolver> migrationResolvers) { Set<ResolvedMigration> migrations = new HashSet<ResolvedMigration>(); for (MigrationResolver migrationResolver : migrationResolvers) { migrations.addAll(migrationResolver.resolveMigrations()); } return migrations; } /** * Checks for incompatible migrations. * * @param migrations The migrations to check. * @throws CassandraMigrationException when two different migration with the same version number are found. */ /* private -> for testing */ static void checkForIncompatibilities(List<ResolvedMigration> migrations) { // check for more than one migration with same version for (int i = 0; i < migrations.size() - 1; i++) { ResolvedMigration current = migrations.get(i); ResolvedMigration next = migrations.get(i + 1); if (current.getVersion().compareTo(next.getVersion()) == 0) { throw new CassandraMigrationException(String.format("Found more than one migration with version %s\nOffenders:\n-> %s (%s)\n-> %s (%s)", current.getVersion(), current.getPhysicalLocation(), current.getType(), next.getPhysicalLocation(), next.getType())); } } } }