| /******************************************************************************* |
| * 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); |
| } |
| } |
| } |