blob: 58ffff473031705b1ee63e6b4a1a3428acaaf69c [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.taverna.update.impl;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path;
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.apache.taverna.configuration.app.ApplicationConfiguration;
import org.apache.taverna.download.DownloadException;
import org.apache.taverna.download.DownloadManager;
import org.apache.taverna.profile.xml.jaxb.ApplicationProfile;
import org.apache.taverna.profile.xml.jaxb.BundleInfo;
import org.apache.taverna.profile.xml.jaxb.UpdateSite;
import org.apache.taverna.profile.xml.jaxb.Updates;
import org.apache.taverna.update.UpdateException;
import org.apache.taverna.update.UpdateManager;
import org.apache.taverna.versions.xml.jaxb.Version;
import org.apache.taverna.versions.xml.jaxb.Versions;
import org.osgi.service.event.EventAdmin;
/**
* Implementation of the Taverna Update Manager.
*
*/
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();
URI updatesURL;
try {
URI updateSiteURI = new URI(updates.getUpdateSite());
updatesURL = updateSiteURI.resolve(updates.getUpdatesFile());
} catch (URISyntaxException e) {
throw new UpdateException(String.format("Update site URL (%s) is not a valid URL",
updates.getUpdateSite()), e);
}
File updateDirectory = applicationConfiguration.getApplicationHomeDir().resolve("updates").toFile();
updateDirectory.mkdirs();
File updatesFile = new File(updateDirectory, updates.getUpdatesFile());
try {
downloadManager.download(updatesURL, updatesFile.toPath(), 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();
URI profileURL;
try {
URI updateSiteURI = new URI(updates.getUpdateSite());
profileURL = updateSiteURI.resolve(latestVersion.getFile());
} catch (URISyntaxException e) {
throw new UpdateException(String.format("Update site URL (%s) is not a valid URL",
updates.getUpdateSite()), e);
}
File updateDirectory = applicationConfiguration.getApplicationHomeDir().resolve(
"updates").toFile();
updateDirectory.mkdirs();
File latestProfileFile = new File(updateDirectory, "ApplicationProfile-"
+ latestVersion.getVersion() + ".xml");
try {
downloadManager.download(profileURL, latestProfileFile.toPath(), 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, applicationConfiguration.getStartupDir().resolve("lib").toFile());
File applicationProfileFile = applicationConfiguration.getStartupDir().resolve("ApplicationProfile.xml").toFile();
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) {
URI bundleURI = updateLibDirectory.resolve(bundle.getFileName());
Path bundleDestination = new File(file, bundle.getFileName()).toPath();
try {
downloadManager.download(bundleURI, bundleDestination, DIGEST_ALGORITHM);
} catch (DownloadException e) {
throw new UpdateException(String.format("Error downloading %1$s to %2$s",
bundleURI, 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;
}
}