package org.jtheque.updates.impl; /* * Copyright JTheque (Baptiste Wicht) * * 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. */ import org.jtheque.core.Core; import org.jtheque.core.Folders; import org.jtheque.core.Versionable; import org.jtheque.core.utils.OSGiUtils; import org.jtheque.core.utils.WebHelper; import org.jtheque.modules.Module; import org.jtheque.modules.ModuleState; import org.jtheque.resources.ResourceService; import org.jtheque.ui.UIUtils; import org.jtheque.updates.InstallationResult; import org.jtheque.updates.UpdateService; import org.jtheque.utils.StringUtils; import org.jtheque.utils.annotations.GuardedBy; import org.jtheque.utils.annotations.ThreadSafe; import org.jtheque.utils.bean.Version; import org.jtheque.utils.collections.ArrayUtils; import org.jtheque.utils.collections.CollectionUtils; import org.jtheque.utils.io.FileException; import org.jtheque.utils.io.FileUtils; import org.jtheque.utils.io.WebUtils; import org.slf4j.LoggerFactory; import javax.annotation.Resource; import java.io.File; import java.util.Collection; import java.util.List; import java.util.Set; /** * Manage the update of the application and its module. This class can go on internet to verify if a more recent version * of something is available and download one new version if there is one. * * @author Baptiste Wicht */ @ThreadSafe public final class UpdateServiceImpl implements UpdateService { @Resource private Core core; @Resource private UIUtils uiUtils; @Resource private ResourceService resourceService; @Resource private WebHelper webHelper; @GuardedBy("this") private final DescriptorsLoader descriptorsLoader = new DescriptorsLoader(); @Override public void updateCore() { if (webHelper.isReachable(Core.DESCRIPTOR_FILE_URL)) { synchronized (this) { CoreVersion onlineVersion = descriptorsLoader.getCoreVersion(getMostRecentCoreVersion()); if (onlineVersion == null || onlineVersion.getVersion().equals(Core.VERSION)) { return; } applyCoreVersion(onlineVersion); } } } @Override public InstallationResult installModule(String url) { if (webHelper.isReachable(url)) { synchronized (this) { ModuleVersion moduleVersion = descriptorsLoader.getMostRecentModuleVersion(url); applyModuleVersion(moduleVersion); return new SimpleInstallationResult(true, moduleVersion.getModuleFile()); } } return new SimpleInstallationResult(false, ""); } @Override public void update(Module module) { if (webHelper.isReachable(module.getDescriptorURL())) { if (module.getState() == ModuleState.STARTED) { throw new IllegalArgumentException("The module must be stopped"); } synchronized (this) { update(module, getMostRecentVersion(module)); } } } @Override public Version getMostRecentCoreVersion() { if (webHelper.isReachable(Core.DESCRIPTOR_FILE_URL)) { synchronized (this) { return descriptorsLoader.getMostRecentCoreVersion(); } } return null; } @Override public Version getMostRecentVersion(Versionable object) { if(StringUtils.isNotEmpty(object.getDescriptorURL())){ if (webHelper.isReachable(object.getDescriptorURL())) { synchronized (this) { return descriptorsLoader.getMostRecentVersion(object); } } } return null; } /** * Update the module. * * @param module The module to update. * @param version The current version. */ private void update(Module module, Version version) { ModuleVersion onlineVersion = descriptorsLoader.getModuleVersion(version, module); if (onlineVersion == null) { return; } if (onlineVersion.getCoreVersion().isGreaterThan(Core.VERSION)) { uiUtils.displayI18nText("modules.message.version.problem"); } else { applyModuleVersion(onlineVersion); OSGiUtils.update(module.getBundle(), new File(Folders.getModulesFolder(), onlineVersion.getModuleFile())); uiUtils.displayI18nText("message.application.updated"); } } /** * Apply the module version. * * @param moduleVersion The module version to apply. */ private void applyModuleVersion(ModuleVersion moduleVersion) { try { if (StringUtils.isNotEmpty(moduleVersion.getModuleFile())) { WebUtils.downloadFile(moduleVersion.getModuleURL(), new File(Folders.getModulesFolder(), moduleVersion.getModuleFile()).getAbsolutePath()); } for (FileDescriptor resource : moduleVersion.getResources()) { resourceService.getOrDownloadResource(resource.getId(), resource.getVersion(), resource.getUrl()); } } catch (FileException e) { LoggerFactory.getLogger(getClass()).error(e.getMessage(), e); } } /** * Apply the given core version. * * @param coreVersion The core version to apply. */ private void applyCoreVersion(CoreVersion coreVersion) { LoggerFactory.getLogger(getClass()).debug("Apply core version {}", coreVersion.getVersion()); File bundlesFolder = new File(Folders.getCoreFolder(), "bundles"); Set<File> currentBundles = ArrayUtils.asSet(bundlesFolder.listFiles()); //Make the diffs and download the new bundles for (FileDescriptor newBundle : coreVersion.getBundles()) { File f = new File(bundlesFolder, newBundle.getId()); if (currentBundles.contains(f)) { currentBundles.remove(f); } LoggerFactory.getLogger(getClass()).debug("Download bundle {}", newBundle.getId()); try { WebUtils.downloadFile(newBundle.getUrl(), f.getAbsolutePath()); } catch (FileException e) { LoggerFactory.getLogger(getClass()).error(e.getMessage(), e); } } //Delete the remaining files for (File f : currentBundles) { LoggerFactory.getLogger(getClass()).debug("Delete bundle {}", f.getAbsolutePath()); FileUtils.delete(f); } } @Override public List<String> getPossibleUpdates(Iterable<? extends Module> modules) { if (WebUtils.isInternetNotReachable()) { return CollectionUtils.emptyList(); } List<String> messages = CollectionUtils.newList(2); if (isAModuleNotUpToDate(modules)) { messages.add("dialogs.propose.module.update"); } if (!isCurrentVersionUpToDate()) { messages.add("dialogs.propose.update"); } return messages; } /** * Indicate if all modules are up to date. * * @param modules The modules to test for possible updates. * * @return true if all modules are up to date else false. */ private boolean isAModuleNotUpToDate(Iterable<? extends Module> modules) { for (Module module : modules) { if (!isUpToDate(module)) { return true; } } return false; } @Override public boolean isCurrentVersionUpToDate() { if (webHelper.isReachable(Core.DESCRIPTOR_FILE_URL)) { Collection<Version> versions; synchronized (this) { versions = descriptorsLoader.getCoreVersions(); } return isUpToDate(Core.VERSION, versions); } return true; } @Override public boolean isUpToDate(Module object) { if (webHelper.isReachable(object.getDescriptorURL())) { Collection<Version> versions; synchronized (this) { versions = descriptorsLoader.getVersions(object); } return isUpToDate(object.getVersion(), versions); } return true; } /** * Indicate if the given version is up to date with the available versions. * * @param version The version to test. * @param versions The available versions. * * @return true if the version is up to date else false. */ private static boolean isUpToDate(Version version, Iterable<Version> versions) { for (Version v : versions) { if (v.isGreaterThan(version)) { return false; } } return true; } }