Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: e63855d1439786544f1ba324688481d43a5c1e32 (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
/*******************************************************************************
 * 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.net.ContentHandler;
import java.net.URLConnection;
import org.osgi.framework.*;
import org.osgi.service.url.URLConstants;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;

/**
 * The ContentHandlerProxy is a ContentHandler that acts as a proxy for registered ContentHandlers.
 * When a ContentHandler is requested from the ContentHandlerFactory and it exists in the service
 * registry, a ContentHandlerProxy is created which will pass all the requests from the requestor to 
 * the real ContentHandler.  We can't return the real ContentHandler from the ContentHandlerFactory
 * because the JVM caches ContentHandlers and therefore would not support a dynamic environment of
 * ContentHandlers being registered and unregistered.
 */
public class ContentHandlerProxy extends ContentHandler implements ServiceTrackerCustomizer<ContentHandler, ServiceReference<ContentHandler>> {
	protected ContentHandler realHandler;

	//TODO avoid type-based names
	protected ServiceTracker<ContentHandler, ServiceReference<ContentHandler>> contentHandlerServiceTracker;

	protected BundleContext context;
	protected ServiceReference<ContentHandler> contentHandlerServiceReference;

	protected String contentType;

	protected int ranking = Integer.MIN_VALUE;

	public ContentHandlerProxy(String contentType, ServiceReference<ContentHandler> reference, BundleContext context) {
		this.context = context;
		this.contentType = contentType;

		// In case the reference == null, the proxy is constructed with DefaultContentHandler for a Content Handler 
		// until a real ContentHandler for this mime-type is registered
		setNewHandler(reference, getRank(reference));

		contentHandlerServiceTracker = new ServiceTracker<>(context, ContentHandler.class.getName(), this);
		URLStreamHandlerFactoryImpl.secureAction.open(contentHandlerServiceTracker);
	}

	private void setNewHandler(ServiceReference<ContentHandler> reference, int rank) {
		if (contentHandlerServiceReference != null)
			context.ungetService(contentHandlerServiceReference);

		contentHandlerServiceReference = reference;
		ranking = rank;

		if (reference == null)
			realHandler = new DefaultContentHandler();
		else
			realHandler = URLStreamHandlerFactoryImpl.secureAction.getService(reference, context);
	}

	/**
	 * @see org.osgi.util.tracker.ServiceTrackerCustomizer#addingService(ServiceReference)
	 */
	public ServiceReference<ContentHandler> addingService(ServiceReference<ContentHandler> reference) {
		//check to see if our contentType is being registered by another service
		Object prop = reference.getProperty(URLConstants.URL_CONTENT_MIMETYPE);
		if (prop instanceof String) {
			prop = new String[] {(String) prop};
		}
		if (!(prop instanceof String[])) {
			return null;
		}
		String[] contentTypes = (String[]) prop;
		for (int i = 0; i < contentTypes.length; i++) {
			if (contentTypes[i].equals(contentType)) {
				//If our contentType is registered by another service, check the service ranking and switch URLStreamHandlers if nessecary.
				int newServiceRanking = getRank(reference);
				if (newServiceRanking > ranking || contentHandlerServiceReference == null)
					setNewHandler(reference, newServiceRanking);
				return (reference);
			}
		}

		//we don't want to continue hearing events about a ContentHandler service not registered under our contentType
		return (null);
	}

	/**
	 * @see org.osgi.util.tracker.ServiceTrackerCustomizer#modifiedService(ServiceReference, Object)
	 */

	public void modifiedService(ServiceReference<ContentHandler> reference, ServiceReference<ContentHandler> service) {
		int newrank = getRank(reference);
		if (reference == contentHandlerServiceReference) {
			if (newrank < ranking) {
				// The ContentHandler we are currently using has dropped it's ranking below a ContentHandler 
				// registered for the same protocol.  We need to swap out ContentHandlers.
				// this should get us the highest ranked service, if available
				ServiceReference<ContentHandler> newReference = contentHandlerServiceTracker.getServiceReference();
				if (newReference != contentHandlerServiceReference && 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<ContentHandler> reference, ServiceReference<ContentHandler> service) {
		//check to see if our URLStreamHandler was unregistered.
		if (reference != contentHandlerServiceReference)
			return;
		// If so, look for a lower ranking URLHandler
		// this should get us the highest ranking service left, if available
		ServiceReference<ContentHandler> newReference = contentHandlerServiceTracker.getServiceReference();
		// if newReference == null then we will use the DefaultContentHandler here
		setNewHandler(newReference, getRank(newReference));
	}

	/**
	 * @see java.net.ContentHandler#getContent(URLConnection)
	 */

	public Object getContent(URLConnection uConn) throws IOException {
		return realHandler.getContent(uConn);
	}

	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;
	}

	class DefaultContentHandler extends ContentHandler {

		/**
		 * @see java.net.ContentHandler#getContent(URLConnection)
		 */
		public Object getContent(URLConnection uConn) throws IOException {
			return uConn.getInputStream();
		}
	}
}

Back to the top