| /******************************************************************************* |
| * 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.io.*; |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import java.util.*; |
| import javax.xml.parsers.*; |
| import org.eclipse.equinox.metatype.EquinoxMetaTypeInformation; |
| import org.eclipse.equinox.metatype.EquinoxMetaTypeService; |
| import org.eclipse.equinox.metatype.impl.Persistence.Reader; |
| import org.eclipse.equinox.metatype.impl.Persistence.Writer; |
| import org.eclipse.osgi.util.NLS; |
| import org.osgi.framework.*; |
| import org.osgi.service.metatype.MetaTypeInformation; |
| import org.osgi.util.tracker.ServiceTracker; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * Implementation of MetaTypeService |
| */ |
| public class MetaTypeServiceImpl implements EquinoxMetaTypeService, SynchronousBundleListener { |
| private static String CACHE_FILE = "metaTypeCache"; //$NON-NLS-1$ |
| SAXParserFactory _parserFactory; |
| private Hashtable<Long, EquinoxMetaTypeInformation> _mtps = new Hashtable<Long, EquinoxMetaTypeInformation>(7); |
| |
| private final LogTracker logger; |
| private final ServiceTracker<Object, Object> metaTypeProviderTracker; |
| |
| /** |
| * Constructor of class MetaTypeServiceImpl. |
| */ |
| public MetaTypeServiceImpl(SAXParserFactory parserFactory, LogTracker logger, ServiceTracker<Object, Object> metaTypeProviderTracker) { |
| this._parserFactory = parserFactory; |
| this.logger = logger; |
| this.metaTypeProviderTracker = metaTypeProviderTracker; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.service.metatype.MetaTypeService#getMetaTypeInformation(org.osgi.framework.Bundle) |
| */ |
| @Override |
| public EquinoxMetaTypeInformation getMetaTypeInformation(Bundle bundle) { |
| return getMetaTypeProvider(bundle); |
| } |
| |
| /** |
| * Internal Method - to get MetaTypeProvider object. |
| */ |
| private EquinoxMetaTypeInformation getMetaTypeProvider(final Bundle b) { |
| // Avoid synthetic accessor method warnings. |
| final LogTracker loggerTemp = this.logger; |
| final ServiceTracker<Object, Object> tracker = this.metaTypeProviderTracker; |
| Long bID = Long.valueOf(b.getBundleId()); |
| synchronized (_mtps) { |
| if (_mtps.containsKey(bID)) |
| return _mtps.get(bID); |
| EquinoxMetaTypeInformation mti = AccessController.doPrivileged(new PrivilegedAction<EquinoxMetaTypeInformation>() { |
| @Override |
| public EquinoxMetaTypeInformation run() { |
| MetaTypeInformationImpl impl = null; |
| try { |
| impl = new MetaTypeInformationImpl(b, newParser(), loggerTemp); |
| } catch (Exception e) { |
| loggerTemp.log(LogTracker.LOG_ERROR, NLS.bind(MetaTypeMsg.METADATA_PARSE_ERROR, b.getBundleId(), b.getSymbolicName()), e); |
| } |
| if (impl == null || !impl._isThereMeta) |
| return new MetaTypeProviderTracker(b, loggerTemp, tracker); |
| return impl; |
| } |
| }); |
| _mtps.put(bID, mti); |
| return mti; |
| } |
| } |
| |
| SAXParser newParser() throws ParserConfigurationException, SAXException { |
| boolean namespaceAware = _parserFactory.isNamespaceAware(); |
| boolean validating = _parserFactory.isValidating(); |
| // Always want a non-validating parser. |
| _parserFactory.setValidating(false); |
| try { |
| // If the factory is already namespace aware, we know it can create namespace aware parsers |
| // because that was checked in the service tracker. |
| if (namespaceAware) { |
| return _parserFactory.newSAXParser(); |
| } |
| // If the factory is not already namespace aware, it may or may not be able to create |
| // namespace aware parsers. |
| _parserFactory.setNamespaceAware(true); |
| try { |
| return _parserFactory.newSAXParser(); |
| } catch (Exception e) { |
| // Factory cannot create namespace aware parsers. Go with the last resort. |
| _parserFactory.setNamespaceAware(false); |
| return _parserFactory.newSAXParser(); |
| } |
| } finally { |
| // Restore the previous settings in all cases. |
| _parserFactory.setNamespaceAware(namespaceAware); |
| _parserFactory.setValidating(validating); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.framework.BundleListener#bundleChanged(org.osgi.framework.BundleEvent) |
| */ |
| public void bundleChanged(BundleEvent event) { |
| |
| int type = event.getType(); |
| Long bID = Long.valueOf(event.getBundle().getBundleId()); |
| |
| switch (type) { |
| case BundleEvent.UPDATED : |
| case BundleEvent.UNINSTALLED : |
| _mtps.remove(bID); |
| break; |
| case BundleEvent.INSTALLED : |
| case BundleEvent.RESOLVED : |
| case BundleEvent.STARTED : |
| case BundleEvent.STOPPED : |
| case BundleEvent.UNRESOLVED : |
| default : |
| break; |
| } |
| } |
| |
| void load(BundleContext context, LogTracker log, ServiceTracker<Object, Object> tracker) throws IOException { |
| File cache = context.getDataFile(CACHE_FILE); |
| // using system context to see all bundles by the ID |
| BundleContext systemContext = context.getBundle(Constants.SYSTEM_BUNDLE_LOCATION).getBundleContext(); |
| if (cache.isFile()) { |
| try (Reader reader = new Reader(new DataInputStream(new BufferedInputStream(new FileInputStream(cache))))) { |
| if (!reader.isValidPersistenceVersion()) { |
| logger.log(LogTracker.LOG_INFO, "Metatype cache version is not supported. Ignoring cache."); //$NON-NLS-1$ |
| return; |
| } |
| int numService = reader.readInt(); |
| for (int i = 0; i < numService; i++) { |
| long id = reader.readLong(); |
| Bundle b = systemContext.getBundle(id); |
| if (b != null) { |
| _mtps.put(b.getBundleId(), new MetaTypeProviderTracker(b, log, tracker)); |
| } |
| } |
| |
| reader.readIndexedStrings(); |
| |
| int numXML = reader.readInt(); |
| for (int i = 0; i < numXML; i++) { |
| MetaTypeInformationImpl info = MetaTypeInformationImpl.load(systemContext, log, reader); |
| if (info != null) { |
| _mtps.put(info.getBundle().getBundleId(), info); |
| } |
| } |
| } |
| } |
| } |
| |
| void save(BundleContext context) throws IOException { |
| File cache = context.getDataFile(CACHE_FILE); |
| try (Writer writer = new Writer(new DataOutputStream(new BufferedOutputStream(new FileOutputStream(cache))))) { |
| writer.writePersistenceVersion(); |
| List<MetaTypeInformation> serviceInfos = new ArrayList<>(); |
| List<MetaTypeInformationImpl> xmlInfos = new ArrayList<>(); |
| synchronized (_mtps) { |
| for (MetaTypeInformation info : _mtps.values()) { |
| if (info instanceof MetaTypeInformationImpl) { |
| xmlInfos.add((MetaTypeInformationImpl) info); |
| } else { |
| serviceInfos.add(info); |
| } |
| } |
| } |
| |
| writer.writeInt(serviceInfos.size()); |
| for (MetaTypeInformation info : serviceInfos) { |
| writer.writeLong(info.getBundle().getBundleId()); |
| } |
| |
| Set<String> strings = new HashSet<>(); |
| for (MetaTypeInformationImpl info : xmlInfos) { |
| info.getStrings(strings); |
| } |
| writer.writeIndexedStrings(strings); |
| |
| writer.writeInt(xmlInfos.size()); |
| for (MetaTypeInformationImpl info : xmlInfos) { |
| info.write(writer); |
| } |
| } |
| } |
| } |