/***************************************************************************** * Copyright (c) 2006-2013, Cloudsmith Inc. * The code, documentation and other materials contained herein have been * licensed under the Eclipse Public License - v 1.0 by the copyright holder * listed above, as the Initial Contributor under such license. The text of * such license is available at www.eclipse.org. * * Contributors: * Lorenzo Bettini - https://bugs.eclipse.org/bugs/show_bug.cgi?id=428301 *****************************************************************************/ package org.eclipse.buckminster.pde.mapprovider; import static org.eclipse.buckminster.core.helpers.MapUtils.getString; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.UUID; import org.eclipse.buckminster.core.CorePlugin; import org.eclipse.buckminster.core.XMLConstants; import org.eclipse.buckminster.core.common.model.Documentation; import org.eclipse.buckminster.core.common.model.Format; import org.eclipse.buckminster.core.common.model.Replace; import org.eclipse.buckminster.core.cspec.IComponentIdentifier; import org.eclipse.buckminster.core.cspec.model.ComponentIdentifier; import org.eclipse.buckminster.core.cspec.model.ComponentRequest; import org.eclipse.buckminster.core.ctype.IComponentType; import org.eclipse.buckminster.core.helpers.FileHandle; import org.eclipse.buckminster.core.helpers.FileUtils; import org.eclipse.buckminster.core.reader.ICatalogReader; import org.eclipse.buckminster.core.reader.IComponentReader; import org.eclipse.buckminster.core.reader.IReaderType; import org.eclipse.buckminster.core.resolver.NodeQuery; import org.eclipse.buckminster.core.resolver.ResolverDecision; import org.eclipse.buckminster.core.resolver.ResolverDecisionType; import org.eclipse.buckminster.core.rmap.model.Provider; import org.eclipse.buckminster.core.rmap.model.ProviderScore; import org.eclipse.buckminster.core.rmap.model.SearchPath; import org.eclipse.buckminster.core.rmap.model.VersionConverterDesc; import org.eclipse.buckminster.core.version.IVersionConverter; import org.eclipse.buckminster.core.version.ProviderMatch; import org.eclipse.buckminster.core.version.VersionMatch; import org.eclipse.buckminster.core.version.VersionSelector; import org.eclipse.buckminster.osgi.filter.Filter; import org.eclipse.buckminster.pde.Messages; import org.eclipse.buckminster.pde.PDEPlugin; import org.eclipse.buckminster.pde.mapfile.MapFile; import org.eclipse.buckminster.pde.mapfile.MapFileEntry; import org.eclipse.buckminster.runtime.BuckminsterException; import org.eclipse.buckminster.runtime.IOUtils; import org.eclipse.buckminster.runtime.MonitorUtils; import org.eclipse.buckminster.runtime.Trivial; 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.Path; import org.eclipse.core.runtime.Status; import org.eclipse.equinox.p2.metadata.Version; import org.eclipse.equinox.p2.metadata.VersionRange; import org.eclipse.osgi.util.NLS; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; public class PDEMapProvider extends Provider { public static final String BM_PDEMAP_PROVIDER_NS = XMLConstants.BM_PREFIX + "PDEMapProvider-1.0"; //$NON-NLS-1$ public static final String BM_PDEMAP_PROVIDER_PREFIX = "pmp"; //$NON-NLS-1$ private final Replace replace; public PDEMapProvider(SearchPath searchPath, String remoteReaderType, String[] componentTypes, VersionConverterDesc vcDesc, Format uri, Filter resolutionFilter, Map<String, String> properties, Replace replace, Documentation documentation) { super(searchPath, remoteReaderType, componentTypes, vcDesc, uri, null, null, resolutionFilter, properties, null, documentation); this.replace = replace; } @Override public void addPrefixMappings(HashMap<String, String> prefixMappings) { super.addPrefixMappings(prefixMappings); prefixMappings.put(BM_PDEMAP_PROVIDER_PREFIX, BM_PDEMAP_PROVIDER_NS); } @Override public ProviderMatch findMatch(NodeQuery query, MultiStatus problemCollector, IProgressMonitor monitor) throws CoreException { monitor.beginTask("", 100); //$NON-NLS-1$ try { String providerURI = getURI(query.getProperties()); String readerType = getReaderTypeId(); ProviderScore score = query.getProviderScore(isMutable(), hasSource()); if (score == ProviderScore.REJECTED) { ResolverDecision decision = query.logDecision(ResolverDecisionType.REJECTING_PROVIDER, readerType, providerURI, org.eclipse.buckminster.core.Messages.Score_is_below_threshold); problemCollector.add(new Status(IStatus.ERROR, CorePlugin.getID(), IStatus.OK, decision.toString(), null)); return null; } MapFileEntry tv = getMapFileEntry(query, problemCollector, getMap(query, problemCollector, MonitorUtils.subMonitor(monitor, 50))); if (tv == null) { // // Map was not materialized // String msg = NLS.bind(Messages.PDEMapProvider_0_for_1_did_not_find_any_maps, readerType, providerURI); problemCollector.add(new Status(IStatus.ERROR, CorePlugin.getID(), IStatus.OK, msg, null)); return null; } Map<String, Object> properties = tv.getProperties(); Version v = null; VersionSelector vs = null; IReaderType rt = tv.getReaderType(); String id = null; if ("p2".equals(rt.getId())) { //$NON-NLS-1$ String vstr = getString(properties, "version"); //$NON-NLS-1$ if (vstr != null) v = Version.create(vstr); id = getString(properties, "id"); //$NON-NLS-1$ } else { String tag = getString(properties, "tag"); //$NON-NLS-1$ if (tag != null) vs = VersionSelector.tag(tag); IVersionConverter vc = getVersionConverter(); if (vc != null) { // Let's check that the given tag matches what we are asking // for. // v = vc.createVersion(vs); } } if (v != null) { VersionRange vd = query.getVersionRange(); if (!(vd == null || vd.isIncluded(v))) { ResolverDecision decision = query.logDecision(ResolverDecisionType.VERSION_REJECTED, v, NLS.bind(org.eclipse.buckminster.core.Messages.Not_designated_by_0, vd)); problemCollector.add(new Status(IStatus.ERROR, CorePlugin.getID(), IStatus.OK, decision.toString(), null)); return null; } } VersionMatch vm = new VersionMatch(v, vs, -1, null, id); ComponentRequest rq = query.getComponentRequest(); String repoLocator = rt.convertFetchFactoryLocator(properties, rq.getName()); Format uri = new Format(repoLocator); Map<String, String> props = rt.getFetchFactoryProviderProps(properties, this); Provider delegated = new Provider(getSearchPath(), rt.getId(), getComponentTypeIDs(), getVersionConverterDesc(), uri, null, null, getResolutionFilter(), props, null, null); String ctypeID = rq.getComponentTypeID(); if (ctypeID == null) return delegated.findMatch(query, problemCollector, monitor); IComponentType ctype = tv.getComponentIdentifier().getComponentType(); ProviderMatch pm = new ProviderMatch(PDEMapProvider.this, ctype, vm, score, query); pm.setProvider(delegated); pm.setComponentType(ctype); return pm; } finally { monitor.done(); } } /** * Returns a map that contains entries in the following form: * * <pre> * <elementType>@<elementID> = <REPOSITORYgt;, <TAG>, [...] * </pre> * * @return */ public Map<ComponentIdentifier, MapFileEntry> getMap(NodeQuery query, MultiStatus problemCollector, IProgressMonitor monitor) throws CoreException { monitor.beginTask(null, 700); Map<UUID, Object> userCache = query.getContext().getUserCache(); synchronized (userCache) { Map<ComponentIdentifier, MapFileEntry> map = getCachedMap(userCache); if (map != null) return map; FileHandle folderHandle = null; try { VersionSelector[] btPath = query.getBranchTagPath(); if (btPath.length == 0) folderHandle = materializeMaps(null, query, MonitorUtils.subMonitor(monitor, 500)); else { CoreException lastException = null; for (VersionSelector bt : btPath) { try { folderHandle = materializeMaps(bt, query, MonitorUtils.subMonitor(monitor, 500)); lastException = null; break; } catch (CoreException e) { lastException = e; } } if (lastException != null) throw lastException; } map = new HashMap<ComponentIdentifier, MapFileEntry>(); File folder = folderHandle.getFile(); String[] mapFiles = folder.list(); if (mapFiles == null || mapFiles.length == 0) return null; MonitorUtils.worked(monitor, 50); int amountPerFile = 100 / mapFiles.length; Map<String, ? extends Object> queryProps = getProperties(query.getProperties()); for (String file : mapFiles) { if (file.endsWith(".map")) //$NON-NLS-1$ collectEntries(queryProps, new File(folder, file), map); MonitorUtils.worked(monitor, amountPerFile); } map = Collections.unmodifiableMap(map); cacheMap(userCache, map); return map; } catch (CoreException e) { problemCollector.add(e.getStatus()); PDEPlugin.getLogger().debug(e.getMessage()); return null; } finally { if (folderHandle != null && folderHandle.isTemporary()) FileUtils.deleteRecursive(folderHandle.getFile(), MonitorUtils.subMonitor(monitor, 50)); monitor.done(); } } } @Override protected void addAttributes(AttributesImpl attrs) throws SAXException { super.addAttributes(attrs); attrs.addAttribute(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "type", "xsi:type", "CDATA", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ BM_PDEMAP_PROVIDER_PREFIX + ":PDEMapProvider"); //$NON-NLS-1$ } private void cacheMap(Map<UUID, Object> userCache, Map<ComponentIdentifier, MapFileEntry> map) { userCache.put(getId(), map); } private void collectEntries(Map<String, ? extends Object> queryProps, File mapFile, Map<ComponentIdentifier, MapFileEntry> map) throws CoreException { InputStream input = null; try { input = new FileInputStream(mapFile); ArrayList<MapFileEntry> list = new ArrayList<MapFileEntry>(); MapFile.parse(input, mapFile.getCanonicalPath(), replace, queryProps, list); for (MapFileEntry entry : list) map.put(entry.getComponentIdentifier(), entry); } catch (IOException e) { throw BuckminsterException.wrap(e); } finally { IOUtils.close(input); } } @SuppressWarnings("unchecked") private Map<ComponentIdentifier, MapFileEntry> getCachedMap(Map<UUID, Object> userCache) { return (Map<ComponentIdentifier, MapFileEntry>) userCache.get(getId()); } private MapFileEntry getMapFileEntry(NodeQuery query, MultiStatus problemCollector, Map<ComponentIdentifier, MapFileEntry> map) { if (map == null) return null; ComponentRequest wanted = query.getComponentRequest(); String name = wanted.getName(); String ctype = wanted.getComponentTypeID(); VersionRange vd = wanted.getVersionRange(); IComponentIdentifier candidate = null; MapFileEntry candidateEntry = null; for (Map.Entry<ComponentIdentifier, MapFileEntry> entry : map.entrySet()) { ComponentIdentifier cn = entry.getKey(); if (cn.getName().equals(name) && Trivial.equalsAllowNull(ctype, cn.getComponentTypeID())) { Version v = cn.getVersion(); if (vd != null) { if (!(v == null || vd.isIncluded(v))) continue; } if (candidate != null) { if (v == null) continue; if (candidate.getVersion() != null && candidate.getVersion().compareTo(v) > 0) continue; } candidate = cn; candidateEntry = entry.getValue(); } } if (candidateEntry == null) { String msg = NLS.bind(Messages.PDEMapProvider_0_for_1_unable_to_find_2_in_map, new Object[] { getReaderTypeId(), getURI(query.getProperties()), wanted }); problemCollector.add(new Status(IStatus.ERROR, CorePlugin.getID(), IStatus.OK, msg, null)); PDEPlugin.getLogger().debug(msg); } return candidateEntry; } private FileHandle materializeMaps(VersionSelector vs, NodeQuery query, IProgressMonitor monitor) throws CoreException { MonitorUtils.begin(monitor, 500); ProviderMatch match = new ProviderMatch(this, CorePlugin.getDefault().getComponentType(IComponentType.UNKNOWN), new VersionMatch(null, vs, -1, null, null), ProviderScore.GOOD, query); IComponentReader reader = match.getReader(MonitorUtils.subMonitor(monitor, 100)); try { File location = reader.getLocation(); if (location != null) return new FileHandle(location.getAbsolutePath(), location, false); File tempFolder = FileUtils.createTempFolder("bucky", ".tmp"); //$NON-NLS-1$ //$NON-NLS-2$ try { ((ICatalogReader) reader).innerMaterialize(Path.fromOSString(tempFolder.getAbsolutePath()), MonitorUtils.subMonitor(monitor, 350)); } catch (CoreException e) { FileUtils.deleteRecursive(tempFolder, MonitorUtils.subMonitor(monitor, 50)); throw e; } return new FileHandle(tempFolder.getName(), tempFolder, true); } finally { IOUtils.close(reader); MonitorUtils.done(monitor); } } }