
eu.cloudnetservice.node.version.ServiceVersionProvider Maven / Gradle / Ivy
/*
* Copyright 2019-2023 CloudNetService team & 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 eu.cloudnetservice.node.version;
import static com.google.gson.reflect.TypeToken.getParameterized;
import com.google.common.base.Preconditions;
import eu.cloudnetservice.common.JavaVersion;
import eu.cloudnetservice.common.StringUtil;
import eu.cloudnetservice.common.document.gson.JsonDocument;
import eu.cloudnetservice.common.io.FileUtil;
import eu.cloudnetservice.common.language.I18n;
import eu.cloudnetservice.common.log.LogManager;
import eu.cloudnetservice.common.log.Logger;
import eu.cloudnetservice.driver.event.EventManager;
import eu.cloudnetservice.driver.service.ServiceEnvironmentType;
import eu.cloudnetservice.node.console.animation.progressbar.ConsoleProgressWrappers;
import eu.cloudnetservice.node.template.listener.TemplatePrepareListener;
import eu.cloudnetservice.node.version.execute.InstallStep;
import eu.cloudnetservice.node.version.information.VersionInstaller;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import kong.unirest.Unirest;
import lombok.NonNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnmodifiableView;
@Singleton
public class ServiceVersionProvider {
private static final Logger LOGGER = LogManager.logger(ServiceVersionProvider.class);
private static final Path VERSION_CACHE_PATH = Path.of(System.getProperty(
"cloudnet.versioncache.path",
"local/versioncache"));
private static final int VERSIONS_FILE_VERSION = 3;
private static final Type COL_SER_VERSION = getParameterized(Collection.class, ServiceVersionType.class).getType();
private static final Type COL_ENV_TYPE = getParameterized(Collection.class, ServiceEnvironmentType.class).getType();
private final Map serviceVersionTypes = new ConcurrentHashMap<>();
private final Map serviceEnvironmentTypes = new ConcurrentHashMap<>();
private final ConsoleProgressWrappers consoleProgressWrappers;
@Inject
public ServiceVersionProvider(
@NonNull EventManager eventManager,
@NonNull ConsoleProgressWrappers consoleProgressWrappers
) {
this.consoleProgressWrappers = consoleProgressWrappers;
eventManager.registerListener(new TemplatePrepareListener());
}
public boolean loadServiceVersionTypes(@NonNull String url) throws IOException {
this.serviceVersionTypes.clear();
return Unirest
.get(url)
.asObject(rawResponse -> {
// only accept a 200 response
if (rawResponse.getStatus() == 200) {
return this.loadVersionsFromInputStream(rawResponse.getContent());
}
return false;
})
.getBody();
}
public void loadDefaultVersionTypes() {
this.serviceVersionTypes.clear();
this.loadVersionsFromInputStream(this.getClass().getClassLoader().getResourceAsStream("files/versions.json"));
}
private boolean loadVersionsFromInputStream(@Nullable InputStream inputStream) {
if (inputStream == null) {
return false;
}
var document = JsonDocument.newDocument(inputStream);
var fileVersion = document.getInt("fileVersion", -1);
if (VERSIONS_FILE_VERSION == fileVersion && document.contains("versions")) {
// load all service environments
Collection environments = document.get("environments", COL_ENV_TYPE);
environments.forEach(this::registerServiceEnvironmentType);
// load all service versions after the environment types as they need to be present
Collection versions = document.get("versions", COL_SER_VERSION);
versions.forEach(this::registerServiceVersionType);
// successful load
return true;
}
return false;
}
public void interruptInstallSteps() {
for (var installStep : InstallStep.values()) {
installStep.interrupt();
}
}
public void registerServiceVersionType(@NonNull ServiceVersionType versionType) {
// ensure that we know the target environment for the service version
Preconditions.checkNotNull(
this.getEnvironmentType(versionType.environmentType()),
"Missing environment %s for service version %s",
versionType.environmentType(),
versionType.name());
// register the service version
this.serviceVersionTypes.put(StringUtil.toLower(versionType.name()), versionType);
}
public @Nullable ServiceVersionType getServiceVersionType(@NonNull String name) {
return this.serviceVersionTypes.get(StringUtil.toLower(name));
}
public void registerServiceEnvironmentType(@NonNull ServiceEnvironmentType environmentType) {
this.serviceEnvironmentTypes.put(StringUtil.toUpper(environmentType.name()), environmentType);
}
public @Nullable ServiceEnvironmentType getEnvironmentType(@NonNull String name) {
return this.serviceEnvironmentTypes.get(StringUtil.toUpper(name));
}
public boolean installServiceVersion(@NonNull VersionInstaller installer, boolean force) {
var fullVersionIdentifier = String.format("%s-%s",
installer.serviceVersionType().name(),
installer.serviceVersion().name());
if (!force
&& installer.installerExecutable() == null
&& !installer.serviceVersionType().canInstall(installer.serviceVersion())
) {
throw new IllegalArgumentException(String.format(
"Cannot run %s on %s",
fullVersionIdentifier,
JavaVersion.runtimeVersion().name()));
}
if (installer.serviceVersion().deprecated()) {
LOGGER.warning(I18n.trans("versions-installer-deprecated-version"));
}
try {
// delete all old application files to prevent that they are used to start the service
installer.removeServiceVersions(this.serviceVersionTypes.values());
} catch (IOException exception) {
LOGGER.severe("Exception while deleting old application files", exception);
}
var workingDirectory = FileUtil.createTempFile();
var cachedFilePath = VERSION_CACHE_PATH.resolve(fullVersionIdentifier);
try {
if (installer.cacheFiles() && Files.exists(cachedFilePath)) {
InstallStep.DEPLOY.execute(installer, cachedFilePath, Files.walk(cachedFilePath).collect(Collectors.toSet()));
} else {
Files.createDirectories(workingDirectory);
List installSteps = new ArrayList<>(installer.serviceVersionType().installSteps());
installSteps.add(InstallStep.DEPLOY);
Set lastStepResult = new HashSet<>();
for (var installStep : installSteps) {
lastStepResult = installStep.execute(installer, workingDirectory, lastStepResult);
}
if (installer.serviceVersion().cacheFiles()) {
for (var path : lastStepResult) {
var targetPath = cachedFilePath.resolve(workingDirectory.relativize(path));
Files.createDirectories(targetPath.getParent());
Files.copy(path, targetPath, StandardCopyOption.REPLACE_EXISTING);
}
}
}
for (var entry : installer.serviceVersion().additionalDownloads().entrySet()) {
this.consoleProgressWrappers.wrapDownload(entry.getValue(), in -> installer.deployFile(in, entry.getKey()));
}
return true;
} catch (Exception exception) {
LOGGER.severe("Exception while installing application files", exception);
} finally {
FileUtil.delete(workingDirectory);
}
return false;
}
@UnmodifiableView
public @NonNull Map serviceVersionTypes() {
return Collections.unmodifiableMap(this.serviceVersionTypes);
}
@UnmodifiableView
public @NonNull Map knownEnvironments() {
return Collections.unmodifiableMap(this.serviceEnvironmentTypes);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy