/******************************************************************************* * Copyright (c) 2000, 2007 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 * James D Miles (IBM Corp.) - bug 191368, Policy URL doesn't support UTF-8 characters *******************************************************************************/ package org.eclipse.update.internal.search; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.osgi.util.NLS; import org.eclipse.update.core.ISite; import org.eclipse.update.core.Utilities; import org.eclipse.update.internal.core.Messages; import org.eclipse.update.internal.core.URLEncoder; import org.eclipse.update.internal.core.UpdateManagerUtils; import org.eclipse.update.internal.core.connection.ConnectionFactory; import org.eclipse.update.internal.core.connection.IResponse; import org.eclipse.update.search.IUpdateSiteAdapter; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * * This class opens connection to the update map resource * and parses the file to load update URL mappings. * These mappings are used to redirect new updates search * when the redirection pattern matches. */ public class UpdatePolicy { private static final String TAG_POLICY = "update-policy"; //$NON-NLS-1$ private static final String TAG_URL_MAP = "url-map"; //$NON-NLS-1$ private static final String ATT_URL = "url"; //$NON-NLS-1$ private static final String ATT_PATTERN = "pattern"; //$NON-NLS-1$ private static final String ATT_TYPE = "url-type"; //$NON-NLS-1$ private static final String ATT_TYPE_VALUE_UPDATE = "update"; //$NON-NLS-1$ //private static final String ATT_TYPE_VALUE_BOTH = "both"; //$NON-NLS-1$ private static final String ATT_TYPE_VALUE_DISCOVERY = "discovery"; //$NON-NLS-1$ private static final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); private static class MapSite implements IUpdateSiteAdapter { private URL url; public MapSite(URL url) { this.url = url; } public String getLabel() { if (url == null) { return ""; //$NON-NLS-1$ } return url.toString(); } public URL getURL() { return url; } } private static class UpdateMapEntry { private IUpdateSiteAdapter site; private String pattern; public UpdateMapEntry(String pattern, URL url) { this.pattern = pattern; this.site = new MapSite(url); } public IUpdateSiteAdapter getSite() { return site; } public boolean matches(String id) { return id.startsWith(pattern); } public String getPattern() { return pattern; } } private ArrayList entries; private ArrayList discoveryEntries; private IUpdateSiteAdapter defaultSite; private IUpdateSiteAdapter defaultDiscoverySite; private boolean loaded = false; private boolean fallbackAllowed = true; public UpdatePolicy() { entries = new ArrayList(); discoveryEntries = new ArrayList(); } public void load(URL mapFile, IProgressMonitor monitor) throws CoreException { InputStream policyStream = null; try { IResponse response = ConnectionFactory.get(mapFile); UpdateManagerUtils.checkConnectionResult(response, mapFile); policyStream = response.getInputStream(monitor); // the stream can be null if the user cancels the connection if (policyStream == null) return; documentBuilderFactory.setNamespaceAware(true); DocumentBuilder parser = documentBuilderFactory.newDocumentBuilder(); Document doc = parser.parse(new InputSource(policyStream)); processUpdatePolicy(doc); loaded = true; } catch (IOException e) { throw Utilities.newCoreException( NLS.bind(Messages.SiteURLFactory_UnableToAccessSiteStream, (new String[] { mapFile == null ? "" : mapFile.toExternalForm() })), //$NON-NLS-1$ ISite.SITE_ACCESS_EXCEPTION, e); } catch (SAXException e) { throw Utilities.newCoreException( Messages.UpdatePolicy_parsePolicy, 0, e); } catch(ParserConfigurationException e) { throw Utilities.newCoreException( Messages.UpdatePolicy_parsePolicy, 0, e); } finally { if (policyStream != null) { try { policyStream.close(); } catch (IOException e) { } } } } public boolean isLoaded() { return loaded; } /* * Given the feature ID, returns the mapped update URL if * found in the mappings. This URL will be used INSTEAD of * the update URL encoded in the feature itself during the * new update search. * <p>In case of multiple matches (e.g. org.eclipse and org.eclipse.platform) * the URL for the longer pattern will be picked (i.e. org.eclipse.platform). */ public IUpdateSiteAdapter getMappedSite(String id) { UpdateMapEntry lastEntry = null; for (int i = 0; i < entries.size(); i++) { UpdateMapEntry entry = (UpdateMapEntry) entries.get(i); if (entry.matches(id)) { if (lastEntry == null) lastEntry = entry; else { // Choose the match with longer pattern. // For example, if two matches are found: // 'org.eclipse' and 'org.eclipse.platform', // pick 'org.eclipse.platform'. String pattern = entry.getPattern(); String lastPattern = lastEntry.getPattern(); if (pattern.length() > lastPattern.length()) lastEntry = entry; } } } if (lastEntry != null) return lastEntry.getSite(); else return defaultSite; } /* * Given the feature ID, returns the mapped discovery URL if * found in the mappings. This URL will be used INSTEAD of * the discovery URL encoded in the feature itself during the * new search. * <p>In case of multiple matches (e.g. org.eclipse and org.eclipse.platform) * the URL for the longer pattern will be picked (i.e. org.eclipse.platform). */ public IUpdateSiteAdapter getMappedDiscoverySite(String id) { UpdateMapEntry lastEntry = null; for (int i = 0; i < discoveryEntries.size(); i++) { UpdateMapEntry entry = (UpdateMapEntry) discoveryEntries.get(i); if (entry.matches(id)) { if (lastEntry == null) lastEntry = entry; else { // Choose the match with longer pattern. // For example, if two matches are found: // 'org.eclipse' and 'org.eclipse.platform', // pick 'org.eclipse.platform'. String pattern = entry.getPattern(); String lastPattern = lastEntry.getPattern(); if (pattern.length() > lastPattern.length()) lastEntry = entry; } } } if (lastEntry != null) return lastEntry.getSite(); else return defaultDiscoverySite; } public boolean isFallbackAllowed() { return fallbackAllowed; } private void reset() { if (!entries.isEmpty()) entries.clear(); if (!discoveryEntries.isEmpty()) discoveryEntries.clear(); } private void processUpdatePolicy(Document document) throws CoreException { Node root = document.getDocumentElement(); reset(); if (root.getNodeName().equals(TAG_POLICY)==false) throwCoreException("'"+TAG_POLICY+Messages.UpdatePolicy_policyExpected, null); //$NON-NLS-1$ NodeList nodes = root.getChildNodes(); for (int i=0; i<nodes.getLength(); i++) { Node child = nodes.item(i); if (child.getNodeType() != Node.ELEMENT_NODE) continue; String tag = child.getNodeName(); if (tag.equals(TAG_URL_MAP)) processMapNode(child); } } private void processMapNode(Node node) throws CoreException { String pattern = getAttribute(node, ATT_PATTERN); String urlName = getAttribute(node, ATT_URL); String type = getAttribute(node, ATT_TYPE); assertNotNull(ATT_PATTERN, pattern); assertNotNull(ATT_URL, urlName); // empty url means feature is not updateable if (urlName.trim().length() == 0) { addUpdateEntry(pattern, null, type); return; } try { URL url = new URL(urlName); URL resolvedURL = URLEncoder.encode(url); addUpdateEntry(pattern, resolvedURL, type); } catch (MalformedURLException e) { throwCoreException(Messages.UpdatePolicy_invalidURL+urlName, null); } } private void assertNotNull(String name, String value) throws CoreException { if (value==null) throwCoreException(name+Messages.UpdatePolicy_nameNoNull, null); } private String getAttribute(Node node, String name) { NamedNodeMap attMap = node.getAttributes(); Node att = attMap.getNamedItem(name); if (att==null) return null; return att.getNodeValue(); } private void addUpdateEntry(String pattern, URL url, String type) { if (pattern.equalsIgnoreCase("*")) {//$NON-NLS-1$ if (type == null) defaultSite = new MapSite(url); else if (type.equals(ATT_TYPE_VALUE_UPDATE)) defaultSite = new MapSite(url); else if (type.equals(ATT_TYPE_VALUE_DISCOVERY)) defaultDiscoverySite = new MapSite(url); else { defaultSite = new MapSite(url); defaultDiscoverySite = new MapSite(url); } } else { if (type == null ) entries.add(new UpdateMapEntry(pattern, url)); else if (type.equals(ATT_TYPE_VALUE_UPDATE)) entries.add(new UpdateMapEntry(pattern, url)); else if (type.equals(ATT_TYPE_VALUE_DISCOVERY)) discoveryEntries.add(new UpdateMapEntry(pattern, url)); else { entries.add(new UpdateMapEntry(pattern, url)); discoveryEntries.add(new UpdateMapEntry(pattern, url)); } } } private void throwCoreException(String message, Throwable e) throws CoreException { String fullMessage = Messages.UpdatePolicy_UpdatePolicy+message; throw Utilities.newCoreException(fullMessage, 0, e); } }