/*
* Copyright (c) 2013-2017 Cinchapi Inc.
*
* 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 com.cinchapi.concourse.server.upgrade;
import static com.cinchapi.concourse.server.upgrade.UpgradeTask.getCurrentSystemVersion;
import java.util.Set;
import org.reflections.Reflections;
import com.cinchapi.common.reflect.Reflection;
import com.cinchapi.concourse.server.GlobalState;
import com.cinchapi.concourse.server.io.FileSystem;
import com.cinchapi.concourse.server.upgrade.task.Upgrade2;
import com.cinchapi.concourse.util.Logger;
import com.google.common.collect.Sets;
/**
* A collection of methods that are responsible for bootstrapping and managing
* the upgrade process.
*
* @author Jeff Nelson
*/
public final class UpgradeTasks {
static {
Reflections.log = null; // turn off logging
}
/**
* The package that contains all upgrade tasks.
*/
private static final String[] pkgs = { "com.cinchapi.concourse.server.upgrade.task" };
/**
* Run all the upgrade tasks that are greater than the
* {@link UpgradeTask#getCurrentSystemVersion() current system version}.
*/
public static void runLatest() {
int currentSystemVersion = 0;
try {
currentSystemVersion = bootstrap();
}
catch (Exception e) {
String user = System.getProperty("user.name");
Logger.error(
"An error occurred while trying to bootstrap the upgrade framework, "
+ "which usually indicates that Concourse Server is configured to store "
+ "data in one or more locations where the current user ({}) does not "
+ "have write permission. Please check the prefs file at {} to make sure "
+ "you have properly configured the buffer_directory and database_directory. "
+ "If those properties are properly configured, please give \"{}\" write "
+ "permission to those directories.", user,
GlobalState.getPrefsFilePath(), user);
throw e;
}
// Find the new upgrade tasks
Set<UpgradeTask> tasks = Sets.newTreeSet();
for (String pkg : pkgs) {
Reflections reflections = new Reflections(pkg);
Set<Class<? extends UpgradeTask>> classes = reflections
.getSubTypesOf(UpgradeTask.class);
classes.addAll(reflections.getSubTypesOf(SmartUpgradeTask.class));
for (Class<? extends UpgradeTask> clazz : classes) {
UpgradeTask task = Reflection.newInstance(clazz);
if(task.version() > currentSystemVersion) {
tasks.add(task);
}
}
}
// Run the new upgrade tasks
for (UpgradeTask task : tasks) {
try {
task.run();
}
catch (Exception e) {
if(task instanceof Upgrade2) {
// CON-137: Even if Upgrade2 fails and we can't migrate
// data, still set the system version so we aren't
// blocked on this task in the future.
UpgradeTask.setCurrentSystemVersion(task.version());
Logger.info("Due to a bug in a previous "
+ "release, the system version has "
+ "been force upgraded " + "to {}", task.version());
}
else {
throw e; // fail fast because we assume subsequent tasks
// depend on the one that failed
}
}
}
}
/**
* Bootstrap a new Concourse Server installation with the latest system
* version, if necessary.
* <p>
* If this method detects that the Concourse Server installation is "fresh"
* it will assign the latest system version without running any upgrade
* tasks.
* </p>
*
* @return whatever the latest system version is after the affects, if any,
* of this method take affect; if the system is not fresh, calling
* this method has the same affect as calling
* {@link UpgradeTask#getCurrentSystemVersion()}.
*/
private static int bootstrap() {
String seal = ".douge";
int currentSystemVersion = getCurrentSystemVersion();
if(FileSystem.hasFile(seal)) {
UpgradeTask theTask = null;
// Go through the upgrade tasks and find the one with the largest
// schema version.
for (String pkg : pkgs) {
Reflections reflections = new Reflections(pkg);
Set<Class<? extends UpgradeTask>> classes = reflections
.getSubTypesOf(UpgradeTask.class);
classes.addAll(reflections
.getSubTypesOf(SmartUpgradeTask.class));
for (Class<? extends UpgradeTask> clazz : classes) {
UpgradeTask task = Reflection.newInstance(clazz);
if(theTask == null || task.version() > theTask.version()) {
theTask = task;
}
}
}
UpgradeTask.setCurrentSystemVersion(theTask.version());
Logger.info("The upgrade framework has been initialized "
+ "with a system version of {}", theTask.version());
FileSystem.deleteFile(seal);
return theTask.version();
}
return currentSystemVersion;
}
}