| /******************************************************************************* |
| * Copyright (c) 2010, 2018 The Eclipse Foundation 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: |
| * The Eclipse Foundation - initial API and implementation |
| * Yatta Solutions - bug 397004, bug 385936, bug 432803: public API |
| *******************************************************************************/ |
| package org.eclipse.epp.internal.mpc.core.service; |
| |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.MalformedURLException; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.stream.Collectors; |
| |
| import org.apache.http.NameValuePair; |
| import org.apache.http.client.HttpClient; |
| import org.apache.http.client.entity.UrlEncodedFormEntity; |
| import org.apache.http.client.methods.HttpPost; |
| import org.apache.http.message.BasicNameValuePair; |
| 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.SubMonitor; |
| import org.eclipse.epp.internal.mpc.core.MarketplaceClientCore; |
| import org.eclipse.epp.internal.mpc.core.model.Category; |
| import org.eclipse.epp.internal.mpc.core.model.Market; |
| import org.eclipse.epp.internal.mpc.core.model.Marketplace; |
| import org.eclipse.epp.internal.mpc.core.model.News; |
| import org.eclipse.epp.internal.mpc.core.model.Node; |
| import org.eclipse.epp.internal.mpc.core.model.NodeListing; |
| import org.eclipse.epp.internal.mpc.core.model.Search; |
| import org.eclipse.epp.internal.mpc.core.model.SearchResult; |
| import org.eclipse.epp.internal.mpc.core.service.AbstractDataStorageService.NotAuthorizedException; |
| import org.eclipse.epp.internal.mpc.core.util.HttpUtil; |
| import org.eclipse.epp.internal.mpc.core.util.ServiceUtil; |
| import org.eclipse.epp.internal.mpc.core.util.URLUtil; |
| import org.eclipse.epp.mpc.core.model.ICategory; |
| import org.eclipse.epp.mpc.core.model.IFavoriteList; |
| import org.eclipse.epp.mpc.core.model.IIdentifiable; |
| import org.eclipse.epp.mpc.core.model.IIus; |
| import org.eclipse.epp.mpc.core.model.IMarket; |
| import org.eclipse.epp.mpc.core.model.INode; |
| import org.eclipse.epp.mpc.core.model.ISearchResult; |
| import org.eclipse.epp.mpc.core.service.IMarketplaceService; |
| import org.eclipse.epp.mpc.core.service.IMarketplaceServiceLocator; |
| import org.eclipse.epp.mpc.core.service.IUserFavoritesService; |
| import org.eclipse.epp.mpc.core.service.ServiceHelper; |
| import org.eclipse.osgi.util.NLS; |
| |
| /** |
| * @author David Green |
| * @author Carsten Reckord |
| */ |
| @SuppressWarnings("deprecation") |
| public class DefaultMarketplaceService extends RemoteMarketplaceService<Marketplace> implements IMarketplaceService, |
| MarketplaceService { |
| |
| // This provisional API will be identified by /api/p at the end of most urls. |
| // |
| // /api/p - Returns Markets + Categories |
| // /node/%/api/p OR /content/%/api/p - Returns a single listing's detail |
| // /taxonomy/term/%/api/p - Returns a category listing of results |
| // /featured/api/p - Returns a server-defined number of featured results. |
| // /recent/api/p - Returns a server-defined number of recent updates |
| // /favorites/top/api/p - Returns a server-defined number of top favorites |
| // /popular/top/api/p - Returns a server-defined number of most active results |
| // /related/api/p - Returns a server-defined number of recommendations based on a list of nodes provided as query parameter |
| // /news/api/p - Returns the news configuration details (news location/title...). |
| // |
| // There is one exception to adding /api/p at the end and that is for search results. |
| // |
| // /api/p/search/apachesolr_search/[query]?page=[]&filters=[] - Returns search result from the Solr Search DB. |
| // |
| // Once we've locked down the provisional API it will likely be named api/1. |
| |
| public static final String API_FAVORITES_URI = "favorites/top"; //$NON-NLS-1$ |
| |
| public static final String API_FEATURED_URI = "featured"; //$NON-NLS-1$ |
| |
| public static final String API_NEWS_URI = "news"; //$NON-NLS-1$ |
| |
| public static final String API_NODE_CONTENT_URI = "content"; //$NON-NLS-1$ |
| |
| public static final String API_NODE_URI = "node"; //$NON-NLS-1$ |
| |
| public static final String API_POPULAR_URI = "popular/top"; //$NON-NLS-1$ |
| |
| public static final String API_RELATED_URI = "related"; //$NON-NLS-1$ |
| |
| public static final String API_RECENT_URI = "recent"; //$NON-NLS-1$ |
| |
| public static final String API_SEARCH_URI = "search/apachesolr_search/"; //$NON-NLS-1$ |
| |
| public static final String API_SEARCH_URI_FULL = API_URI_SUFFIX + '/' + API_SEARCH_URI; |
| |
| public static final String API_TAXONOMY_URI = "taxonomy/term/"; //$NON-NLS-1$ |
| |
| public static final String API_FREETAGGING_URI = "category/free-tagging/"; //$NON-NLS-1$ |
| |
| public static final String DEFAULT_SERVICE_LOCATION = System |
| .getProperty(IMarketplaceServiceLocator.DEFAULT_MARKETPLACE_PROPERTY_NAME, "http://marketplace.eclipse.org"); //$NON-NLS-1$ |
| |
| public static final URL DEFAULT_SERVICE_URL; |
| |
| /** |
| * parameter identifying client |
| * |
| * @see {@link #setRequestMetaParameters(Map)} |
| */ |
| public static final String META_PARAM_CLIENT = "client"; //$NON-NLS-1$ |
| |
| /** |
| * parameter identifying client plugin version |
| * |
| * @see {@link #setRequestMetaParameters(Map)} |
| */ |
| public static final String META_PARAM_CLIENT_VERSION = "client.version"; //$NON-NLS-1$ |
| |
| /** |
| * parameter identifying windowing system as reported by {@link org.eclipse.core.runtime.Platform#getWS()} |
| * |
| * @see {@link #setRequestMetaParameters(Map)} |
| */ |
| public static final String META_PARAM_WS = "ws"; //$NON-NLS-1$ |
| |
| /** |
| * parameter identifying operating system as reported by {@link org.eclipse.core.runtime.Platform#getOS()} |
| * |
| * @see {@link #setRequestMetaParameters(Map)} |
| */ |
| public static final String META_PARAM_OS = "os"; //$NON-NLS-1$ |
| |
| /** |
| * parameter identifying the current local as reported by {@link org.eclipse.core.runtime.Platform#getNL()} |
| * |
| * @see {@link #setRequestMetaParameters(Map)} |
| */ |
| public static final String META_PARAM_NL = "nl"; //$NON-NLS-1$ |
| |
| /** |
| * parameter identifying Java version |
| * |
| * @see {@link #setRequestMetaParameters(Map)} |
| */ |
| public static final String META_PARAM_JAVA_VERSION = "java.version"; //$NON-NLS-1$ |
| |
| /** |
| * parameter identifying the Eclipse runtime version (the version of the org.eclipse.core.runtime bundle) |
| * |
| * @see {@link #setRequestMetaParameters(Map)} |
| */ |
| public static final String META_PARAM_RUNTIME_VERSION = "runtime.version"; //$NON-NLS-1$ |
| |
| /** |
| * parameter identifying the Eclipse platform version (the version of the org.eclipse.platform bundle) This |
| * parameter is optional and only sent if the platform bundle is present. It is used to identify the actual running |
| * platform's version (esp. where different platforms share the same runtime, like the parallel 3.x/4.x versions) |
| * |
| * @see {@link #setRequestMetaParameters(Map)} |
| */ |
| public static final String META_PARAM_PLATFORM_VERSION = "platform.version"; //$NON-NLS-1$ |
| |
| /** |
| * parameter identifying the Eclipse product version |
| * |
| * @see {@link #setRequestMetaParameters(Map)} |
| */ |
| public static final String META_PARAM_PRODUCT_VERSION = "product.version"; //$NON-NLS-1$ |
| |
| /** |
| * parameter identifying the product id, as provided by <code>Platform.getProduct().getId()</code> |
| * |
| * @see {@link #setRequestMetaParameters(Map)} |
| */ |
| public static final String META_PARAM_PRODUCT = "product"; //$NON-NLS-1$ |
| |
| /** |
| * parameter identifying a list of nodes for a {@link #related(List, IProgressMonitor)} query |
| */ |
| public static final String PARAM_BASED_ON_NODES = "nodes"; //$NON-NLS-1$ |
| |
| static { |
| DEFAULT_SERVICE_URL = ServiceUtil.parseUrl(DEFAULT_SERVICE_LOCATION); |
| } |
| |
| private IUserFavoritesService userFavoritesService; |
| |
| public DefaultMarketplaceService(URL baseUrl) { |
| this.baseUrl = baseUrl == null ? DEFAULT_SERVICE_URL : baseUrl; |
| } |
| |
| public DefaultMarketplaceService() { |
| this(null); |
| } |
| |
| @Override |
| public URL getBaseUrl() { |
| return super.getBaseUrl(); |
| } |
| |
| @Override |
| public List<Market> listMarkets(IProgressMonitor monitor) throws CoreException { |
| Marketplace marketplace = processRequest(API_URI_SUFFIX, monitor); |
| return marketplace.getMarket(); |
| } |
| |
| @Override |
| public Market getMarket(IMarket market, IProgressMonitor monitor) throws CoreException { |
| if (market.getId() == null && market.getUrl() == null) { |
| throw new IllegalArgumentException(); |
| } |
| List<Market> markets = listMarkets(monitor); |
| if (market.getId() != null) { |
| String marketId = market.getId(); |
| for (Market aMarket : markets) { |
| if (marketId.equals(aMarket.getId())) { |
| return aMarket; |
| } |
| } |
| throw new CoreException( |
| createErrorStatus(Messages.DefaultMarketplaceService_marketNotFound, market.getId())); |
| } else { // (market.getUrl() != null) |
| String marketUrl = market.getUrl(); |
| for (Market aMarket : markets) { |
| if (marketUrl.equals(aMarket.getUrl())) { |
| return aMarket; |
| } |
| } |
| throw new CoreException( |
| createErrorStatus(Messages.DefaultMarketplaceService_marketNotFound, market.getUrl())); |
| } |
| } |
| |
| @Override |
| public Market getMarket(Market market, IProgressMonitor monitor) throws CoreException { |
| return getMarket((IMarket) market, monitor); |
| } |
| |
| @Override |
| public Category getCategory(ICategory category, IProgressMonitor monitor) throws CoreException { |
| SubMonitor progress = SubMonitor.convert(monitor, 200); |
| if (category.getId() != null && category.getUrl() == null) { |
| List<Market> markets = listMarkets(progress.newChild(50)); |
| ICategory resolvedCategory = null; |
| outer: for (Market market : markets) { |
| List<Category> categories = market.getCategory(); |
| for (Category aCategory : categories) { |
| if (aCategory.equalsId(category)) { |
| resolvedCategory = aCategory; |
| break outer; |
| } |
| } |
| } |
| if (progress.isCanceled()) { |
| throw new OperationCanceledException(); |
| } else if (resolvedCategory == null) { |
| throw new CoreException( |
| createErrorStatus(Messages.DefaultMarketplaceService_categoryNotFound, category.getId())); |
| } else { |
| return getCategory(resolvedCategory, progress.newChild(150)); |
| } |
| } |
| Marketplace marketplace = processRequest(category.getUrl(), API_URI_SUFFIX, progress.newChild(200)); |
| if (marketplace.getCategory().isEmpty()) { |
| throw new CoreException( |
| createErrorStatus(Messages.DefaultMarketplaceService_categoryNotFound, category.getUrl())); |
| } else if (marketplace.getCategory().size() > 1) { |
| throw new CoreException( |
| createErrorStatus(Messages.DefaultMarketplaceService_unexpectedResponse, category.getUrl())); |
| } |
| Category resolvedCategory = marketplace.getCategory().get(0); |
| return resolvedCategory; |
| } |
| |
| @Override |
| public Category getCategory(Category category, IProgressMonitor monitor) throws CoreException { |
| return getCategory((ICategory) category, monitor); |
| } |
| |
| @Override |
| public Node getNode(INode node, IProgressMonitor monitor) throws CoreException { |
| Marketplace marketplace; |
| String query; |
| if (node.getId() != null) { |
| // bug 304928: prefer the id method rather than the URL, since the id provides a stable URL and the |
| // URL is based on the name, which could change. |
| query = node.getId(); |
| String encodedId = urlEncode(node.getId()); |
| marketplace = processRequest(API_NODE_URI + '/' + encodedId + '/' + API_URI_SUFFIX, monitor); |
| } else { |
| query = node.getUrl(); |
| marketplace = processRequest(node.getUrl(), API_URI_SUFFIX, monitor); |
| } |
| if (marketplace.getNode().isEmpty()) { |
| throw new CoreException(createErrorStatus(Messages.DefaultMarketplaceService_nodeNotFound, query)); |
| } else if (marketplace.getNode().size() > 1) { |
| throw new CoreException(createErrorStatus(Messages.DefaultMarketplaceService_unexpectedResponse, query)); |
| } |
| Node resolvedNode = marketplace.getNode().get(0); |
| return resolvedNode; |
| } |
| |
| @Override |
| public Node getNode(Node node, IProgressMonitor monitor) throws CoreException { |
| return getNode((INode) node, monitor); |
| } |
| |
| @Override |
| public List<INode> getNodes(Collection<? extends INode> nodes, IProgressMonitor monitor) throws CoreException { |
| SubMonitor progress = SubMonitor.convert(monitor, Messages.DefaultMarketplaceService_getNodesProgress, nodes.size()); |
| if (nodes.isEmpty()) { |
| return new ArrayList<INode>(); |
| } |
| List<INode> nodesById = null; |
| List<INode> nodesByUrl = null; |
| for (INode node : nodes) { |
| if (node.getId() == null) { |
| if (node.getUrl() == null) { |
| throw new CoreException(createErrorStatus(Messages.DefaultMarketplaceService_invalidNode, node)); |
| } |
| if (nodesByUrl == null) { |
| nodesByUrl = new ArrayList<INode>(); |
| } |
| nodesByUrl.add(node); |
| } else { |
| if (nodesById == null) { |
| nodesById = new ArrayList<INode>(nodes.size()); |
| } |
| nodesById.add(node); |
| } |
| } |
| Map<INode, INode> resolvedNodeMapping = new HashMap<INode, INode>(nodes.size()); |
| if (nodesById != null) { |
| getNodesById(nodesById, resolvedNodeMapping, progress.newChild(nodesById.size())); |
| } |
| if (nodesByUrl != null) { |
| getNodesByUrl(nodesByUrl, resolvedNodeMapping, progress.newChild(nodesByUrl.size())); |
| } |
| |
| List<INode> resultNodes = new ArrayList<INode>(nodes.size()); |
| MultiStatus missingNodes = null; |
| for (INode inputNode : nodes) { |
| INode resolvedNode = resolvedNodeMapping.get(inputNode); |
| if (resolvedNode != null) { |
| resultNodes.add(resolvedNode); |
| } else { |
| String query; |
| if (inputNode.getId() != null) { |
| query = inputNode.getId(); |
| } else { |
| query = inputNode.getUrl(); |
| } |
| IStatus missingNodeDetailStatus = createStatus(IStatus.INFO, |
| Messages.DefaultMarketplaceService_nodeNotFound, query); |
| if (missingNodes == null) { |
| missingNodes = new MultiStatus(MarketplaceClientCore.BUNDLE_ID, 0, |
| "Some entries could not be found on the Marketplace", null); //$NON-NLS-1$ |
| } |
| missingNodes.add(missingNodeDetailStatus); |
| } |
| } |
| if (missingNodes != null) { |
| MarketplaceClientCore.getLog().log(missingNodes); |
| } |
| return resultNodes; |
| } |
| |
| private void getNodesById(Collection<? extends INode> nodes, Map<INode, INode> resolvedNodeMapping, |
| IProgressMonitor monitor) throws CoreException { |
| StringBuilder nodeIdQuery = new StringBuilder(); |
| Map<String, INode> nodeIds = new HashMap<String, INode>(nodes.size()); |
| for (INode node : nodes) { |
| if (node.getId() == null) { |
| continue; |
| } |
| nodeIds.put(node.getId(), node); |
| String encodedId = urlEncode(node.getId()); |
| if (nodeIdQuery.length() > 0) { |
| nodeIdQuery.append(","); //$NON-NLS-1$ |
| } |
| nodeIdQuery.append(encodedId); |
| } |
| Marketplace marketplace = processRequest(API_NODE_URI + '/' + nodeIdQuery + '/' + API_URI_SUFFIX, monitor); |
| List<Node> resolvedNodes = marketplace.getNode(); |
| for (Node node : resolvedNodes) { |
| INode inputNode = nodeIds.get(node.getId()); |
| if (inputNode != null) { |
| resolvedNodeMapping.put(inputNode, node); |
| } else { |
| throw new CoreException( |
| createErrorStatus(Messages.DefaultMarketplaceService_unexpectedResponse, nodeIdQuery)); |
| } |
| } |
| } |
| |
| private void getNodesByUrl(Collection<? extends INode> nodes, Map<INode, INode> resolvedNodeMapping, |
| IProgressMonitor monitor) throws CoreException { |
| SubMonitor progress = SubMonitor.convert(monitor, nodes.size()); |
| int remaining = nodes.size(); |
| for (INode node : nodes) { |
| if (node.getId() == null && node.getUrl() != null) { |
| Node resolvedNode = getNode(node, progress.newChild(1)); |
| resolvedNodeMapping.put(node, resolvedNode); |
| } |
| progress.setWorkRemaining(--remaining); |
| } |
| } |
| |
| @Override |
| public SearchResult search(IMarket market, ICategory category, String queryText, IProgressMonitor monitor) |
| throws CoreException { |
| String relativeUrl = computeRelativeSearchUrl(market, category, queryText, true); |
| return processSearchRequest(relativeUrl, queryText, monitor); |
| } |
| |
| @Override |
| public SearchResult search(Market market, Category category, String queryText, IProgressMonitor monitor) |
| throws CoreException { |
| return search((IMarket) market, (ICategory) category, queryText, monitor); |
| } |
| |
| /** |
| * Creates the query URL for the Marketplace REST API. |
| * <p> |
| * If the query string is non-empty, the format for the returned relative URL is |
| * <code>search/apachesolr_search/[query]?filters=[filters]</code> where [query] is the URL encoded query string and |
| * [filters] are the category and market IDs (category first for browser urls, market first for API urls). If both |
| * market and category are null, the filters are omitted completely. |
| * <p> |
| * If the query is empty and either market or category are not null, the format for the relative URL is |
| * <code>taxonomy/term/[filters]</code> where [filters] is the comma-separated list of category and market, in that |
| * order. |
| * <p> |
| * If the query is empty and both category and market are null, the result is null |
| * |
| * @param market |
| * the market to search or null |
| * @param category |
| * the category to search or null |
| * @param queryText |
| * the search query |
| * @param api |
| * true to create REST API url, false for browser url |
| * @return the relative search url, e.g. |
| * <code>api/p/search/apachesolr_search/WikiText?filters=tid:38%20tid:31</code> or |
| * <code>taxonomy/term/38,31/api/p</code> |
| */ |
| public String computeRelativeSearchUrl(IMarket market, ICategory category, String queryText, boolean api) { |
| String relativeUrl; |
| if (queryText != null && queryText.trim().length() > 0) { |
| relativeUrl = (api ? API_SEARCH_URI_FULL : API_SEARCH_URI) + urlEncode(queryText.trim()); |
| String queryString = ""; //$NON-NLS-1$ |
| if (market != null || category != null) { |
| queryString += "filters="; //$NON-NLS-1$ |
| IIdentifiable first = api ? market : category; |
| IIdentifiable second = api ? category : market; |
| if (first != null) { |
| queryString += "tid:" + urlEncode(first.getId()); //$NON-NLS-1$ |
| if (second != null) { |
| queryString += "%20"; //$NON-NLS-1$ |
| } |
| } |
| if (second != null) { |
| queryString += "tid:" + urlEncode(second.getId()); //$NON-NLS-1$ |
| } |
| } |
| if (queryString.length() > 0) { |
| relativeUrl += '?' + queryString; |
| } |
| } else if (market != null || category != null) { |
| // http://marketplace.eclipse.org/taxonomy/term/38,31 |
| relativeUrl = API_TAXONOMY_URI; |
| if (category != null) { |
| relativeUrl += urlEncode(category.getId()); |
| if (market != null) { |
| relativeUrl += ','; |
| } |
| } |
| if (market != null) { |
| relativeUrl += urlEncode(market.getId()); |
| } |
| if (api) { |
| relativeUrl += '/' + API_URI_SUFFIX; |
| } |
| } else { |
| relativeUrl = null; |
| } |
| return relativeUrl; |
| } |
| |
| private SearchResult processSearchRequest(String relativeUrl, String queryText, IProgressMonitor monitor) |
| throws CoreException { |
| SearchResult result = new SearchResult(); |
| if (relativeUrl == null) { |
| // empty search |
| result.setMatchCount(0); |
| result.setNodes(new ArrayList<Node>()); |
| } else { |
| Marketplace marketplace; |
| try { |
| marketplace = processRequest(relativeUrl, monitor); |
| } catch (CoreException ex) { |
| Throwable cause = ex.getCause(); |
| if (cause instanceof FileNotFoundException) { |
| throw new CoreException(createErrorStatus( |
| NLS.bind(Messages.DefaultMarketplaceService_UnsupportedSearchString, queryText), cause)); |
| } |
| throw ex; |
| } |
| Search search = marketplace.getSearch(); |
| if (search != null) { |
| result.setMatchCount(search.getCount()); |
| result.setNodes(search.getNode()); |
| } else if (marketplace.getCategory().size() == 1) { |
| Category category = marketplace.getCategory().get(0); |
| result.setMatchCount(category.getNode().size()); |
| result.setNodes(category.getNode()); |
| } else { |
| throw new CoreException( |
| createErrorStatus(Messages.DefaultMarketplaceService_unexpectedResponse, relativeUrl)); |
| } |
| } |
| return result; |
| } |
| |
| @Override |
| public SearchResult tagged(String tag, IProgressMonitor monitor) throws CoreException { |
| return processSearchRequest(API_FREETAGGING_URI + URLUtil.urlEncode(tag) + '/' + API_URI_SUFFIX, tag, monitor); |
| } |
| |
| @Override |
| public SearchResult tagged(List<String> tags, IProgressMonitor monitor) throws CoreException { |
| String combinedTags = tags.stream().collect(Collectors.joining(",")); //$NON-NLS-1$ |
| return tagged(combinedTags, monitor); |
| } |
| |
| @Override |
| public SearchResult featured(IProgressMonitor monitor) throws CoreException { |
| return featured(null, null, monitor); |
| } |
| |
| @Override |
| public SearchResult featured(IMarket market, ICategory category, IProgressMonitor monitor) throws CoreException { |
| String nodePart = ""; //$NON-NLS-1$ |
| if (market != null) { |
| nodePart += urlEncode(market.getId()); |
| } |
| if (category != null) { |
| if (nodePart.length() > 0) { |
| nodePart += ","; //$NON-NLS-1$ |
| } |
| nodePart += urlEncode(category.getId()); |
| } |
| String uri = API_FEATURED_URI + '/'; |
| if (nodePart.length() > 0) { |
| uri += nodePart + '/'; |
| } |
| Marketplace marketplace = processRequest(uri + API_URI_SUFFIX, monitor); |
| return createSearchResult(marketplace.getFeatured()); |
| } |
| |
| @Override |
| public SearchResult featured(IProgressMonitor monitor, Market market, Category category) throws CoreException { |
| return featured(market, category, monitor); |
| } |
| |
| @Override |
| public SearchResult recent(IProgressMonitor monitor) throws CoreException { |
| Marketplace marketplace = processRequest(API_RECENT_URI + '/' + API_URI_SUFFIX, monitor); |
| return createSearchResult(marketplace.getRecent()); |
| } |
| |
| /** |
| * @deprecated use {@link #topFavorites(IProgressMonitor)} instead |
| */ |
| @Override |
| @Deprecated |
| public SearchResult favorites(IProgressMonitor monitor) throws CoreException { |
| return topFavorites(monitor); |
| } |
| |
| @Override |
| public SearchResult topFavorites(IProgressMonitor monitor) throws CoreException { |
| Marketplace marketplace = processRequest(API_FAVORITES_URI + '/' + API_URI_SUFFIX, monitor); |
| return createSearchResult(marketplace.getFavorites()); |
| } |
| |
| @Override |
| public List<IFavoriteList> userFavoriteLists(IProgressMonitor monitor) throws CoreException { |
| IUserFavoritesService userFavoritesService = getUserFavoritesService(); |
| if (userFavoritesService == null) { |
| throw new UnsupportedOperationException(); |
| } |
| try { |
| return userFavoritesService.getRandomFavoriteLists(monitor); |
| } catch (Exception e) { |
| throw new CoreException(MarketplaceClientCore.computeStatus(e, |
| Messages.DefaultMarketplaceService_FavoritesErrorRetrieving)); |
| } |
| } |
| |
| @Override |
| public ISearchResult userFavorites(IProgressMonitor monitor) throws CoreException, NotAuthorizedException { |
| SubMonitor progress = SubMonitor.convert(monitor, Messages.DefaultMarketplaceService_FavoritesRetrieve, 10000); |
| IUserFavoritesService userFavoritesService = getUserFavoritesService(); |
| if (userFavoritesService == null) { |
| throw new UnsupportedOperationException(); |
| } |
| final List<INode> favorites; |
| try { |
| favorites = userFavoritesService.getFavorites(progress.newChild(1000)); |
| } catch (NotAuthorizedException e) { |
| throw e; |
| } catch (Exception e) { |
| throw new CoreException(MarketplaceClientCore.computeStatus(e, Messages.DefaultMarketplaceService_FavoritesErrorRetrieving)); |
| } |
| progress.setWorkRemaining(9000); |
| return resolveFavoriteNodes(favorites, progress.newChild(9000), true); |
| } |
| |
| @Override |
| public ISearchResult userFavorites(URI favoritesUri, IProgressMonitor monitor) throws CoreException { |
| SubMonitor progress = SubMonitor.convert(monitor, Messages.DefaultMarketplaceService_FavoritesRetrieve, 10000); |
| IUserFavoritesService userFavoritesService = getUserFavoritesService(); |
| if (userFavoritesService == null) { |
| throw new UnsupportedOperationException(); |
| } |
| final List<INode> favorites; |
| try { |
| favorites = userFavoritesService.getFavorites(favoritesUri, progress.newChild(1000)); |
| } catch (Exception e) { |
| throw new CoreException(MarketplaceClientCore.computeStatus(e, |
| Messages.DefaultMarketplaceService_FavoritesErrorRetrieving)); |
| } |
| progress.setWorkRemaining(9000); |
| return resolveFavoriteNodes(favorites, progress.newChild(9000), false); |
| } |
| |
| @Override |
| public void userFavorites(List<? extends INode> nodes, IProgressMonitor monitor) |
| throws NotAuthorizedException, CoreException { |
| SubMonitor progress = SubMonitor.convert(monitor, Messages.DefaultMarketplaceService_FavoritesUpdate, 10000); |
| IUserFavoritesService userFavoritesService = getUserFavoritesService(); |
| if (userFavoritesService == null) { |
| throw new UnsupportedOperationException(); |
| } |
| if (nodes == null || nodes.isEmpty()) { |
| return; |
| } |
| Set<String> favorites = null; |
| try { |
| favorites = userFavoritesService == null ? null : userFavoritesService.getFavoriteIds(progress); |
| } catch (NotAuthorizedException e) { |
| throw e; |
| } catch (Exception e) { |
| throw new CoreException(MarketplaceClientCore.computeStatus(e, Messages.DefaultMarketplaceService_FavoritesErrorRetrieving)); |
| } finally { |
| for (INode node : nodes) { |
| ((Node) node).setUserFavorite(favorites == null ? null : favorites.contains(node.getId())); |
| } |
| } |
| } |
| |
| private ISearchResult resolveFavoriteNodes(final List<INode> nodes, IProgressMonitor monitor, boolean filterIncompatible) throws CoreException { |
| IMarketplaceService resolveService = this; |
| IMarketplaceService registeredService = ServiceHelper.getMarketplaceServiceLocator() |
| .getMarketplaceService(this.getBaseUrl().toString()); |
| if (registeredService instanceof CachingMarketplaceService) { |
| CachingMarketplaceService cachingService = (CachingMarketplaceService) registeredService; |
| if (cachingService.getDelegate() == this) { |
| resolveService = cachingService; |
| } |
| } |
| final List<INode> resolvedNodes = resolveService.getNodes(nodes, monitor); |
| for (ListIterator<INode> i = resolvedNodes.listIterator(); i.hasNext();) { |
| INode resolved = i.next(); |
| ((Node) resolved).setUserFavorite(true); |
| if (filterIncompatible && !isInstallable(resolved)) { |
| i.remove(); |
| } |
| } |
| if (!filterIncompatible) { |
| //sort the node list so uninstallable nodes come last |
| Collections.sort(resolvedNodes, (n1, n2) -> { |
| if (n1 == n2) { |
| return 0; |
| } |
| boolean n1Installable = isInstallable(n1); |
| boolean n2Installable = isInstallable(n2); |
| if (n1Installable == n2Installable) { |
| return 0; |
| } |
| if (n1Installable) { // && !n2Installable |
| return -1; |
| } |
| // !n1Installable && n2Installable |
| return 1; |
| }); |
| } |
| |
| return new ISearchResult() { |
| |
| @Override |
| public List<? extends INode> getNodes() { |
| return resolvedNodes; |
| } |
| |
| @Override |
| public Integer getMatchCount() { |
| return resolvedNodes.size(); |
| } |
| }; |
| } |
| |
| private boolean isInstallable(INode resolved) { |
| IIus ius = resolved.getIus(); |
| return ius != null && !ius.getIuElements().isEmpty(); |
| } |
| |
| @Override |
| public SearchResult popular(IProgressMonitor monitor) throws CoreException { |
| Marketplace marketplace = processRequest(API_POPULAR_URI + '/' + API_URI_SUFFIX, monitor); |
| return createSearchResult(marketplace.getPopular()); |
| } |
| |
| @Override |
| public SearchResult related(List<? extends INode> basedOn, IProgressMonitor monitor) throws CoreException { |
| String basedOnQuery = ""; //$NON-NLS-1$ |
| if (basedOn != null && !basedOn.isEmpty()) { |
| StringBuilder sb = new StringBuilder().append('?').append(PARAM_BASED_ON_NODES).append('='); |
| boolean first = true; |
| for (INode node : basedOn) { |
| if (!first) { |
| sb.append('+'); |
| } |
| sb.append(node.getId()); |
| first = false; |
| } |
| basedOnQuery = sb.toString(); |
| } |
| Marketplace marketplace = processRequest(API_RELATED_URI + '/' + API_URI_SUFFIX + basedOnQuery, monitor); |
| return createSearchResult(marketplace.getRelated()); |
| } |
| |
| protected SearchResult createSearchResult(NodeListing nodeList) throws CoreException { |
| if (nodeList == null) { |
| throw new CoreException( |
| createErrorStatus(Messages.DefaultMarketplaceService_unexpectedResponse, Messages.DefaultMarketplaceService_nullResultNodes)); |
| } |
| SearchResult result = new SearchResult(); |
| result.setMatchCount(nodeList.getCount()); |
| result.setNodes(nodeList.getNode()); |
| return result; |
| } |
| |
| @Override |
| public News news(IProgressMonitor monitor) throws CoreException { |
| try { |
| Marketplace marketplace = processRequest(API_NEWS_URI + '/' + API_URI_SUFFIX, monitor); |
| return marketplace.getNews(); |
| } catch (CoreException ex) { |
| final Throwable cause = ex.getCause(); |
| if (cause instanceof FileNotFoundException) { |
| // optional news API not supported |
| return null; |
| } |
| throw ex; |
| } |
| } |
| |
| /** |
| * @deprecated use {@link #reportInstallError(IStatus, Set, Set, String, IProgressMonitor)} instead |
| */ |
| @Override |
| @Deprecated |
| public void reportInstallError(IProgressMonitor monitor, IStatus result, Set<Node> nodes, |
| Set<String> iuIdsAndVersions, String resolutionDetails) throws CoreException { |
| reportInstallError(result, nodes, iuIdsAndVersions, resolutionDetails, monitor); |
| } |
| |
| @Override |
| public void reportInstallError(IStatus result, Set<? extends INode> nodes, Set<String> iuIdsAndVersions, |
| String resolutionDetails, IProgressMonitor monitor) throws CoreException { |
| HttpClient client; |
| URL location; |
| HttpPost method; |
| try { |
| location = new URL(baseUrl, "install/error/report"); //$NON-NLS-1$ |
| String target = location.toURI().toString(); |
| client = HttpUtil.createHttpClient(target); |
| method = new HttpPost(target); |
| } catch (URISyntaxException e) { |
| throw new IllegalStateException(e); |
| } catch (MalformedURLException e) { |
| throw new IllegalStateException(e); |
| } |
| try { |
| List<NameValuePair> parameters = new ArrayList<NameValuePair>(); |
| |
| Map<String, String> requestMetaParameters = getRequestMetaParameters(); |
| for (Map.Entry<String, String> metaParam : requestMetaParameters.entrySet()) { |
| if (metaParam.getKey() != null) { |
| parameters.add(new BasicNameValuePair(metaParam.getKey(), metaParam.getValue())); |
| } |
| } |
| |
| parameters.add(new BasicNameValuePair("status", Integer.toString(result.getSeverity()))); //$NON-NLS-1$ |
| parameters.add(new BasicNameValuePair("statusMessage", result.getMessage())); //$NON-NLS-1$ |
| for (INode node : nodes) { |
| parameters.add(new BasicNameValuePair("node", node.getId())); //$NON-NLS-1$ |
| } |
| if (iuIdsAndVersions != null && !iuIdsAndVersions.isEmpty()) { |
| for (String iuAndVersion : iuIdsAndVersions) { |
| parameters.add(new BasicNameValuePair("iu", iuAndVersion)); //$NON-NLS-1$ |
| } |
| } |
| parameters.add(new BasicNameValuePair("detailedMessage", resolutionDetails)); //$NON-NLS-1$ |
| if (!parameters.isEmpty()) { |
| UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parameters, "UTF-8"); //$NON-NLS-1$ |
| method.setEntity(entity); |
| client.execute(method); |
| } |
| } catch (IOException e) { |
| String message = NLS.bind(Messages.DefaultMarketplaceService_cannotCompleteRequest_reason, |
| location.toString(), e.getMessage()); |
| throw new CoreException(createErrorStatus(message, e)); |
| } finally { |
| client.getConnectionManager().shutdown(); |
| } |
| } |
| |
| @Override |
| public void reportInstallSuccess(INode node, IProgressMonitor monitor) { |
| String url = node.getUrl(); |
| if (!url.endsWith("/")) { //$NON-NLS-1$ |
| url += "/"; //$NON-NLS-1$ |
| } |
| url += "success"; //$NON-NLS-1$ |
| url = addMetaParameters(url); |
| try { |
| InputStream stream = transport.stream(new URI(url), monitor); |
| |
| try { |
| while (stream.read() != -1) { |
| // nothing to do |
| } |
| } finally { |
| stream.close(); |
| } |
| } catch (Throwable e) { |
| //per bug 314028 logging this error is not useful. |
| } |
| } |
| |
| @Override |
| public IUserFavoritesService getUserFavoritesService() { |
| return userFavoritesService; |
| } |
| |
| public void setUserFavoritesService(IUserFavoritesService userFavoritesService) { |
| this.userFavoritesService = userFavoritesService; |
| } |
| } |