blob: 17ec1587a3e8409d585e5c3e29a2f764f77fcab6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2017 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.equinox.metatype.impl;
import java.util.Dictionary;
import java.util.Hashtable;
import javax.xml.parsers.SAXParserFactory;
import org.eclipse.equinox.metatype.EquinoxMetaTypeService;
import org.osgi.framework.*;
import org.osgi.service.cm.ManagedService;
import org.osgi.service.log.LogService;
import org.osgi.service.metatype.MetaTypeProvider;
import org.osgi.service.metatype.MetaTypeService;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
/**
* MetaType Activator
*/
public class Activator implements BundleActivator {
/*
* The following filter guarantees only services meeting the following
* criteria will be tracked.
*
* (1) A ManagedService or ManagedServiceFactory registered with a
* SERVICE_PID property. May also be registered as a MetaTypeProvider.
* (2) A MetaTypeProvider registered with a METATYPE_PID or
* METATYPE_FACTORY_PID property.
*
* Note that it's still necessary to inspect a ManagedService or
* ManagedServiceFactory to ensure it also implements MetaTypeProvider.
*/
private static final String FILTER = "(|(&(" + Constants.OBJECTCLASS + '=' + ManagedService.class.getName() + "*)(" + Constants.SERVICE_PID + "=*))(&(" + Constants.OBJECTCLASS + '=' + MetaTypeProvider.class.getName() + ")(|(" + MetaTypeProvider.METATYPE_PID + "=*)(" + MetaTypeProvider.METATYPE_FACTORY_PID + "=*))))"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
private static final String SERVICE_PID = "org.osgi.impl.service.metatype.MetaTypeService"; //$NON-NLS-1$
private LogTracker logServiceTracker;
// Could be ManagedService, ManagedServiceFactory, or MetaTypeProvider.
// The tracker tracks all services regardless of bundle. Services are
// filtered by bundle later in the MetaTypeProviderTracker class. It may
// therefore be shared among multiple instances of that class.
private ServiceTracker<Object, Object> metaTypeProviderTracker;
private ServiceTracker<SAXParserFactory, SAXParserFactory> saxParserFactoryTracker;
public void start(BundleContext context) throws InvalidSyntaxException {
LogTracker lsTracker;
ServiceTracker<Object, Object> mtpTracker;
ServiceTracker<SAXParserFactory, SAXParserFactory> spfTracker;
Filter filter = context.createFilter(FILTER);
synchronized (this) {
lsTracker = logServiceTracker = new LogTracker(context, System.out);
mtpTracker = metaTypeProviderTracker = new ServiceTracker<Object, Object>(context, filter, null);
spfTracker = saxParserFactoryTracker = new ServiceTracker<SAXParserFactory, SAXParserFactory>(context, SAXParserFactory.class, new SAXParserFactoryTrackerCustomizer(context, lsTracker, mtpTracker));
}
// Do this first to make logging available as early as possible.
lsTracker.open();
lsTracker.log(LogService.LOG_DEBUG, "====== Meta Type Service starting ! ====="); //$NON-NLS-1$
// Do this next to make MetaTypeProviders available as early as possible.
mtpTracker.open();
// Do this last because it may result in the MetaTypeService being registered.
spfTracker.open();
}
public void stop(BundleContext context) {
ServiceTracker<SAXParserFactory, SAXParserFactory> spfTracker;
ServiceTracker<Object, Object> mtpTracker;
LogTracker lsTracker;
synchronized (this) {
spfTracker = saxParserFactoryTracker;
// Set this to null so the SAXParserFactoryTrackerCustomizer knows
// not to register a new MetaTypeService when removedService() is
// called while the tracker is closing.
saxParserFactoryTracker = null;
mtpTracker = metaTypeProviderTracker;
lsTracker = logServiceTracker;
}
lsTracker.log(LogService.LOG_DEBUG, "====== Meta Type Service stopping ! ====="); //$NON-NLS-1$
spfTracker.close();
mtpTracker.close();
// Do this last to leave logging available as long as possible.
lsTracker.close();
}
synchronized ServiceTracker<SAXParserFactory, SAXParserFactory> getSAXParserFactoryTracker() {
return saxParserFactoryTracker;
}
private class SAXParserFactoryTrackerCustomizer implements ServiceTrackerCustomizer<SAXParserFactory, SAXParserFactory> {
private final BundleContext bundleCtx;
private final LogTracker logService;
private final ServiceTracker<Object, Object> mtpTracker;
private MetaTypeServiceImpl metaTypeService;
private ServiceRegistration<?> metaTypeServiceRegistration;
private SAXParserFactory saxParserFactory;
public SAXParserFactoryTrackerCustomizer(BundleContext bundleContext, LogTracker logService, ServiceTracker<Object, Object> metaTypeProviderTracker) {
this.bundleCtx = bundleContext;
this.logService = logService;
this.mtpTracker = metaTypeProviderTracker;
}
public SAXParserFactory addingService(ServiceReference<SAXParserFactory> ref) {
SAXParserFactory parserFactory = bundleCtx.getService(ref);
if (parserFactory == null)
return null;
ServiceRegistration<?> registration = null;
MetaTypeServiceImpl service = null;
SAXParserFactory oldFactory = null;
synchronized (this) {
// No previous factory case. We'll accept anything.
if (saxParserFactory == null) {
// Save this parserFactory as the currently used parserFactory
saxParserFactory = parserFactory;
}
// Nothing to do case. Current factory is explicitly namespace aware.
else if (saxParserFactory.isNamespaceAware()) {
return parserFactory;
} else if (parserFactory.isNamespaceAware() || // Previous factory not set for namespace awareness but the new one is case.
// Now the fun case. Neither factory is set for namespace awareness. Need to see if we're currently using
// a factory incapable of creating namespace aware parsers and, if so, if it can be replaced with the new one.
(!supportsNamespaceAwareness(saxParserFactory) && supportsNamespaceAwareness(parserFactory))) {
oldFactory = saxParserFactory;
saxParserFactory = parserFactory;
registration = metaTypeServiceRegistration;
service = metaTypeService;
}
}
swapFactories(oldFactory, parserFactory, registration, service);
return parserFactory;
}
private void swapFactories(SAXParserFactory oldFactory, SAXParserFactory newFactory, ServiceRegistration<?> registration, MetaTypeServiceImpl service) {
if (oldFactory == null) {
registerMetaTypeService();
return;
}
unregisterMetaTypeService(registration, service);
registerMetaTypeService();
}
public void modifiedService(ServiceReference<SAXParserFactory> ref, SAXParserFactory object) {
// Nothing.
}
public void removedService(ServiceReference<SAXParserFactory> ref, SAXParserFactory object) {
ServiceRegistration<?> registration = null;
MetaTypeServiceImpl service = null;
synchronized (this) {
if (object == saxParserFactory) {
// This means the SAXParserFactory was used to start the MetaTypeService and we need to reset.
saxParserFactory = null;
registration = metaTypeServiceRegistration;
service = metaTypeService;
}
}
if (registration != null) {
// Unregister the MetaType service.
unregisterMetaTypeService(registration, service);
// See if another factory is available
SAXParserFactory factory = findBestPossibleFactory();
// If the factory is null, either the bundle is stopping or there are no
// available services. Either way, we don't want to register the MetaType service.
if (factory != null) {
// We have another parser so let's restart the MetaType service if it hasn't been already.
boolean register = false;
synchronized (this) {
// If not null, something else beat us to the punch.
if (saxParserFactory == null) {
saxParserFactory = factory;
register = true;
}
}
if (register) {
registerMetaTypeService();
}
}
}
bundleCtx.ungetService(ref);
}
private SAXParserFactory findBestPossibleFactory() {
ServiceTracker<SAXParserFactory, SAXParserFactory> tracker = getSAXParserFactoryTracker();
// The tracker will be null if the bundle is stopping.
if (tracker == null)
return null;
SAXParserFactory[] factories = (SAXParserFactory[]) tracker.getServices();
// The factories will be null if there are no services being tracked.
if (factories == null)
return null;
SAXParserFactory result = null;
for (SAXParserFactory factory : factories) {
if (factory.isNamespaceAware()) {
// If the factory is namespace aware, we have exactly what we want.
result = factory;
break;
}
// If no "second best" parser has been found yet, see if this one fits the bill.
if (result == null && supportsNamespaceAwareness(factory)) {
result = factory;
}
}
// If no factories capable of providing namespace aware parsers have been found,
// just grab the first available one, if any.
if (result == null)
result = tracker.getService();
return result;
}
private void registerMetaTypeService() {
Dictionary<String, Object> properties = new Hashtable<String, Object>(7);
properties = new Hashtable<String, Object>(7);
properties.put(Constants.SERVICE_VENDOR, "IBM"); //$NON-NLS-1$
properties.put(Constants.SERVICE_DESCRIPTION, MetaTypeMsg.SERVICE_DESCRIPTION);
properties.put(Constants.SERVICE_PID, SERVICE_PID);
MetaTypeServiceImpl service;
synchronized (this) {
service = metaTypeService = new MetaTypeServiceImpl(saxParserFactory, logService, mtpTracker);
}
bundleCtx.addBundleListener(service);
ServiceRegistration<?> registration = bundleCtx.registerService(new String[] {MetaTypeService.class.getName(), EquinoxMetaTypeService.class.getName()}, service, properties);
synchronized (this) {
metaTypeServiceRegistration = registration;
}
}
private boolean supportsNamespaceAwareness(SAXParserFactory factory) {
if (factory.isNamespaceAware())
return true;
factory.setNamespaceAware(true);
try {
factory.newSAXParser();
return true;
} catch (Exception e) {
return false;
} finally {
// Go back to the original settings.
factory.setNamespaceAware(false);
}
}
private void unregisterMetaTypeService(ServiceRegistration<?> registration, MetaTypeServiceImpl service) {
registration.unregister();
bundleCtx.removeBundleListener(service);
}
}
}