package org.phoenicis.apps; import org.phoenicis.apps.dto.ApplicationDTO; import org.phoenicis.apps.dto.CategoryDTO; import org.phoenicis.apps.dto.ScriptDTO; import org.phoenicis.apps.repository.*; import org.phoenicis.tools.ToolsConfiguration; import org.phoenicis.tools.files.FileUtilities; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.function.Consumer; import java.util.stream.Collectors; /** * Created by marc on 31.03.17. */ public class DefaultRepositoryManager implements RepositoryManager { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultRepositoryManager.class); private final LocalRepository.Factory localRepositoryFactory; private final ClasspathRepository.Factory classPathRepositoryFactory; private final String cacheDirectoryPath; private final FileUtilities fileUtilities; private MultipleRepository multipleRepository; private FilterRepository filterRepository; private CachedRepository cachedRepository; private BackgroundRepository backgroundRepository; private List<CallbackPair> callbacks; public DefaultRepositoryManager(ExecutorService executorService, boolean enforceUncompatibleOperatingSystems, ToolsConfiguration toolsConfiguration, String cacheDirectoryPath, FileUtilities fileUtilities, LocalRepository.Factory localRepositoryFactory, ClasspathRepository.Factory classPathRepositoryFactory) { super(); this.localRepositoryFactory = localRepositoryFactory; this.classPathRepositoryFactory = classPathRepositoryFactory; this.cacheDirectoryPath = cacheDirectoryPath; this.fileUtilities = fileUtilities; this.callbacks = new ArrayList<CallbackPair>(); this.multipleRepository = new MultipleRepository(); this.filterRepository = new FilterRepository(multipleRepository, toolsConfiguration.operatingSystemFetcher(), enforceUncompatibleOperatingSystems); this.cachedRepository = new CachedRepository(filterRepository); this.backgroundRepository = new BackgroundRepository(cachedRepository, executorService); } @Override public void addCallbacks(Consumer<List<CategoryDTO>> onRepositoryChange, Consumer<Exception> onError) { this.callbacks.add(new CallbackPair(onRepositoryChange, onError)); } @Override public ApplicationDTO getApplication(List<String> path) { return this.cachedRepository.getApplication(path); } @Override public ScriptDTO getScript(List<String> path) { return this.cachedRepository.getScript(path); } @Override public void moveRepository(String repositoryUrl, int toIndex) { LOGGER.info(String.format("Move repository: %s to %d", repositoryUrl, toIndex)); this.multipleRepository.moveRepository(toRepository(repositoryUrl), toIndex); this.triggerRepositoryChange(); } @Override public void addRepositories(int index, String... repositoryUrls) { LOGGER.info(String.format("Adding repositories: %s at index %d", Arrays.toString(repositoryUrls), index)); for (int repositoryUrlIndex = 0; repositoryUrlIndex < repositoryUrls.length; repositoryUrlIndex++) { Repository repository = toRepository(repositoryUrls[repositoryUrlIndex]); this.multipleRepository.addRepository(index + repositoryUrlIndex, repository); } this.triggerRepositoryChange(); } @Override public void addRepositories(String... repositoryUrls) { this.addRepositories(this.multipleRepository.size(), repositoryUrls); } @Override public void removeRepositories(String... repositoryUrls) { LOGGER.info(String.format("Removing repositories: %s", Arrays.toString(repositoryUrls))); List<Repository> toDeleteRepositories = Arrays.stream(repositoryUrls).map(this::toRepository) .collect(Collectors.toList()); toDeleteRepositories.forEach(this.multipleRepository::removeRepository); this.triggerRepositoryChange(); toDeleteRepositories.forEach(Repository::onDelete); } @Override public void triggerRepositoryChange() { this.cachedRepository.clearCache(); if (!this.callbacks.isEmpty()) { this.backgroundRepository.fetchInstallableApplications( categories -> this.callbacks .forEach(callbackPair -> callbackPair.getOnRepositoryChange().accept(categories)), exception -> this.callbacks.forEach(callbackPair -> callbackPair.getOnError().accept(exception))); } } private Repository toRepository(String repositoryUrl) { LOGGER.info("Converting: " + repositoryUrl + " to Repository"); try { final URI url = new URI(repositoryUrl); final String scheme = url.getScheme().split("\\+")[0]; switch (scheme) { case "git": return new GitRepository(repositoryUrl.replace("git+", ""), cacheDirectoryPath, localRepositoryFactory, fileUtilities); case "file": return localRepositoryFactory.createInstance(url.getRawPath()); case "classpath": return classPathRepositoryFactory.createInstance(url.getPath()); default: LOGGER.warn("Unsupported URL: " + repositoryUrl); return new NullRepository(); } } catch (URISyntaxException e) { LOGGER.warn("Cannot parse URL: " + repositoryUrl, e); return new NullRepository(); } } private class CallbackPair { private Consumer<List<CategoryDTO>> onRepositoryChange; private Consumer<Exception> onError; public CallbackPair(Consumer<List<CategoryDTO>> onRepositoryChange, Consumer<Exception> onError) { this.onRepositoryChange = onRepositoryChange; this.onError = onError; } public Consumer<List<CategoryDTO>> getOnRepositoryChange() { return this.onRepositoryChange; } public Consumer<Exception> getOnError() { return this.onError; } } }