blob: d5a748194991d4a675618e00a37693b0f5bcaef0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2010 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.update.search;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.osgi.util.NLS;
import org.eclipse.update.core.IFeature;
import org.eclipse.update.core.IFeatureReference;
import org.eclipse.update.core.ISite;
import org.eclipse.update.core.ISiteWithMirrors;
import org.eclipse.update.core.IURLEntry;
import org.eclipse.update.core.SiteManager;
import org.eclipse.update.internal.core.ExtendedSite;
import org.eclipse.update.internal.core.Messages;
import org.eclipse.update.internal.operations.UpdateUtils;
import org.eclipse.update.internal.search.SiteSearchCategory;
import org.eclipse.update.internal.search.UpdatePolicy;
import org.eclipse.update.internal.search.UpdateSiteAdapter;
import org.eclipse.update.internal.search.UpdatesSearchCategory;
/**
* This class is central to update search. The search pattern
* is encapsulated in update search category, while the search
* scope is defined in the scope object. When these two objects
* are defined and set, search can be performed using the
* provided method. Search results are reported to the
* result collector, while search progress is tracked using
* the progress monitor.
* <p>Classes that implement <samp>IUpdateSearchResultCollector</samp>
* should call 'accept' to test if the match should be
* accepted according to the filters added to the request.
*
* <p>
* <b>Note:</b> This class/interface is part of an interim API that is still under development and expected to
* change significantly before reaching stability. It is being made available at this early stage to solicit feedback
* from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken
* (repeatedly) as the API evolves.
* </p>
* @see UpdateSearchScope
* @see IUpdateSearchCategory
* @since 3.0
* @deprecated The org.eclipse.update component has been replaced by Equinox p2.
* This API will be deleted in a future release. See bug 311590 for details.
*/
public class UpdateSearchRequest {
private IUpdateSearchCategory category;
private UpdateSearchScope scope;
private boolean searchInProgress = false;
private AggregateFilter aggregateFilter = new AggregateFilter();
private static class UpdateSearchSite
extends UpdateSiteAdapter
implements IUpdateSearchSite {
private String[] categoriesToSkip;
public UpdateSearchSite(
String label,
URL siteURL,
String[] categoriesToSkip) {
super(label, siteURL);
this.categoriesToSkip = categoriesToSkip;
}
public String[] getCategoriesToSkip() {
return categoriesToSkip;
}
}
class MirroredUpdateSiteAdapter extends UpdateSiteAdapter {
public MirroredUpdateSiteAdapter(IURLEntry mirror) {
super(mirror.getAnnotation(), mirror.getURL());
}
}
class AggregateFilter implements IUpdateSearchFilter {
private ArrayList filters;
public void addFilter(IUpdateSearchFilter filter) {
if (filters == null)
filters = new ArrayList();
if (filters.contains(filter) == false)
filters.add(filter);
}
public void removeFilter(IUpdateSearchFilter filter) {
if (filters == null)
return;
filters.remove(filter);
}
/**
* @deprecated In 3.1 only the accept (IFeatureReference) will be used
*/
public boolean accept(IFeature match) {
if (filters == null)
return true;
for (int i = 0; i < filters.size(); i++) {
IUpdateSearchFilter filter = (IUpdateSearchFilter) filters.get(i);
if (filter.accept(match) == false)
return false;
}
return true;
}
public boolean accept(IFeatureReference match) {
if (filters == null)
return true;
for (int i = 0; i < filters.size(); i++) {
IUpdateSearchFilter filter = (IUpdateSearchFilter) filters.get(i);
if (filter.accept(match) == false)
return false;
}
return true;
}
}
/**
* Returns an updates search category for use in discovering updates
* to existing function on update sites.
*
* @return an updates search category
* @since 3.1
*/
public static IUpdateSearchCategory createDefaultUpdatesSearchCategory() {
return new UpdatesSearchCategory();
}
/**
* Returns a site search category for use in discovering new function on update sites.
*
* @return a site search category
* @since 3.1
*/
public static IUpdateSearchCategory createDefaultSiteSearchCategory() {
return new SiteSearchCategory();
}
/**
* The constructor that accepts the search category and
* scope objects.
* @param category the actual search pattern that should be applied
* @param scope a list of sites that need to be scanned during the search
*/
public UpdateSearchRequest(
IUpdateSearchCategory category,
UpdateSearchScope scope) {
this.category = category;
this.scope = scope;
}
/**
* Returns the search catagory used in this request.
* @return the search category
*/
public IUpdateSearchCategory getCategory() {
return category;
}
/**
* Returns the scope of this search request.
* @return search scope
*/
public UpdateSearchScope getScope() {
return scope;
}
/**
* Adds a filter to this request. This method does nothing
* if search is alrady in progress.
* @param filter the filter
* @see UpdateSearchRequest#removeFilter
*/
public void addFilter(IUpdateSearchFilter filter) {
if (searchInProgress)
return;
aggregateFilter.addFilter(filter);
}
/**
* Removes the filter from this request. This method does
* nothing if search is alrady in progress.
* @param filter the filter to remove
* @see UpdateSearchRequest#addFilter
*/
public void removeFilter(IUpdateSearchFilter filter) {
if (searchInProgress)
return;
aggregateFilter.removeFilter(filter);
}
/**
* Sets the scope object. It is possible to reuse the search request
* object by modifying the scope and re-running the search.
* @param scope the new search scope
*/
public void setScope(UpdateSearchScope scope) {
this.scope = scope;
}
/**
* Tests whether this search request is current running.
* @return <samp>true</samp> if the search is currently running, <samp>false</samp> otherwise.
*/
public boolean isSearchInProgress() {
return searchInProgress;
}
/**
* Runs the search using the category and scope configured into
* this request. As results arrive, they are passed to the
* search result collector object.
* @param collector matched features are passed to this object
* @param monitor used to track the search progress
* @throws CoreException
*/
public void performSearch(
IUpdateSearchResultCollector collector,
IProgressMonitor monitor)
throws CoreException, OperationCanceledException {
ArrayList statusList = new ArrayList();
searchInProgress = true;
IUpdateSearchQuery[] queries = category.getQueries();
IUpdateSearchSite[] candidates = scope.getSearchSites();
Set visitedSitesURL = new HashSet();
Set visitedSites = new HashSet();
for(int i = 0; i < candidates.length; i++) {
visitedSitesURL.add(candidates[i].getURL());
//visitedSites.add(candidates[i]);
}
URL updateMapURL = scope.getUpdateMapURL();
boolean searchFeatureProvidedSites = scope.isFeatureProvidedSitesEnabled();
if (!monitor.isCanceled()) {
int nsearchsites = 0;
try {
for (int i = 0; i < queries.length; i++) {
if (queries[i].getQuerySearchSite() != null)
nsearchsites++;
}
} catch (Throwable t) {
t.printStackTrace();
}
int ntasks = nsearchsites + queries.length * candidates.length;
if (updateMapURL!=null) ntasks++;
monitor.beginTask(Messages.UpdateSearchRequest_searching, ntasks);
try {
UpdatePolicy updatePolicy=null;
if (updateMapURL!=null) {
updatePolicy = new UpdatePolicy();
IStatus status =UpdateUtils.loadUpdatePolicy(updatePolicy, updateMapURL, new SubProgressMonitor(monitor, 1));
if (status != null)
statusList.add(status);
}
List combinedAssociateSites = new ArrayList();
for (int i = 0; i < queries.length; i++) {
IUpdateSearchQuery query = queries[i];
IQueryUpdateSiteAdapter qsite = query.getQuerySearchSite();
// currently, the next conditional is only executed (qsite!=null) when
// running an update search.
if (qsite != null && searchFeatureProvidedSites) {
// do not update features that are installed in read-only locations
if (query instanceof UpdatesSearchCategory.UpdateQuery) {
IFeature feature = ((UpdatesSearchCategory.UpdateQuery)query).getFeature();
if (feature != null && !feature.getSite().getCurrentConfiguredSite().verifyUpdatableStatus().isOK())
continue;
}
// check for mapping
IUpdateSiteAdapter mappedSite = getMappedSite(updatePolicy, qsite);
// when there is no mapped site the feature is not updatable
if (mappedSite == null || mappedSite.getURL() == null)
continue;
SubProgressMonitor subMonitor =
new SubProgressMonitor(monitor, 1);
List associateSites = new ArrayList();
IStatus status =
searchOneSite(
mappedSite,
null,
query,
collector,
associateSites,
subMonitor,
true);
if (status != null)
statusList.add(status);
if (monitor.isCanceled())
break;
combinedAssociateSites = combineAssociateSites( combinedAssociateSites, associateSites, visitedSitesURL, visitedSites);
}
for (int j = 0; j < candidates.length; j++) {
if (monitor.isCanceled()) {
break;
}
IUpdateSearchSite source = candidates[j];
SubProgressMonitor subMonitor =
new SubProgressMonitor(monitor, 1);
List associateSites = new ArrayList();
IStatus status =
searchOneSite(
source,
source.getCategoriesToSkip(),
query,
collector,
associateSites,
subMonitor,
true);
if (status != null)
statusList.add(status);
combinedAssociateSites = combineAssociateSites( combinedAssociateSites, associateSites, visitedSitesURL, visitedSites);
}
if (monitor.isCanceled())
break;
for(int associateSitesDepth = 0; associateSitesDepth < 5; associateSitesDepth++) {
List tempCombinedSites = new ArrayList();
Iterator combinedAssociateSitesIterator = combinedAssociateSites.iterator();
while(combinedAssociateSitesIterator.hasNext()) {
IUpdateSearchSite source = (IUpdateSearchSite)combinedAssociateSitesIterator.next();
List associateSites = new ArrayList();
SubProgressMonitor subMonitor = new SubProgressMonitor(monitor, 1);
IStatus status =
searchOneSite(
source,
source.getCategoriesToSkip(),
query,
collector,
associateSites,
subMonitor,
true);
combinedAssociateSites = combineAssociateSites( tempCombinedSites, associateSites, visitedSitesURL, visitedSites);
if (status != null)
statusList.add(status);
}
combinedAssociateSites = tempCombinedSites;
}
if (monitor.isCanceled())
break;
}
} catch (CoreException e) {
searchInProgress = false;
monitor.done();
throw e;
}
}
searchInProgress = false;
monitor.done();
Iterator visitedSitesIterator = visitedSites.iterator();
while (visitedSitesIterator.hasNext()) {
IUpdateSearchSite associateSite = (IUpdateSearchSite)visitedSitesIterator.next();
scope.addSearchSite(associateSite.getLabel(), associateSite.getURL(), null);
}
if (statusList.size() > 0) {
if (statusList.size()==1 && ((IStatus)statusList.get(0)).getSeverity()==IStatus.CANCEL)
throw new OperationCanceledException();
IStatus[] children =
(IStatus[]) statusList.toArray(new IStatus[statusList.size()]);
MultiStatus multiStatus =
new MultiStatus(
"org.eclipse.update.core", //$NON-NLS-1$
ISite.SITE_ACCESS_EXCEPTION,
children,
Messages.Search_networkProblems,
null);
throw new CoreException(multiStatus);
}
}
private List combineAssociateSites(List combinedAssociateSites, List associateSites, Set visitedSitesURL, Set visitedSites) {
Iterator iterator = associateSites.iterator();
while(iterator.hasNext()) {
UpdateSearchSite associateSite = (UpdateSearchSite)iterator.next();
if ( !visitedSitesURL.contains(associateSite.getURL())) {
combinedAssociateSites.add(associateSite);
visitedSitesURL.add(associateSite.getURL());
visitedSites.add(associateSite);
}
}
return combinedAssociateSites;
}
/*
* See if this query site adapter is mapped in the map file
* to a different URL.
*/
private IUpdateSiteAdapter getMappedSite(UpdatePolicy policy, IQueryUpdateSiteAdapter qsite) {
if (policy!=null && policy.isLoaded()) {
IUpdateSiteAdapter mappedSite = policy.getMappedSite(qsite.getMappingId());
if (mappedSite!=null)
return mappedSite;
else // no match - use original site if fallback allowed, or nothing.
return policy.isFallbackAllowed()? qsite : null;
}
return qsite;
}
/*
* Search one site using the provided query.
*/
private IStatus searchOneSite(
IUpdateSiteAdapter siteAdapter,
String[] categoriesToSkip,
IUpdateSearchQuery query,
IUpdateSearchResultCollector collector,
List associateSites,
SubProgressMonitor monitor,
boolean checkMirrors)
throws CoreException {
String text = NLS.bind(Messages.UpdateSearchRequest_contacting, siteAdapter.getLabel());
monitor.subTask(text);
monitor.beginTask("", 10); //$NON-NLS-1$
URL siteURL = siteAdapter.getURL();
ISite site;
try {
site =
SiteManager.getSite(
siteURL,
new SubProgressMonitor(monitor, 1));
// If frozen connection was canceled, there will be no site.
if (site == null) {
monitor.worked(9);
return null;
}
// prompt the user to pick up a site (do not recursively go into mirror sites on the mirror site)
if ((collector instanceof IUpdateSearchResultCollectorFromMirror) &&
(site instanceof ISiteWithMirrors) &&
!(siteAdapter instanceof MirroredUpdateSiteAdapter)) {
IURLEntry mirror = null;
try {
mirror = ((IUpdateSearchResultCollectorFromMirror)collector).getMirror((ISiteWithMirrors)site, siteAdapter.getLabel());
if (site instanceof ExtendedSite) {
((ExtendedSite)site).setSelectedMirror(mirror);
}
}
catch (OperationCanceledException e) {
monitor.setCanceled(true);
return Status.CANCEL_STATUS;
}
if (mirror != null)
return searchOneSite(new MirroredUpdateSiteAdapter(mirror), categoriesToSkip, query, collector, associateSites, new SubProgressMonitor(monitor,1), false);
}
} catch (CoreException e) {
// Test the exception. If the exception is
// due to the site connection problems,
// allow the search to move on to
// the next site. Otherwise,
// rethrow the exception, causing the search
// to terminate.
IStatus status = e.getStatus();
if (status == null)
// || status.getCode() != ISite.SITE_ACCESS_EXCEPTION)
throw e;
monitor.worked(10);
return status;
}
text = NLS.bind(Messages.UpdateSearchRequest_checking, siteAdapter.getLabel());
monitor.getWrappedProgressMonitor().subTask(text);
if (site instanceof ExtendedSite) {
//System.out.println("ExtendedSite is here"); //$NON-NLS-1$
IURLEntry[] associateSitesList = ((ExtendedSite)site).getAssociateSites();
if (associateSitesList != null) {
for(int i = 0; i < associateSitesList.length; i++) {
associateSites.add(new UpdateSearchSite(associateSitesList[i].getAnnotation(), associateSitesList[i].getURL(), null));
}
}
}
query.run(
site,
categoriesToSkip,
aggregateFilter,
collector,
new SubProgressMonitor(monitor, 9));
return null;
}
}