/* | |
* Copyright (C) 2012 The Android Open Source Project | |
* | |
* 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 com.motorola.studio.android.installer.utilities; | |
import java.net.URI; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.Iterator; | |
import java.util.List; | |
import org.eclipse.core.runtime.IProgressMonitor; | |
import org.eclipse.core.runtime.IStatus; | |
import org.eclipse.core.runtime.Status; | |
import org.eclipse.core.runtime.SubMonitor; | |
import org.eclipse.equinox.p2.metadata.IInstallableUnit; | |
import org.eclipse.equinox.p2.operations.InstallOperation; | |
import org.eclipse.equinox.p2.operations.Update; | |
import org.eclipse.equinox.p2.operations.UpdateOperation; | |
import org.eclipse.equinox.p2.query.IQuery; | |
import org.eclipse.equinox.p2.query.QueryUtil; | |
import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository; | |
import com.motorola.studio.android.common.log.StudioLogger; | |
import com.motorola.studio.android.installer.InstallerException; | |
import com.motorola.studio.android.installer.InstallerPlugin; | |
import com.motorola.studio.android.installer.i18n.InstallerNLS; | |
import com.motorola.studio.android.installer.utilities.IInstallManager.BACKEND; | |
import com.motorola.studio.android.installer.utilities.IInstallManager.CATEGORY; | |
/** | |
* Implements the methods for using the INstall framework with P2 | |
*/ | |
class P2Installer | |
{ | |
private final String LANGUAGE_PACK_QUERY = | |
"this.id ~= /com.motorola.studio.android.feature.nl*/"; | |
private final String NKD_QUERY = "org.eclipse.sequoyah.android.cdt.feature.feature.group"; | |
private final String SUBVERSION_QUERY = "org.eclipse.team.svn.feature.group"; | |
private final String EGIT_QUERY = "org.eclipse.egit.feature.group"; | |
private final String MYLYN_QUERY1 = "org.eclipse.mylyn.context_feature.feature.group"; | |
private final String MYLYN_QUERY2 = "org.eclipse.mylyn_feature.feature.group"; | |
private final String CVS_QUERY = "org.eclipse.cvs.feature.group"; | |
private final HashMap<URI, IMetadataRepository> mainRepositories = | |
new HashMap<URI, IMetadataRepository>(); | |
//will be used to record an instance of a InstallOperation. If it did not exist, we would have to instantiate | |
//the InstallOperation on validate and download methods | |
private InstallOperation installOp; | |
private UpdateOperation up; | |
/** | |
* | |
*/ | |
public void resetP2Installer() | |
{ | |
mainRepositories.clear(); | |
installOp = null; | |
} | |
private boolean isAllRepositoriesLoaded(Collection<URI> links) | |
{ | |
boolean isAllLoaded = false; | |
if (!mainRepositories.isEmpty()) | |
{ | |
isAllLoaded = true; | |
for (URI uri : links) | |
{ | |
if (!mainRepositories.containsKey(uri)) | |
{ | |
isAllLoaded = false; | |
break; | |
} | |
} | |
} | |
return isAllLoaded; | |
} | |
/** | |
* Load the P2 repositories. | |
* Initially the method verifies if the repositories were already instantiated and is | |
* on the metadata and arfacts maps (metadataRepositoriesMap, artifactRepositoriesMap). | |
* If yes, they should not be instantiated again. Otherwise, the repository and its references | |
* will be loaded. | |
* | |
* If the global maps did not exist, every time that methods validateInstallation, listAvailableUpdates | |
* and downloadAndInstall were called, all the repositories would be loaded again. | |
*/ | |
private IStatus loadRepositories(List<URI> links, IProgressMonitor monitor) | |
{ | |
StudioLogger.debug(this, "loading repositories..."); | |
IStatus status = Status.OK_STATUS; | |
StudioLogger.debug(this, "there are not loaded repositories"); | |
SubMonitor submonitor = SubMonitor.convert(monitor); | |
int reposSize = 0; | |
if (links != null) | |
{ | |
reposSize += links.size(); | |
} | |
submonitor.beginTask(InstallerNLS.AbstractConfigurationPage_LoadingRepositoriesTask, | |
reposSize * 100); | |
boolean isAllRepositoriesLoaded = isAllRepositoriesLoaded(links); | |
if (!isAllRepositoriesLoaded) | |
{ | |
P2RepositoriesFactory p2RepositoriesFactory = P2RepositoriesFactory.getInstance(); | |
for (URI uri : links) | |
{ | |
IMetadataRepository metadataRepository = null; | |
//loads metadata repositories from URIs | |
try | |
{ | |
metadataRepository = | |
p2RepositoriesFactory.getMetadataRepository(uri, true, | |
submonitor.newChild(100)); | |
mainRepositories.put(uri, metadataRepository); | |
} | |
catch (Exception e) | |
{ | |
status = new Status(IStatus.WARNING, InstallerPlugin.PLUGIN_ID, e.getMessage()); | |
StudioLogger.error(this.getClass(), | |
"could not instantiate repository from URI " + uri); | |
} | |
} | |
} | |
else | |
{ | |
submonitor.done(); | |
} | |
return status; | |
} | |
/** | |
* Loads InstallableItems based on category and links. They are put into the collection listToFill | |
* | |
* @param listToFill | |
* @param links | |
* @param category | |
* @param monitor | |
* @return | |
* @throws InstallerException | |
*/ | |
public IStatus listAllAvailableInstallItems(Collection<InstallableItem> listToFill, | |
List<URI> uriList, CATEGORY category, IProgressMonitor monitor) | |
throws InstallerException | |
{ | |
StudioLogger.debug(this, "listing available installable items..."); | |
IStatus status = | |
new Status(IStatus.ERROR, InstallerPlugin.PLUGIN_ID, 0, | |
InstallerNLS.P2Installer_Could_Not_Find_Proper_Backend, null); | |
//this links list is created because the method loadRepositories remove some items of the list | |
//if the repositorie is already loaded. | |
List<URI> links = new ArrayList<URI>(); | |
links.addAll(uriList); | |
SubMonitor submonitor = SubMonitor.convert(monitor); | |
submonitor.beginTask(InstallerNLS.P2Installer_Loading_Repositories, 100); | |
status = loadRepositories(links, submonitor.newChild(20)); | |
Collection<IInstallableUnit> units = new HashSet<IInstallableUnit>(); | |
IQuery<IInstallableUnit> query = null; | |
//category is used to create the correct query | |
switch (category) | |
{ | |
case LANG_PACKS: | |
{ | |
//Filter IUs in order to receive only lang packs | |
query = QueryUtil.createMatchQuery(LANGUAGE_PACK_QUERY); //$NON-NLS-1$ | |
break; | |
} | |
case NDK: | |
{ | |
//Filter IUs in order to receive only NDK related | |
query = QueryUtil.createIUQuery(NKD_QUERY); //$NON-NLS-1$ | |
break; | |
} | |
case UPDATE_STUDIO: | |
{ | |
//No special query needed | |
break; | |
} | |
case OTHER_COMPONENTS: | |
{ | |
Collection<IQuery<IInstallableUnit>> queries = | |
new ArrayList<IQuery<IInstallableUnit>>(); | |
queries.add(QueryUtil.createIUQuery(SUBVERSION_QUERY)); | |
queries.add(QueryUtil.createIUQuery(MYLYN_QUERY1)); | |
queries.add(QueryUtil.createIUQuery(MYLYN_QUERY2)); | |
queries.add(QueryUtil.createIUQuery(CVS_QUERY)); | |
queries.add(QueryUtil.createIUQuery(EGIT_QUERY)); | |
query = QueryUtil.createCompoundQuery(queries, false); | |
break; | |
} | |
default: | |
{ | |
// No specific query to use as filter, download them all! | |
break; | |
} | |
} | |
int monitorWorkSize = 0; | |
try | |
{ | |
monitorWorkSize = 40 / mainRepositories.values().size(); | |
} | |
catch (ArithmeticException e) | |
{ | |
// Do nothing | |
} | |
for (Iterator<IMetadataRepository> iterator = mainRepositories.values().iterator(); iterator | |
.hasNext();) | |
{ | |
IMetadataRepository repository = iterator.next(); | |
try | |
{ | |
Collection<IInstallableUnit> ius = | |
P2Utilities.getInstallableUnits(repository, query, | |
submonitor.newChild(monitorWorkSize)); | |
units.addAll(ius); | |
status = Status.OK_STATUS; | |
} | |
catch (InstallerException e) | |
{ | |
StudioLogger.error(this.getClass(), "could not retrieve installable units"); | |
status = | |
new Status(IStatus.ERROR, InstallerPlugin.PLUGIN_ID, 0, | |
"Error retrieving available installable units", null); | |
} | |
} | |
monitorWorkSize = 0; | |
try | |
{ | |
monitorWorkSize = 40 / units.size(); | |
} | |
catch (ArithmeticException e) | |
{ | |
// Do nothing | |
} | |
for (Iterator<IInstallableUnit> iterator = units.iterator(); iterator.hasNext();) | |
{ | |
IInstallableUnit iInstallableUnit = iterator.next(); | |
if (P2Utilities.isGroup(iInstallableUnit)) | |
{ | |
// I'm only returning groups since only groups | |
// are listed for the user to select what to install | |
InstallableItem item = iu2InstallableItem(iInstallableUnit, monitor); | |
listToFill.add(item); | |
} | |
} | |
if (status.getMessage().equals("org.eclipse.core.runtime.OperationCanceledException")) | |
{ | |
StudioLogger.debug(this, "operation was canceled"); | |
status = | |
new Status(Status.CANCEL, status.getPlugin(), status.getCode(), | |
status.getMessage(), status.getException()); | |
} | |
submonitor.done(); | |
return status; | |
} | |
//Translates a P2 installable unit to a InstallableItem object | |
private InstallableItem iu2InstallableItem(IInstallableUnit unit, IProgressMonitor monitor) | |
throws InstallerException | |
{ | |
InstallableItem item = new P2InstallableItem(); | |
item.setData(unit); | |
item.setBundleID(unit.getId()); | |
item.setInstalled(P2Utilities.isInstalled(unit, monitor)); | |
item.setLicense(P2Utilities.getLicenseText(unit)); | |
item.setDisplayName(P2Utilities.getIUExternalizedValue(unit, IInstallableUnit.PROP_NAME)); | |
item.setDescription(P2Utilities.getIUExternalizedValue(unit, | |
IInstallableUnit.PROP_DESCRIPTION)); | |
item.setProvider(P2Utilities.getIUExternalizedValue(unit, IInstallableUnit.PROP_PROVIDER)); | |
item.setRequirementsIds(P2Utilities.getRequirements(unit)); | |
return item; | |
} | |
/** | |
* Method used to install the installable items. The repositories and installOp should be already loaded, | |
* otherwise they will be. | |
* | |
* @param links | |
* @param itemsToDownloadAndInstall | |
* @param monitor | |
* @return | |
*/ | |
public IStatus downloadAndInstall(List<URI> links, | |
Collection<InstallableItem> itemsToDownloadAndInstall, IProgressMonitor monitor) | |
{ | |
StudioLogger.debug(this, "downloadAndInstall: installing selected installable items"); | |
IStatus status = | |
new Status(IStatus.ERROR, InstallerPlugin.PLUGIN_ID, 0, | |
InstallerNLS.P2Installer_Could_Not_Install_Selected_Items, null); | |
if ((itemsToDownloadAndInstall != null) && (!itemsToDownloadAndInstall.isEmpty())) | |
{ | |
final List<IInstallableUnit> installableUnits = new ArrayList<IInstallableUnit>(); | |
for (InstallableItem item : itemsToDownloadAndInstall) | |
{ | |
IInstallableUnit unit = (IInstallableUnit) item.getData(); | |
installableUnits.add(unit); | |
} | |
try | |
{ | |
if (installOp == null) | |
{ | |
status = loadRepositories(links, monitor); | |
status = | |
P2Utilities.installIu(installableUnits, mainRepositories.values(), | |
monitor); | |
} | |
else | |
{ | |
status = P2Utilities.installIu(installableUnits, installOp, monitor); | |
} | |
} | |
catch (InstallerException e) | |
{ | |
StudioLogger.error(this.getClass(), "could not install selected installable unit"); | |
status = new Status(IStatus.WARNING, InstallerPlugin.PLUGIN_ID, e.getMessage()); | |
} | |
//clean installOp and maps. After the installation has occurred, they can be cleaned. | |
installOp = null; | |
mainRepositories.clear(); | |
return status; | |
} | |
return status; | |
} | |
/** | |
* Updates studio. | |
* IMPORTANT: the method listAllAvailableUpdates MUST be called first | |
* | |
* @param monitor | |
* @return | |
*/ | |
public IStatus updateStudio(IProgressMonitor monitor) | |
{ | |
StudioLogger.debug(this, "updateStudio: installing selected installable items"); | |
IStatus status = | |
new Status(IStatus.ERROR, InstallerPlugin.PLUGIN_ID, 0, | |
InstallerNLS.P2Installer_Could_Not_Install_Selected_Items, null); | |
try | |
{ | |
status = P2Utilities.updateIu(up, monitor); | |
} | |
catch (Exception e) | |
{ | |
StudioLogger.error(this.getClass(), "could not install selected installable unit.", e); | |
status = new Status(IStatus.WARNING, InstallerPlugin.PLUGIN_ID, e.getMessage()); | |
} | |
return status; | |
} | |
/** | |
* @param itemsToDownloadAndInstall | |
* @param backEnd | |
* @param monitor | |
* @return | |
*/ | |
public IStatus validateInstallation(List<URI> links, | |
Collection<InstallableItem> itemsToDownloadAndInstall, BACKEND backEnd, | |
IProgressMonitor monitor) | |
{ | |
// installOp will be loaded as global variable because will be used on method downloadAndInstall. | |
// It must not be instantiated twice if it were already validated. | |
installOp = null; | |
List<URI> allURIs = new ArrayList<URI>(mainRepositories.keySet()); | |
for (Iterator<URI> iterator = allURIs.iterator(); iterator.hasNext();) | |
{ | |
URI uri = iterator.next(); | |
if (!links.contains(uri)) | |
{ | |
mainRepositories.remove(uri); | |
} | |
} | |
Collection<IInstallableUnit> temp = new HashSet<IInstallableUnit>(); | |
loadRepositories(links, monitor); | |
for (Iterator<InstallableItem> iterator = itemsToDownloadAndInstall.iterator(); iterator | |
.hasNext();) | |
{ | |
InstallableItem iInstallableItem = iterator.next(); | |
temp.add((IInstallableUnit) iInstallableItem.getData()); | |
} | |
if ((itemsToDownloadAndInstall != null) && (itemsToDownloadAndInstall.size() > 0)) | |
{ | |
try | |
{ | |
installOp = | |
P2Utilities.getInstallOperation(temp, mainRepositories.values(), monitor); | |
} | |
catch (InstallerException e) | |
{ | |
StudioLogger.error(this.getClass(), "Could not retrieve install operation"); | |
return new Status(IStatus.ERROR, InstallerPlugin.PLUGIN_ID, 0, e.getMessage(), null); | |
} | |
} | |
return installOp != null ? installOp.getResolutionResult() : Status.CANCEL_STATUS; | |
} | |
/** | |
* Lists all available updates given a link. | |
* After this method the method updateStudio can be invoked | |
* | |
* @param listToFill | |
* @param links | |
* @param category | |
* @param backEnd | |
* @param monitor | |
* @return | |
* @throws InstallerException | |
*/ | |
public IStatus listAllAvailableUpdates(Collection<InstallableItem> listToFill, List<URI> links, | |
CATEGORY category, BACKEND backEnd, IProgressMonitor monitor) throws InstallerException | |
{ | |
IStatus result = Status.OK_STATUS; | |
try | |
{ | |
up = P2Utilities.getUpdateOperation(links, monitor); | |
if (up != null) | |
{ | |
result = up.getResolutionResult(); | |
Update[] updates = up.getSelectedUpdates(); | |
if (listToFill != null) | |
{ | |
for (int i = 0; i < updates.length; i++) | |
{ | |
InstallableItem item = iu2InstallableItem(updates[i].replacement, monitor); | |
listToFill.add(item); | |
} | |
} | |
} | |
else | |
{ | |
result = Status.CANCEL_STATUS; | |
} | |
} | |
catch (Exception e) | |
{ | |
StudioLogger.error(this.getClass(), "Error looking for updates. ", e); | |
result = new Status(IStatus.ERROR, InstallerPlugin.PLUGIN_ID, 0, e.getMessage(), null); | |
} | |
return result; | |
} | |
} |