blob: 768cdfc81739c74ca54f9c0e8a903cf11b994ab0 [file] [log] [blame]
/*******************************************************************************
* 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.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();
}
}
}