blob: 6e62a86f10c044191c319ce2d4637f076f710a1d [file] [log] [blame]
/*******************************************************************************
* 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);
}
}