| /******************************************************************************* |
| * Copyright (c) 2004, 2009 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.osgi.framework.internal.core; |
| |
| import java.io.IOException; |
| import java.net.*; |
| import org.eclipse.osgi.baseadaptor.BaseAdaptor; |
| import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry; |
| import org.eclipse.osgi.baseadaptor.loader.BaseClassLoader; |
| import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor; |
| import org.eclipse.osgi.framework.internal.protocol.ProtocolActivator; |
| import org.eclipse.osgi.internal.baseadaptor.AdaptorMsg; |
| import org.eclipse.osgi.internal.loader.BundleLoader; |
| import org.eclipse.osgi.util.NLS; |
| import org.osgi.framework.*; |
| |
| /** |
| * URLStreamHandler the bundleentry and bundleresource protocols. |
| */ |
| |
| public abstract class BundleResourceHandler extends URLStreamHandler implements ProtocolActivator { |
| public static final String SECURITY_CHECKED = "SECURITY_CHECKED"; //$NON-NLS-1$ |
| public static final String SECURITY_UNCHECKED = "SECURITY_UNCHECKED"; //$NON-NLS-1$ |
| private BaseAdaptor adaptor; |
| protected BundleEntry bundleEntry; |
| |
| /** |
| * Constructor for a bundle protocol resource URLStreamHandler. |
| */ |
| public BundleResourceHandler() { |
| this(null, null); |
| } |
| |
| public BundleResourceHandler(BundleEntry bundleEntry, BaseAdaptor adaptor) { |
| this.bundleEntry = bundleEntry; |
| this.adaptor = adaptor; |
| } |
| |
| public void start(BundleContext context, FrameworkAdaptor baseAdaptor) { |
| this.adaptor = (BaseAdaptor) baseAdaptor; |
| } |
| |
| /** |
| * Parse reference URL. |
| */ |
| protected void parseURL(URL url, String str, int start, int end) { |
| if (end < start) |
| return; |
| if (url.getPath() != null) |
| // A call to a URL constructor has been made that uses an authorized URL as its context. |
| // Null out bundleEntry because it will not be valid for the new path |
| bundleEntry = null; |
| String spec = ""; //$NON-NLS-1$ |
| if (start < end) |
| spec = str.substring(start, end); |
| end -= start; |
| //Default is to use path and bundleId from context |
| String path = url.getPath(); |
| String host = url.getHost(); |
| int resIndex = url.getPort(); |
| if (resIndex < 0) // -1 indicates port was not set; must default to 0 |
| resIndex = 0; |
| int pathIdx = 0; |
| if (spec.startsWith("//")) { //$NON-NLS-1$ |
| int bundleIdIdx = 2; |
| pathIdx = spec.indexOf('/', bundleIdIdx); |
| if (pathIdx == -1) { |
| pathIdx = end; |
| // Use default |
| path = ""; //$NON-NLS-1$ |
| } |
| int bundleIdEnd = spec.indexOf(':', bundleIdIdx); |
| if (bundleIdEnd > pathIdx || bundleIdEnd == -1) |
| bundleIdEnd = pathIdx; |
| if (bundleIdEnd < pathIdx - 1) |
| try { |
| resIndex = Integer.parseInt(spec.substring(bundleIdEnd + 1, pathIdx)); |
| } catch (NumberFormatException e) { |
| // do nothing; results in resIndex == 0 |
| } |
| host = spec.substring(bundleIdIdx, bundleIdEnd); |
| } |
| if (pathIdx < end && spec.charAt(pathIdx) == '/') |
| path = spec.substring(pathIdx, end); |
| else if (end > pathIdx) { |
| if (path == null || path.equals("")) //$NON-NLS-1$ |
| path = "/"; //$NON-NLS-1$ |
| int last = path.lastIndexOf('/') + 1; |
| if (last == 0) |
| path = spec.substring(pathIdx, end); |
| else |
| path = path.substring(0, last) + spec.substring(pathIdx, end); |
| } |
| if (path == null) |
| path = ""; //$NON-NLS-1$ |
| //modify path if there's any relative references |
| // see RFC2396 Section 5.2 |
| // Note: For ".." references above the root the approach taken is removing them from the resolved path |
| if (path.endsWith("/.") || path.endsWith("/..")) //$NON-NLS-1$ //$NON-NLS-2$ |
| path = path + '/'; |
| int dotIndex; |
| while ((dotIndex = path.indexOf("/./")) >= 0) //$NON-NLS-1$ |
| path = path.substring(0, dotIndex + 1) + path.substring(dotIndex + 3); |
| while ((dotIndex = path.indexOf("/../")) >= 0) { //$NON-NLS-1$ |
| if (dotIndex != 0) |
| path = path.substring(0, path.lastIndexOf('/', dotIndex - 1)) + path.substring(dotIndex + 3); |
| else |
| path = path.substring(dotIndex + 3); |
| } |
| while ((dotIndex = path.indexOf("//")) >= 0) //$NON-NLS-1$ |
| path = path.substring(0, dotIndex + 1) + path.substring(dotIndex + 2); |
| |
| // Check the permission of the caller to see if they |
| // are allowed access to the resource. |
| String authorized = SECURITY_UNCHECKED; |
| long bundleId = getBundleID(host); |
| Bundle bundle = adaptor == null ? null : adaptor.getContext().getBundle(bundleId); |
| if (checkAuthorization(bundle)) |
| authorized = SECURITY_CHECKED; |
| // Always force the use of the hash from the adaptor |
| if (adaptor != null) |
| host = Integer.toString(adaptor.hashCode()) + '.' + Long.toString(bundleId); |
| // Setting the authority portion of the URL to SECURITY_ATHORIZED |
| // ensures that this URL was created by using this parseURL |
| // method. The openConnection method will only open URLs |
| // that have the authority set to this. |
| setURL(url, url.getProtocol(), host, resIndex, authorized, null, path, null, url.getRef()); |
| } |
| |
| /** |
| * Establishes a connection to the resource specified by <code>URL</code>. |
| * Since different protocols may have unique ways of connecting, it must be |
| * overridden by the subclass. |
| * |
| * @return java.net.URLConnection |
| * @param url java.net.URL |
| * |
| * @exception IOException thrown if an IO error occurs during connection establishment |
| */ |
| protected URLConnection openConnection(URL url) throws IOException { |
| if (bundleEntry != null) // if the bundleEntry is not null then return quick |
| return (new BundleURLConnection(url, bundleEntry)); |
| |
| String host = url.getHost(); |
| if (host == null) { |
| throw new IOException(NLS.bind(AdaptorMsg.URL_NO_BUNDLE_ID, url.toExternalForm())); |
| } |
| AbstractBundle bundle = null; |
| long bundleID; |
| try { |
| bundleID = getBundleID(host); |
| } catch (NumberFormatException nfe) { |
| throw new MalformedURLException(NLS.bind(AdaptorMsg.URL_INVALID_BUNDLE_ID, host)); |
| } |
| bundle = adaptor == null ? null : (AbstractBundle) adaptor.getContext().getBundle(bundleID); |
| if (bundle == null) |
| throw new IOException(NLS.bind(AdaptorMsg.URL_NO_BUNDLE_FOUND, url.toExternalForm())); |
| // check to make sure that this URL was created using the |
| // parseURL method. This ensures the security check was done |
| // at URL construction. |
| if (!url.getAuthority().equals(SECURITY_CHECKED)) { |
| // No admin security check was made better check now. |
| checkAuthorization(bundle); |
| } |
| return (new BundleURLConnection(url, findBundleEntry(url, bundle))); |
| } |
| |
| /** |
| * Finds the bundle entry for this protocal. This is handled |
| * differently for Bundle.gerResource() and Bundle.getEntry() |
| * because getResource uses the bundle classloader and getEntry |
| * only used the base bundle file. |
| * @param url The URL to find the BundleEntry for. |
| * @return the bundle entry |
| */ |
| abstract protected BundleEntry findBundleEntry(URL url, AbstractBundle bundle) throws IOException; |
| |
| /** |
| * Converts a bundle URL to a String. |
| * |
| * @param url the URL. |
| * @return a string representation of the URL. |
| */ |
| protected String toExternalForm(URL url) { |
| StringBuffer result = new StringBuffer(url.getProtocol()); |
| result.append("://"); //$NON-NLS-1$ |
| |
| String host = url.getHost(); |
| if ((host != null) && (host.length() > 0)) |
| result.append(host); |
| int index = url.getPort(); |
| if (index > 0) |
| result.append(':').append(index); |
| |
| String path = url.getPath(); |
| if (path != null) { |
| if ((path.length() > 0) && (path.charAt(0) != '/')) /* if name doesn't have a leading slash */ |
| { |
| result.append("/"); //$NON-NLS-1$ |
| } |
| |
| result.append(path); |
| } |
| String ref = url.getRef(); |
| if (ref != null && ref.length() > 0) |
| result.append('#').append(ref); |
| |
| return (result.toString()); |
| } |
| |
| protected int hashCode(URL url) { |
| int hash = 0; |
| String protocol = url.getProtocol(); |
| if (protocol != null) |
| hash += protocol.hashCode(); |
| |
| String host = url.getHost(); |
| if (host != null) |
| hash += host.hashCode(); |
| |
| hash += url.getPort(); |
| |
| String path = url.getPath(); |
| if (path != null) |
| hash += path.hashCode(); |
| |
| if (adaptor != null) |
| hash += adaptor.hashCode(); |
| return hash; |
| } |
| |
| protected boolean equals(URL url1, URL url2) { |
| return sameFile(url1, url2); |
| } |
| |
| protected synchronized InetAddress getHostAddress(URL url) { |
| return null; |
| } |
| |
| protected boolean hostsEqual(URL url1, URL url2) { |
| String host1 = url1.getHost(); |
| String host2 = url2.getHost(); |
| if (host1 != null && host2 != null) |
| return host1.equalsIgnoreCase(host2); |
| return (host1 == null && host2 == null); |
| } |
| |
| protected boolean sameFile(URL url1, URL url2) { |
| // do a hashcode test to allow each handler to check the adaptor first |
| if (url1.hashCode() != url2.hashCode()) |
| return false; |
| String p1 = url1.getProtocol(); |
| String p2 = url2.getProtocol(); |
| if (!((p1 == p2) || (p1 != null && p1.equalsIgnoreCase(p2)))) |
| return false; |
| |
| if (!hostsEqual(url1, url2)) |
| return false; |
| |
| if (url1.getPort() != url2.getPort()) |
| return false; |
| |
| String path1 = url1.getPath(); |
| String path2 = url2.getPath(); |
| if (!((path1 == path2) || (path1 != null && path1.equals(path2)))) |
| return false; |
| |
| return true; |
| // note that the authority is not checked here because it can be different for two |
| // URLs depending on how they were constructed. |
| } |
| |
| protected boolean checkAuthorization(Bundle bundle) { |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm == null) |
| return true; |
| if (bundle == null) |
| return false; |
| sm.checkPermission(new AdminPermission(bundle, AdminPermission.RESOURCE)); |
| return true; |
| } |
| |
| protected static BaseClassLoader getBundleClassLoader(AbstractBundle bundle) { |
| BundleLoader loader = bundle.getBundleLoader(); |
| if (loader == null) |
| return null; |
| return (BaseClassLoader) loader.createClassLoader(); |
| } |
| |
| private long getBundleID(String host) { |
| int dotIndex = host.indexOf('.'); |
| return (dotIndex >= 0 && dotIndex < host.length() - 1) ? Long.parseLong(host.substring(dotIndex + 1)) : Long.parseLong(host); |
| } |
| } |