blob: 6dbd9011e2bee01e04815773d783f6fb47adcd90 [file] [log] [blame]
/*
* 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;
}
}