FELIX-3184 Add support for MetaTypeProvider services:
-- track them with ServiceTracker
-- add caching for bundle MetaTypeInformation
-- add livecycle support for MetaTypeInformation
(to be able to clear up the ServiceTrackers)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1401856 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/metatype/src/main/java/org/apache/felix/metatype/internal/MetaTypeInformationImpl.java b/metatype/src/main/java/org/apache/felix/metatype/internal/MetaTypeInformationImpl.java
index 22ac3fe..081b509 100644
--- a/metatype/src/main/java/org/apache/felix/metatype/internal/MetaTypeInformationImpl.java
+++ b/metatype/src/main/java/org/apache/felix/metatype/internal/MetaTypeInformationImpl.java
@@ -22,11 +22,10 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
-import java.util.TreeSet;
-
import org.apache.felix.metatype.DefaultMetaTypeProvider;
import org.apache.felix.metatype.Designate;
import org.apache.felix.metatype.DesignateObject;
@@ -54,24 +53,37 @@
private final Bundle bundle;
- private Set pids;
+ private final Set pids;
- private Set factoryPids;
+ private final Set factoryPids;
+
+ private final Map metaTypeProviders;
+
+ private final MetaTypeProviderTracker providerTacker;
private Set locales;
- private Map metaTypeProviders;
-
-
protected MetaTypeInformationImpl( Bundle bundle )
{
this.bundle = bundle;
- this.pids = new TreeSet();
- this.factoryPids = new TreeSet();
+ this.pids = new HashSet();
+ this.factoryPids = new HashSet();
this.metaTypeProviders = new HashMap();
+
+ this.providerTacker = new MetaTypeProviderTracker( bundle.getBundleContext(), this );
+ this.providerTacker.open();
}
+ void dispose() {
+ this.providerTacker.close();
+
+ this.pids.clear();
+ this.factoryPids.clear();
+ this.locales = null;
+ this.metaTypeProviders.clear();
+ }
+
/*
* (non-Javadoc)
*
@@ -116,7 +128,7 @@
{
synchronized ( this )
{
- Set newLocales = new TreeSet();
+ Set newLocales = new HashSet();
for ( Iterator mi = this.metaTypeProviders.values().iterator(); mi.hasNext(); )
{
MetaTypeProvider mtp = ( MetaTypeProvider ) mi.next();
@@ -219,30 +231,6 @@
}
- protected void addPids( String[] pids )
- {
- this.addValues( this.pids, pids );
- }
-
-
- protected void removePid( String pid )
- {
- this.pids.remove( pid );
- }
-
-
- protected void addFactoryPids( String[] factoryPids )
- {
- this.addValues( this.factoryPids, factoryPids );
- }
-
-
- protected void removeFactoryPid( String factoryPid )
- {
- this.factoryPids.remove( factoryPid );
- }
-
-
protected void addMetaTypeProvider( String key, MetaTypeProvider mtp )
{
if ( key != null && mtp != null )
@@ -265,6 +253,50 @@
}
+ protected void addSingletonMetaTypeProvider( String[] pids, MetaTypeProvider mtp )
+ {
+ this.addValues( this.pids, pids );
+ for ( int i = 0; i < pids.length; i++ )
+ {
+ addMetaTypeProvider( pids[i], mtp );
+ }
+ }
+
+
+ protected void addFactoryMetaTypeProvider( String[] factoryPids, MetaTypeProvider mtp )
+ {
+ this.addValues( this.factoryPids, factoryPids );
+ for ( int i = 0; i < factoryPids.length; i++ )
+ {
+ addMetaTypeProvider( factoryPids[i], mtp );
+ }
+ }
+
+
+ protected boolean removeSingletonMetaTypeProvider( String[] pids )
+ {
+ boolean wasRegistered = false;
+ for ( int i = 0; i < pids.length; i++ )
+ {
+ wasRegistered |= ( removeMetaTypeProvider( pids[i] ) != null );
+ this.pids.remove( pids[i] );
+ }
+ return wasRegistered;
+ }
+
+
+ protected boolean removeFactoryMetaTypeProvider( String[] factoryPids )
+ {
+ boolean wasRegistered = false;
+ for ( int i = 0; i < factoryPids.length; i++ )
+ {
+ wasRegistered |= ( removeMetaTypeProvider( factoryPids[i] ) != null );
+ this.factoryPids.remove( factoryPids[i] );
+ }
+ return wasRegistered;
+ }
+
+
private void addValues( Collection dest, Object[] values )
{
if ( values != null && values.length > 0 )
diff --git a/metatype/src/main/java/org/apache/felix/metatype/internal/MetaTypeProviderTracker.java b/metatype/src/main/java/org/apache/felix/metatype/internal/MetaTypeProviderTracker.java
new file mode 100644
index 0000000..a43bcd6
--- /dev/null
+++ b/metatype/src/main/java/org/apache/felix/metatype/internal/MetaTypeProviderTracker.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.metatype.internal;
+
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.metatype.MetaTypeProvider;
+import org.osgi.util.tracker.ServiceTracker;
+
+
+public class MetaTypeProviderTracker extends ServiceTracker
+{
+
+ final MetaTypeInformationImpl mti;
+
+
+ public MetaTypeProviderTracker( BundleContext context, final MetaTypeInformationImpl mti )
+ {
+ super( context, MetaTypeProvider.class, null );
+ this.mti = mti;
+ }
+
+
+ public Object addingService( ServiceReference reference )
+ {
+ // only care for services of our bundle
+ if ( !this.context.getBundle().equals( reference.getBundle() ) )
+ {
+ return null;
+ }
+
+ MetaTypeProvider mtp = ( MetaTypeProvider ) super.addingService( reference );
+
+ final String[] pids = ServiceMetaTypeInformation.getStringPlus( reference, MetaTypeProvider.METATYPE_PID );
+ if ( pids != null )
+ {
+ mti.addSingletonMetaTypeProvider( pids, mtp );
+ }
+
+ final String[] factoryPids = ServiceMetaTypeInformation.getStringPlus( reference,
+ MetaTypeProvider.METATYPE_FACTORY_PID );
+ if ( factoryPids != null )
+ {
+ mti.addFactoryMetaTypeProvider( factoryPids, mtp );
+ }
+
+ return mtp;
+ }
+
+
+ public void modifiedService( ServiceReference reference, Object service )
+ {
+ // TODO This is tricky: we must know the registration before the update !!
+ super.modifiedService( reference, service );
+ }
+
+
+ public void removedService( ServiceReference reference, Object service )
+ {
+ final String[] pids = ServiceMetaTypeInformation.getStringPlus( reference, MetaTypeProvider.METATYPE_PID );
+ if ( pids != null )
+ {
+ mti.removeSingletonMetaTypeProvider( pids );
+ }
+
+ final String[] factoryPids = ServiceMetaTypeInformation.getStringPlus( reference,
+ MetaTypeProvider.METATYPE_FACTORY_PID );
+ if ( factoryPids != null )
+ {
+ mti.removeFactoryMetaTypeProvider( factoryPids );
+ }
+
+ super.removedService( reference, service );
+ }
+}
diff --git a/metatype/src/main/java/org/apache/felix/metatype/internal/MetaTypeServiceImpl.java b/metatype/src/main/java/org/apache/felix/metatype/internal/MetaTypeServiceImpl.java
index 43f50ec..b516502 100644
--- a/metatype/src/main/java/org/apache/felix/metatype/internal/MetaTypeServiceImpl.java
+++ b/metatype/src/main/java/org/apache/felix/metatype/internal/MetaTypeServiceImpl.java
@@ -1,4 +1,4 @@
-/*
+/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
@@ -22,11 +22,15 @@
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
import org.apache.felix.metatype.MetaData;
import org.apache.felix.metatype.MetaDataReader;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.SynchronousBundleListener;
import org.osgi.service.log.LogService;
import org.osgi.service.metatype.MetaTypeInformation;
import org.osgi.service.metatype.MetaTypeService;
@@ -35,26 +39,63 @@
/**
* The <code>MetaTypeServiceImpl</code> class is the implementation of the
* <code>MetaTypeService</code> interface of the OSGi Metatype Service
- * Specification 1.1.
+ * Specification 1.1.
*
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
-class MetaTypeServiceImpl implements MetaTypeService
+class MetaTypeServiceImpl implements MetaTypeService, SynchronousBundleListener
{
/** The <code>BundleContext</code> of the providing this service. */
private final BundleContext bundleContext;
+ private final Map bundleMetaTypeInformation;
+
+
/**
* Creates an instance of this class.
- *
+ *
* @param bundleContext The <code>BundleContext</code> ultimately used to
- * access services if there are no meta type documents.
+ * access services if there are no meta type documents.
*/
MetaTypeServiceImpl( BundleContext bundleContext )
{
this.bundleContext = bundleContext;
+ this.bundleMetaTypeInformation = new HashMap();
+
+ bundleContext.addBundleListener( this );
+ }
+
+ void dispose() {
+ MetaTypeInformationImpl[] mti;
+ synchronized(bundleMetaTypeInformation) {
+ mti = (MetaTypeInformationImpl[]) this.bundleMetaTypeInformation.values().toArray(new MetaTypeInformationImpl[bundleMetaTypeInformation.values().size()]);
+ this.bundleMetaTypeInformation.clear();
+ }
+
+ for (int i = 0; i < mti.length; i++) {
+ mti[i].dispose();
+ }
+ }
+
+
+ public void bundleChanged( BundleEvent event )
+ {
+ if ( event.getType() == BundleEvent.STOPPING )
+ {
+ MetaTypeInformationImpl mti;
+ synchronized ( this.bundleMetaTypeInformation )
+ {
+ mti = ( MetaTypeInformationImpl ) this.bundleMetaTypeInformation.remove( new Long( event.getBundle()
+ .getBundleId() ) );
+ }
+
+ if ( mti != null )
+ {
+ mti.dispose();
+ }
+ }
}
@@ -65,19 +106,47 @@
* <p>
* According to the specification, the services of the bundle are ignored
* if at least one meta type document exists.
- *
+ *
* @param bundle The <code>Bundle</code> for which a
* <code>MetaTypeInformation</code> is to be returned.
*/
public MetaTypeInformation getMetaTypeInformation( Bundle bundle )
{
- MetaTypeInformation mti = fromDocuments( bundle );
- if ( mti != null )
+ MetaTypeInformation mti;
+ synchronized ( this.bundleMetaTypeInformation )
{
- return mti;
+ mti = ( MetaTypeInformation ) this.bundleMetaTypeInformation.get( new Long( bundle.getBundleId() ) );
}
- return new ServiceMetaTypeInformation( bundleContext, bundle );
+ if ( mti == null )
+ {
+ mti = fromDocuments( bundle );
+ if ( mti == null )
+ {
+ mti = new ServiceMetaTypeInformation( bundleContext, bundle );
+ }
+
+ MetaTypeInformationImpl impl = null;
+ synchronized ( this.bundleMetaTypeInformation )
+ {
+ if ( bundle.getState() == Bundle.ACTIVE )
+ {
+ this.bundleMetaTypeInformation.put( new Long( bundle.getBundleId() ), mti );
+ }
+ else
+ {
+ impl = ( MetaTypeInformationImpl ) mti;
+ mti = null;
+ }
+ }
+
+ if ( impl != null )
+ {
+ impl.dispose();
+ }
+ }
+
+ return mti;
}