| /******************************************************************************* |
| * Copyright (C) 2013 The University of Manchester |
| * |
| * Modifications to the initial code base are copyright of their |
| * respective authors, or their employers as appropriate. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public License |
| * as published by the Free Software Foundation; either version 2.1 of |
| * the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
| ******************************************************************************/ |
| package uk.org.taverna.commons.update.impl; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.net.MalformedURLException; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.net.URL; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.xml.bind.JAXBContext; |
| import javax.xml.bind.JAXBException; |
| import javax.xml.bind.Unmarshaller; |
| |
| import org.apache.commons.io.FileUtils; |
| import org.osgi.service.event.EventAdmin; |
| |
| import uk.org.taverna.commons.download.DownloadException; |
| import uk.org.taverna.commons.download.DownloadManager; |
| import uk.org.taverna.commons.profile.xml.jaxb.ApplicationProfile; |
| import uk.org.taverna.commons.profile.xml.jaxb.BundleInfo; |
| import uk.org.taverna.commons.profile.xml.jaxb.UpdateSite; |
| import uk.org.taverna.commons.profile.xml.jaxb.Updates; |
| import uk.org.taverna.commons.update.UpdateException; |
| import uk.org.taverna.commons.update.UpdateManager; |
| import uk.org.taverna.commons.versions.xml.jaxb.Version; |
| import uk.org.taverna.commons.versions.xml.jaxb.Versions; |
| import uk.org.taverna.configuration.app.ApplicationConfiguration; |
| |
| /** |
| * Implementation of the Taverna Update Manager. |
| * |
| * @author David Withers |
| */ |
| public class UpdateManagerImpl implements UpdateManager { |
| |
| private static final String DIGEST_ALGORITHM = "MD5"; |
| |
| private EventAdmin eventAdmin; |
| |
| private int checkIntervalSeconds; |
| |
| private ApplicationConfiguration applicationConfiguration; |
| |
| private DownloadManager downloadManager; |
| |
| private long lastCheckTime; |
| private boolean updateAvailable; |
| private Unmarshaller unmarshaller; |
| |
| private Versions applicationVersions; |
| private Version latestVersion; |
| |
| public UpdateManagerImpl() throws UpdateException { |
| try { |
| JAXBContext jaxbContext = JAXBContext.newInstance(UpdateSite.class, ApplicationProfile.class); |
| unmarshaller = jaxbContext.createUnmarshaller(); |
| } catch (JAXBException e) { |
| throw new UpdateException("Error creating JAXBContext", e); |
| } |
| } |
| |
| @Override |
| public boolean checkForUpdates() throws UpdateException { |
| ApplicationProfile applicationProfile = applicationConfiguration.getApplicationProfile(); |
| String version = applicationProfile.getVersion(); |
| Updates updates = applicationProfile.getUpdates(); |
| |
| URL updatesURL; |
| try { |
| URI updateSiteURI = new URI(updates.getUpdateSite()); |
| updatesURL = updateSiteURI.resolve(updates.getUpdatesFile()).toURL(); |
| } catch (MalformedURLException e) { |
| throw new UpdateException(String.format("Update site URL (%s) is not a valid URL", |
| updates.getUpdateSite()), e); |
| } catch (URISyntaxException e) { |
| throw new UpdateException(String.format("Update site URL (%s) is not a valid URL", |
| updates.getUpdateSite()), e); |
| } |
| File updateDirectory = new File(applicationConfiguration.getApplicationHomeDir(), "updates"); |
| updateDirectory.mkdirs(); |
| File updatesFile = new File(updateDirectory, updates.getUpdatesFile()); |
| try { |
| downloadManager.download(updatesURL, updatesFile, DIGEST_ALGORITHM); |
| } catch (DownloadException e) { |
| throw new UpdateException(String.format("Error downloading %1$s", |
| updatesURL), e); |
| } |
| |
| try { |
| UpdateSite updateSite = (UpdateSite) unmarshaller |
| .unmarshal(updatesFile); |
| applicationVersions = updateSite.getVersions(); |
| latestVersion = applicationVersions.getLatestVersion(); |
| updateAvailable = isHigherVersion(latestVersion.getVersion(), version); |
| } catch (JAXBException e) { |
| throw new UpdateException(String.format("Error reading %s", |
| updatesFile.getName()), e); |
| } |
| lastCheckTime = System.currentTimeMillis(); |
| return updateAvailable; |
| } |
| |
| @Override |
| public boolean update() throws UpdateException { |
| if (updateAvailable) { |
| ApplicationProfile applicationProfile = applicationConfiguration.getApplicationProfile(); |
| Updates updates = applicationProfile.getUpdates(); |
| URL profileURL; |
| try { |
| URI updateSiteURI = new URI(updates.getUpdateSite()); |
| profileURL = updateSiteURI.resolve(latestVersion.getFile()).toURL(); |
| } catch (MalformedURLException e) { |
| throw new UpdateException(String.format( |
| "Application profile URL (%s) is not a valid URL", |
| latestVersion.getFile()), e); |
| } catch (URISyntaxException e) { |
| throw new UpdateException(String.format("Update site URL (%s) is not a valid URL", |
| updates.getUpdateSite()), e); |
| } |
| |
| File updateDirectory = new File(applicationConfiguration.getApplicationHomeDir(), |
| "updates"); |
| updateDirectory.mkdirs(); |
| File latestProfileFile = new File(updateDirectory, "ApplicationProfile-" |
| + latestVersion.getVersion() + ".xml"); |
| try { |
| downloadManager.download(profileURL, latestProfileFile, DIGEST_ALGORITHM); |
| } catch (DownloadException e) { |
| throw new UpdateException(String.format("Error downloading %1$s", |
| profileURL), e); |
| } |
| |
| ApplicationProfile latestProfile; |
| try { |
| latestProfile = (ApplicationProfile) unmarshaller.unmarshal(latestProfileFile); |
| } catch (JAXBException e) { |
| throw new UpdateException(String.format("Error reading %s", |
| latestProfileFile.getName()), e); |
| } |
| |
| Set<BundleInfo> requiredBundles = getRequiredBundles( |
| applicationConfiguration.getApplicationProfile(), latestProfile); |
| downloadBundles(latestProfile, requiredBundles, new File(applicationConfiguration.getStartupDir(), "lib")); |
| File applicationProfileFile = new File(applicationConfiguration.getStartupDir(), "ApplicationProfile.xml"); |
| try { |
| FileUtils.copyFile(latestProfileFile, applicationProfileFile); |
| } catch (IOException e) { |
| throw new UpdateException(String.format("Error copying %1$s to %2$s", |
| latestProfileFile.getName(), applicationProfileFile.getName()), e); |
| } |
| // eventAdmin.postEvent(new Event("UpdateManagerEvent", new HashMap())); |
| updateAvailable = false; |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * @param requiredBundles |
| * @param file |
| * @throws UpdateException |
| */ |
| private void downloadBundles(ApplicationProfile profile, Set<BundleInfo> requiredBundles, File file) throws UpdateException { |
| Updates updates = profile.getUpdates(); |
| String updateSite = updates.getUpdateSite(); |
| String libDirectory = updates.getLibDirectory(); |
| if (!libDirectory.endsWith("/")) { |
| libDirectory = libDirectory + "/"; |
| } |
| |
| URI updateLibDirectory; |
| try { |
| updateLibDirectory = new URI(updateSite).resolve(libDirectory); |
| } catch (URISyntaxException e) { |
| throw new UpdateException(String.format("Update site URL (%s) is not a valid URL", |
| updates.getUpdateSite()), e); |
| } |
| for (BundleInfo bundle : requiredBundles) { |
| URL bundleURL; |
| URI bundleURI = updateLibDirectory.resolve(bundle.getFileName()); |
| try { |
| bundleURL = bundleURI.toURL(); |
| } catch (MalformedURLException e) { |
| throw new UpdateException(String.format("Bundle URL (%s) is not a valid URL", |
| bundleURI), e); |
| } |
| File bundleDestination = new File(file, bundle.getFileName()); |
| try { |
| downloadManager.download(bundleURL, new File(file, bundle.getFileName()), DIGEST_ALGORITHM); |
| } catch (DownloadException e) { |
| throw new UpdateException(String.format("Error downloading %1$s to %2$s", |
| bundleURL, bundleDestination), e); |
| } |
| } |
| } |
| |
| /** |
| * Returns the new bundles required for the new application profile. |
| * |
| * @param currentProfile |
| * @param newProfile |
| * @return the new bundles required for the new application profile |
| */ |
| private Set<BundleInfo> getRequiredBundles(ApplicationProfile currentProfile, |
| ApplicationProfile newProfile) { |
| Set<BundleInfo> requiredBundles = new HashSet<BundleInfo>(); |
| Map<String, BundleInfo> currentBundles = new HashMap<String, BundleInfo>(); |
| for (BundleInfo bundle : currentProfile.getBundle()) { |
| currentBundles.put(bundle.getSymbolicName(), bundle); |
| } |
| for (BundleInfo bundle : newProfile.getBundle()) { |
| if (currentBundles.containsKey(bundle.getSymbolicName())) { |
| BundleInfo currentBundle = currentBundles.get(bundle.getSymbolicName()); |
| if (!bundle.getVersion().equals(currentBundle.getVersion())) { |
| requiredBundles.add(bundle); |
| } |
| } else { |
| requiredBundles.add(bundle); |
| } |
| } |
| return requiredBundles; |
| } |
| |
| private boolean isHigherVersion(String version1, String version2) { |
| org.osgi.framework.Version semanticVersion1 = org.osgi.framework.Version.parseVersion(version1); |
| org.osgi.framework.Version semanticVersion2 = org.osgi.framework.Version.parseVersion(version2); |
| return semanticVersion1.compareTo(semanticVersion2) > 0; |
| } |
| |
| public void setEventAdmin(EventAdmin eventAdmin) { |
| this.eventAdmin = eventAdmin; |
| } |
| |
| public void setCheckIntervalSeconds(int checkIntervalSeconds) { |
| this.checkIntervalSeconds = checkIntervalSeconds; |
| } |
| |
| public void setApplicationConfiguration(ApplicationConfiguration applicationConfiguration) { |
| this.applicationConfiguration = applicationConfiguration; |
| } |
| |
| public void setDownloadManager(DownloadManager downloadManager) { |
| this.downloadManager = downloadManager; |
| } |
| |
| } |