| /******************************************************************************* |
| * Copyright (c) 2003, 2016 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.osgi.internal.url; |
| |
| import java.io.IOException; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.net.*; |
| import org.osgi.framework.*; |
| import org.osgi.service.url.URLConstants; |
| import org.osgi.service.url.URLStreamHandlerService; |
| import org.osgi.util.tracker.ServiceTracker; |
| import org.osgi.util.tracker.ServiceTrackerCustomizer; |
| |
| /** |
| * The URLStreamHandlerProxy is a URLStreamHandler that acts as a proxy for registered |
| * URLStreamHandlerServices. When a URLStreamHandler is requested from the URLStreamHandlerFactory |
| * and it exists in the service registry, a URLStreamHandlerProxy is created which will pass all the |
| * requests from the requestor to the real URLStreamHandlerService. We can't return the real |
| * URLStreamHandlerService from the URLStreamHandlerFactory because the JVM caches URLStreamHandlers |
| * and therefore would not support a dynamic environment of URLStreamHandlerServices being registered |
| * and unregistered. |
| */ |
| |
| public class URLStreamHandlerProxy extends URLStreamHandler implements ServiceTrackerCustomizer<URLStreamHandlerService, ServiceReference<URLStreamHandlerService>> { |
| // TODO lots of type-based names |
| protected URLStreamHandlerService realHandlerService; |
| |
| protected URLStreamHandlerSetter urlSetter; |
| |
| protected ServiceTracker<URLStreamHandlerService, ServiceReference<URLStreamHandlerService>> urlStreamHandlerServiceTracker; |
| |
| protected BundleContext context; |
| protected ServiceReference<URLStreamHandlerService> urlStreamServiceReference; |
| |
| protected String protocol; |
| |
| protected int ranking = Integer.MIN_VALUE; |
| |
| public URLStreamHandlerProxy(String protocol, ServiceReference<URLStreamHandlerService> reference, BundleContext context) { |
| this.context = context; |
| this.protocol = protocol; |
| |
| urlSetter = new URLStreamHandlerSetter(this); |
| |
| //set the handler and ranking |
| setNewHandler(reference, getRank(reference)); |
| |
| urlStreamHandlerServiceTracker = new ServiceTracker<>(context, URLStreamHandlerFactoryImpl.URLSTREAMHANDLERCLASS, this); |
| URLStreamHandlerFactoryImpl.secureAction.open(urlStreamHandlerServiceTracker); |
| } |
| |
| private void setNewHandler(ServiceReference<URLStreamHandlerService> reference, int rank) { |
| if (urlStreamServiceReference != null) |
| context.ungetService(urlStreamServiceReference); |
| |
| urlStreamServiceReference = reference; |
| ranking = rank; |
| |
| if (reference == null) |
| realHandlerService = new NullURLStreamHandlerService(); |
| else |
| realHandlerService = URLStreamHandlerFactoryImpl.secureAction.getService(reference, context); |
| } |
| |
| /** |
| * @see java.net.URLStreamHandler#equals(URL, URL) |
| */ |
| protected boolean equals(URL url1, URL url2) { |
| return realHandlerService.equals(url1, url2); |
| } |
| |
| /** |
| * @see java.net.URLStreamHandler#getDefaultPort() |
| */ |
| protected int getDefaultPort() { |
| return realHandlerService.getDefaultPort(); |
| } |
| |
| /** |
| * @see java.net.URLStreamHandler#getHostAddress(URL) |
| */ |
| protected InetAddress getHostAddress(URL url) { |
| return realHandlerService.getHostAddress(url); |
| } |
| |
| /** |
| * @see java.net.URLStreamHandler#hashCode(URL) |
| */ |
| protected int hashCode(URL url) { |
| return realHandlerService.hashCode(url); |
| } |
| |
| /** |
| * @see java.net.URLStreamHandler#hostsEqual(URL, URL) |
| */ |
| protected boolean hostsEqual(URL url1, URL url2) { |
| return realHandlerService.hostsEqual(url1, url2); |
| } |
| |
| /** |
| * @see java.net.URLStreamHandler#openConnection(URL) |
| */ |
| protected URLConnection openConnection(URL url) throws IOException { |
| return realHandlerService.openConnection(url); |
| } |
| |
| /** |
| * @see java.net.URLStreamHandler#parseURL(URL, String, int, int) |
| */ |
| protected void parseURL(URL url, String str, int start, int end) { |
| realHandlerService.parseURL(urlSetter, url, str, start, end); |
| } |
| |
| /** |
| * @see java.net.URLStreamHandler#sameFile(URL, URL) |
| */ |
| protected boolean sameFile(URL url1, URL url2) { |
| return realHandlerService.sameFile(url1, url2); |
| } |
| |
| /** |
| * @see java.net.URLStreamHandler#toExternalForm(URL) |
| */ |
| protected String toExternalForm(URL url) { |
| return realHandlerService.toExternalForm(url); |
| } |
| |
| /** |
| * @see java.net.URLStreamHandler#setURL(URL, String, String, int, String, String, String, String, String) |
| */ |
| public void setURL(URL u, String protocol, String host, int port, String authority, String userInfo, String file, String query, String ref) { |
| super.setURL(u, protocol, host, port, authority, userInfo, file, query, ref); |
| } |
| |
| @SuppressWarnings("deprecation") |
| public void setURL(URL url, String protocol, String host, int port, String file, String ref) { |
| |
| //using non-deprecated URLStreamHandler.setURL method. |
| //setURL(URL u, String protocol, String host, int port, String authority, String userInfo, String file, String query, String ref) |
| super.setURL(url, protocol, host, port, null, null, file, null, ref); |
| } |
| |
| /** |
| * @see org.osgi.util.tracker.ServiceTrackerCustomizer#addingService(ServiceReference) |
| */ |
| public ServiceReference<URLStreamHandlerService> addingService(ServiceReference<URLStreamHandlerService> reference) { |
| //check to see if our protocol is being registered by another service |
| Object prop = reference.getProperty(URLConstants.URL_HANDLER_PROTOCOL); |
| if (prop instanceof String) { |
| prop = new String[] {(String) prop}; |
| } |
| if (!(prop instanceof String[])) { |
| return null; |
| } |
| String[] protocols = (String[]) prop; |
| for (int i = 0; i < protocols.length; i++) { |
| if (protocols[i].equals(protocol)) { |
| //If our protocol is registered by another service, check the service ranking and switch URLStreamHandlers if nessecary. |
| int newServiceRanking = getRank(reference); |
| if (newServiceRanking > ranking || urlStreamServiceReference == null) |
| setNewHandler(reference, newServiceRanking); |
| return reference; |
| } |
| } |
| |
| //we don't want to continue hearing events about a URLStreamHandlerService not registered under our protocol |
| return null; |
| } |
| |
| /** |
| * @see org.osgi.util.tracker.ServiceTrackerCustomizer#modifiedService(ServiceReference, Object) |
| */ |
| // check to see if the ranking has changed. If so, re-select a new URLHandler |
| public void modifiedService(ServiceReference<URLStreamHandlerService> reference, ServiceReference<URLStreamHandlerService> service) { |
| int newRank = getRank(reference); |
| if (reference == urlStreamServiceReference) { |
| if (newRank < ranking) { |
| // The URLHandler we are currently using has dropped it's ranking below a URLHandler registered |
| // for the same protocol. We need to swap out URLHandlers. |
| // this should get us the highest ranked service, if available |
| ServiceReference<URLStreamHandlerService> newReference = urlStreamHandlerServiceTracker.getServiceReference(); |
| if (newReference != urlStreamServiceReference && newReference != null) { |
| setNewHandler(newReference, ((Integer) newReference.getProperty(Constants.SERVICE_RANKING)).intValue()); |
| } |
| } |
| } else if (newRank > ranking) { |
| // the service changed is another URLHandler that we are not currently using |
| // If it's ranking is higher, we must swap it in. |
| setNewHandler(reference, newRank); |
| } |
| } |
| |
| /** |
| * @see org.osgi.util.tracker.ServiceTrackerCustomizer#removedService(ServiceReference, Object) |
| */ |
| public void removedService(ServiceReference<URLStreamHandlerService> reference, ServiceReference<URLStreamHandlerService> service) { |
| // check to see if our URLStreamHandler was unregistered. |
| if (reference != urlStreamServiceReference) |
| return; |
| // If so, look for a lower ranking URLHandler |
| // this should get us the highest ranking service left, if available |
| ServiceReference<URLStreamHandlerService> newReference = urlStreamHandlerServiceTracker.getServiceReference(); |
| // if newReference == null then we will use the NullURLStreamHandlerService here |
| setNewHandler(newReference, getRank(newReference)); |
| } |
| |
| private int getRank(ServiceReference<?> reference) { |
| if (reference == null) |
| return Integer.MIN_VALUE; |
| Object property = reference.getProperty(Constants.SERVICE_RANKING); |
| return (property instanceof Integer) ? ((Integer) property).intValue() : 0; |
| } |
| |
| protected URLConnection openConnection(URL u, Proxy p) throws IOException { |
| try { |
| Method openConn = realHandlerService.getClass().getMethod("openConnection", new Class[] {URL.class, Proxy.class}); //$NON-NLS-1$ |
| openConn.setAccessible(true); |
| return (URLConnection) openConn.invoke(realHandlerService, new Object[] {u, p}); |
| } catch (InvocationTargetException e) { |
| if (e.getTargetException() instanceof IOException) |
| throw (IOException) e.getTargetException(); |
| throw (RuntimeException) e.getTargetException(); |
| } catch (Exception e) { |
| // expected on JRE < 1.5 |
| throw new UnsupportedOperationException(e); |
| } |
| } |
| } |