Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 563da3d1eb45a927e5933e9e70d12000dd5be0db (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
/*******************************************************************************
 * Copyright (c) 2003, 2016 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.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);
		}
	}
}

Back to the top