Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 17ec1587a3e8409d585e5c3e29a2f764f77fcab6 (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
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/*******************************************************************************
 * 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);
		}
	}
}

Back to the top