/******************************************************************************* * 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; } }