/* * Copyright 2019 MovingBlocks * * 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 org.terasology.launcher.packages; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonSyntaxException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.terasology.launcher.packages.db.DatabaseRepositoryDeserializer; import org.terasology.launcher.packages.db.DatabaseRepository; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.Comparator; import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.Optional; /** * Provides package details from all online and local repositories. */ class PackageDatabase { private static final Logger logger = LoggerFactory.getLogger(PackageDatabase.class); private final Path databaseFile; private final Path installDir; private final Gson gson; private final List<Package> database; PackageDatabase(Path databaseFile, Path installDir) { this.databaseFile = databaseFile; this.installDir = installDir; gson = new GsonBuilder() .registerTypeAdapter(DatabaseRepository.class, new DatabaseRepositoryDeserializer()) .create(); database = loadDatabase(); markInstalled(); } /** * Fetches details of all packages for each repository specified in {@code sourcesFile}. * * @param sourcesFile */ void sync(Path sourcesFile) { try (BufferedReader reader = new BufferedReader( new InputStreamReader(Files.newInputStream(sourcesFile)) )) { final List<Package> newDatabase = new LinkedList<>(); // TODO: read the DatabaseRepository list before-hand and pass to sync() try { for (DatabaseRepository source : gson.fromJson(reader, DatabaseRepository[].class)) { logger.trace("Fetching package list from: {}", source.getUrl()); newDatabase.addAll(packageListOf(source)); } } catch (IllegalStateException e) { logger.error("Could not read repositories from '%s'", sourcesFile, e); } database.clear(); database.addAll(newDatabase); } catch (IOException | JsonSyntaxException e) { logger.error("Failed to read sources file '{}': {}", sourcesFile, e.getMessage()); logger.warn("Aborting database synchronisation"); } finally { markInstalled(); saveDatabase(); } } /** * Scans the installation directory and marks the * detected packages as installed. */ private void markInstalled() { if (Files.exists(installDir)) { for (File pkgDir : Objects.requireNonNull(installDir.toFile().listFiles())) { for (File versionDir : Objects.requireNonNull(pkgDir.listFiles())) { database.stream() .filter(pkg -> pkg.getId().equals(pkgDir.getName()) && pkg.getVersion().equals(versionDir.getName())) .findFirst() .ifPresent(pkg -> pkg.setInstalled(true)); } } } } private List<Package> packageListOf(DatabaseRepository source) { return Objects.requireNonNull(RepositoryHandler.ofType(source.getType()), "Invalid repository type") .getPackageList(source); } private List<Package> loadDatabase() { if (Files.exists(databaseFile)) { try (ObjectInputStream in = new ObjectInputStream( Files.newInputStream(databaseFile) )) { return (List<Package>) in.readObject(); } catch (IOException | ClassNotFoundException e) { logger.error("Failed to load database file: {}", databaseFile); } } logger.info("Using empty database"); return new LinkedList<>(); } private void saveDatabase() { try (ObjectOutputStream out = new ObjectOutputStream( Files.newOutputStream(databaseFile) )) { out.writeObject(database); logger.info("Saved database file: {}", databaseFile); } catch (IOException e) { logger.error("Failed to write database file: {}", databaseFile); } } List<Package> getPackages() { return Collections.unmodifiableList(database); } Optional<Package> getLatestInstalledPackageForId(String packageId) { return database.stream() .filter(pkg -> pkg.getId().equals(packageId) && pkg.isInstalled()) .sorted(Comparator.comparing(Package::getVersion).reversed()) .findFirst(); } }