You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by tj...@apache.org on 2019/07/03 16:35:54 UTC

svn commit: r1862491 - in /felix/trunk/scr/src: main/java/org/apache/felix/scr/impl/ main/java/org/apache/felix/scr/impl/config/ main/java/org/apache/felix/scr/impl/manager/ main/java/org/apache/felix/scr/impl/metadata/ test/java/org/apache/felix/scr/i...

Author: tjwatson
Date: Wed Jul  3 16:35:54 2019
New Revision: 1862491

URL: http://svn.apache.org/viewvc?rev=1862491&view=rev
Log:
FELIX-6155 - Add caching of service component metadata

By default the caching is disabled.  The configuration property (or
framework launch property) ds.cache.metadata can be set to the string
"true" to enable the support.  Unfortunately dynamically setting this
with config admin is problematic because the cache is read at activation
time currently.  This could be enhanced to read the cache any time the
value is set to true dynamically.  For now it is required to be set from
the start with a system or framework launch property

Added:
    felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/MetadataStoreHelper.java
Modified:
    felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/Activator.java
    felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java
    felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfigurationImpl.java
    felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ScrConfiguration.java
    felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java
    felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ReferenceMetadata.java
    felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ServiceMetadata.java
    felix/trunk/scr/src/test/java/org/apache/felix/scr/impl/logger/MockScrLogger.java

Modified: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/Activator.java
URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/Activator.java?rev=1862491&r1=1862490&r2=1862491&view=diff
==============================================================================
--- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/Activator.java (original)
+++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/Activator.java Wed Jul  3 16:35:54 2019
@@ -18,9 +18,24 @@
  */
 package org.apache.felix.scr.impl;
 
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
@@ -28,9 +43,14 @@ import java.util.concurrent.locks.Reentr
 import org.apache.felix.scr.impl.config.ScrConfigurationImpl;
 import org.apache.felix.scr.impl.inject.ClassUtils;
 import org.apache.felix.scr.impl.logger.ScrLogger;
+import org.apache.felix.scr.impl.manager.ComponentHolder;
+import org.apache.felix.scr.impl.metadata.ComponentMetadata;
+import org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataReader;
+import org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataWriter;
 import org.apache.felix.scr.impl.runtime.ServiceComponentRuntimeImpl;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.framework.wiring.BundleRevision;
@@ -75,6 +95,8 @@ public class Activator extends AbstractE
 
     private ComponentCommands m_componentCommands;
 
+    private ConcurrentMap<Long, List<ComponentMetadata>> m_componentMetadataStore;
+
     public Activator()
     {
         m_configuration = new ScrConfigurationImpl( this );
@@ -102,6 +124,8 @@ public class Activator extends AbstractE
 
     public void restart(boolean globalExtender)
     {
+        m_componentMetadataStore = load(m_context, logger,
+            m_configuration.cacheMetadata());
         BundleContext context = m_globalContext;
         if ( globalExtender )
         {
@@ -182,6 +206,136 @@ public class Activator extends AbstractE
     {
         super.stop( context );
         m_configuration.stop();
+        store(m_componentMetadataStore, context, logger, m_configuration.cacheMetadata());
+    }
+
+    @Override
+    public void bundleChanged(BundleEvent event)
+    {
+        super.bundleChanged(event);
+        if (event.getType() == BundleEvent.UPDATED
+            || event.getType() == BundleEvent.UNINSTALLED)
+        {
+            m_componentMetadataStore.remove(event.getBundle().getBundleId());
+        }
+    }
+
+    private static ConcurrentMap<Long, List<ComponentMetadata>> load(
+        BundleContext context,
+        ScrLogger logger, boolean loadFromCache)
+    {
+        try
+        {
+            ConcurrentMap<Long, List<ComponentMetadata>> result = new ConcurrentHashMap<>();
+            if (!loadFromCache)
+            {
+                return result;
+            }
+            BundleContext systemContext = context.getBundle(
+                Constants.SYSTEM_BUNDLE_LOCATION).getBundleContext();
+
+            File store = context.getDataFile("componentMetadataStore");
+            if (store.isFile())
+            {
+                try (DataInputStream in = new DataInputStream(
+                    new BufferedInputStream(new FileInputStream(store))))
+                {
+                    MetaDataReader metaDataReader = new MetaDataReader();
+                    int numStrings = in.readInt();
+                    for (int i = 0; i < numStrings; i++)
+                    {
+                        metaDataReader.readIndexedString(in);
+                    }
+                    int numBundles = in.readInt();
+                    for (int i = 0; i < numBundles; i++)
+                    {
+                        // Read all the components for the ID even if the bundle does not exist;
+                        long bundleId = in.readLong();
+                        int numComponents = in.readInt();
+                        long lastModified = in.readLong();
+                        List<ComponentMetadata> components = new ArrayList<>(
+                            numComponents);
+                        for (int j = 0; j < numComponents; j++)
+                        {
+                            components.add(ComponentMetadata.load(in, metaDataReader));
+                        }
+                        // Check with system context by ID to avoid hooks hiding;
+                        Bundle b = systemContext.getBundle(bundleId);
+                        if (b != null)
+                        {
+                            if (lastModified == b.getLastModified())
+                            {
+                                result.put(bundleId, components);
+                            }
+                        }
+                    }
+                }
+                catch (IOException e)
+                {
+                    logger.log(LogService.LOG_WARNING,
+                        "Error loading component metadata cache.", e);
+                }
+            }
+            return result;
+        }
+        catch (RuntimeException re)
+        {
+            // avoid failing all of SCR start on cache load bug
+            logger.log(LogService.LOG_ERROR,
+                "Error loading component metadata cache.", re);
+            return new ConcurrentHashMap<>();
+        }
+
+    }
+
+    private static void store(Map<Long, List<ComponentMetadata>> componentsMap,
+        BundleContext context, ScrLogger logger, boolean storeCache)
+    {
+        if (!storeCache)
+        {
+            return;
+        }
+        BundleContext systemContext = context.getBundle(
+            Constants.SYSTEM_BUNDLE_LOCATION).getBundleContext();
+        File store = context.getDataFile("componentMetadataStore");
+        try (DataOutputStream out = new DataOutputStream(
+            new BufferedOutputStream(new FileOutputStream(store))))
+        {
+            MetaDataWriter metaDataWriter = new MetaDataWriter();
+
+            Set<String> allStrings = new HashSet<>();
+            for (List<ComponentMetadata> components : componentsMap.values())
+            {
+                for (ComponentMetadata component : components)
+                {
+                    component.collectStrings(allStrings);
+                }
+            }
+            // remove possible null
+            allStrings.remove(null);
+            out.writeInt(allStrings.size());
+            for (String s : allStrings)
+            {
+                metaDataWriter.writeIndexedString(s, out);
+            }
+            out.writeInt(componentsMap.size());
+            for (Entry<Long, List<ComponentMetadata>> entry : componentsMap.entrySet())
+            {
+                out.writeLong(entry.getKey());
+                out.writeInt(entry.getValue().size());
+                Bundle b = systemContext.getBundle(entry.getKey());
+                out.writeLong(b == null ? -1 : b.getLastModified());
+                for (ComponentMetadata component : entry.getValue())
+                {
+                    component.store(out, metaDataWriter);
+                }
+            }
+        }
+        catch (IOException e)
+        {
+            logger.log(LogService.LOG_WARNING, "Error storing component metadata cache.",
+                e);
+        }
     }
 
     /**
@@ -312,8 +466,20 @@ public class Activator extends AbstractE
      */
     private void loadComponents(Bundle bundle)
     {
-        if (bundle.getHeaders("").get(ComponentConstants.SERVICE_COMPONENT) == null)
+        final Long bundleId = bundle.getBundleId();
+        List<ComponentMetadata> cached = m_componentMetadataStore.get(bundleId);
+        if (cached != null && cached.isEmpty())
+        {
+            // Cached that there are no components for this bundle.
+            return;
+        }
+
+        if (cached == null
+            && bundle.getHeaders("").get(ComponentConstants.SERVICE_COMPONENT) == null)
         {
+            // Cache that there are no components
+            m_componentMetadataStore.put(bundleId,
+                Collections.<ComponentMetadata> emptyList());
             // no components in the bundle, abandon
             return;
         }
@@ -352,7 +518,6 @@ public class Activator extends AbstractE
         // FELIX-2231 Mark bundle loaded early to prevent concurrent loading
         // if LAZY_ACTIVATION and STARTED event are fired at the same time
         final boolean loaded;
-        final Long bundleId = bundle.getBundleId();
         synchronized ( m_componentBundles )
         {
             if ( m_componentBundles.containsKey( bundleId ) )
@@ -378,9 +543,18 @@ public class Activator extends AbstractE
         try
         {
             BundleComponentActivator ga = new BundleComponentActivator( this.logger, m_componentRegistry, m_componentActor,
-                context, m_configuration );
+                context, m_configuration, cached);
             ga.initialEnable();
-
+            if (cached == null)
+            {
+                List<ComponentHolder<?>> components = ga.getSelectedComponents(null);
+                List<ComponentMetadata> metadatas = new ArrayList<>(components.size());
+                for (ComponentHolder<?> holder : ga.getSelectedComponents(null))
+                {
+                    metadatas.add(holder.getComponentMetadata());
+                }
+                m_componentMetadataStore.put(bundleId, metadatas);
+            }
             // replace bundle activator in the map
             synchronized ( m_componentBundles )
             {
@@ -466,4 +640,4 @@ public class Activator extends AbstractE
             }
         }
     }
-}
\ No newline at end of file
+}

Modified: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java
URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java?rev=1862491&r1=1862490&r2=1862491&view=diff
==============================================================================
--- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java (original)
+++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java Wed Jul  3 16:35:54 2019
@@ -244,7 +244,8 @@ public class BundleComponentActivator im
             final ComponentRegistry componentRegistry,
             final ComponentActorThread componentActor,
             final BundleContext context,
-            final ScrConfiguration configuration)
+            final ScrConfiguration configuration,
+            final List<ComponentMetadata> cachedComponentMetadata)
     throws ComponentException
     {
         // create a logger on behalf of the bundle
@@ -259,14 +260,7 @@ public class BundleComponentActivator im
 
         logger.log( LogService.LOG_DEBUG, "BundleComponentActivator : Bundle active", null);
 
-        // Get the Metadata-Location value from the manifest
-        String descriptorLocations = m_bundle.getHeaders("").get("Service-Component");
-        if ( descriptorLocations == null )
-        {
-            throw new ComponentException( "Service-Component entry not found in the manifest" );
-        }
-
-        initialize( descriptorLocations );
+        initialize(cachedComponentMetadata);
         ConfigAdminTracker tracker = null;
         for ( ComponentHolder<?> holder : m_holders )
         {
@@ -282,36 +276,56 @@ public class BundleComponentActivator im
     /**
      * Gets the MetaData location, parses the meta data and requests the processing
      * of binder instances
-     *
-     * @param descriptorLocations A comma separated list of locations of
-     *      component descriptors. This must not be <code>null</code>.
+     * @param cachedComponentMetadata 
      *
      * @throws IllegalStateException If the bundle has already been uninstalled.
      */
-    protected void initialize(String descriptorLocations)
+    protected void initialize(List<ComponentMetadata> cachedComponentMetadata)
     {
-        logger.log( LogService.LOG_DEBUG, "BundleComponentActivator : Descriptor locations {0}", null, descriptorLocations );
-
-        // 112.4.1: The value of the the header is a comma separated list of XML entries within the Bundle
-        StringTokenizer st = new StringTokenizer( descriptorLocations, ", " );
-
-        while ( st.hasMoreTokens() )
+        if (cachedComponentMetadata != null)
         {
-            String descriptorLocation = st.nextToken();
-
-            URL[] descriptorURLs = findDescriptors( m_bundle, descriptorLocation );
-            if ( descriptorURLs.length == 0 )
+            for (ComponentMetadata metadata : cachedComponentMetadata)
             {
-                // 112.4.1 If an XML document specified by the header cannot be located in the bundle and its attached
-                // fragments, SCR must log an error message with the Log Service, if present, and continue.
-                logger.log( LogService.LOG_ERROR, "Component descriptor entry ''{0}'' not found", null, descriptorLocation);
-                continue;
+                validateAndRegister(metadata);
             }
+        }
+        else
+        {
+            // Get the Metadata-Location value from the manifest
+            String descriptorLocations = m_bundle.getHeaders("").get("Service-Component");
+            if (descriptorLocations == null)
+            {
+                throw new ComponentException(
+                    "Service-Component entry not found in the manifest");
+            }
+
+            logger.log(LogService.LOG_DEBUG,
+                "BundleComponentActivator : Descriptor locations {0}", null,
+                descriptorLocations);
 
-            // load from the descriptors
-            for ( URL descriptorURL : descriptorURLs )
+            // 112.4.1: The value of the the header is a comma separated list of XML entries within the Bundle
+            StringTokenizer st = new StringTokenizer(descriptorLocations, ", ");
+
+            while (st.hasMoreTokens())
             {
-                loadDescriptor( descriptorURL );
+                String descriptorLocation = st.nextToken();
+
+                URL[] descriptorURLs = findDescriptors(m_bundle, descriptorLocation);
+                if (descriptorURLs.length == 0)
+                {
+                    // 112.4.1 If an XML document specified by the header cannot be located in the bundle and its attached
+                    // fragments, SCR must log an error message with the Log Service, if present, and continue.
+                    logger.log(LogService.LOG_ERROR,
+                        "Component descriptor entry ''{0}'' not found", null,
+                        descriptorLocation);
+                    continue;
+                }
+
+                // load from the descriptors
+                for (URL descriptorURL : descriptorURLs)
+                {
+                    loadDescriptor(descriptorURL);
+                }
             }
         }
     }
@@ -431,39 +445,7 @@ public class BundleComponentActivator im
             // or one or more component elements embedded in a larger document
             for ( ComponentMetadata metadata : handler.getComponentMetadataList() )
             {
-                final ComponentLogger componentLogger = new ComponentLogger(metadata, logger);
-                ComponentRegistryKey key = null;
-                try
-                {
-                    // validate the component metadata
-                    metadata.validate( );
-
-                    // check and reserve the component name (validate ensures it's never null)
-                    key = m_componentRegistry.checkComponentName( m_bundle, metadata.getName() );
-
-                    // Request creation of the component manager
-                    ComponentHolder<?> holder = m_componentRegistry.createComponentHolder( this, metadata, componentLogger );
-
-                    // register the component after validation
-                    m_componentRegistry.registerComponentHolder( key, holder );
-                    m_holders.add( holder );
-
-                    componentLogger.log( LogService.LOG_DEBUG,
-                        "BundleComponentActivator : ComponentHolder created.", null );
-
-                }
-                catch ( Throwable t )
-                {
-                    // There is a problem with this particular component, we'll log the error
-                    // and proceed to the next one
-                    componentLogger.log( LogService.LOG_ERROR, "Cannot register component", t );
-
-                    // make sure the name is not reserved any more
-                    if ( key != null )
-                    {
-                        m_componentRegistry.unregisterComponentHolder( key );
-                    }
-                }
+                validateAndRegister(metadata);
             }
         }
         catch ( IOException ex )
@@ -492,6 +474,44 @@ public class BundleComponentActivator im
         }
     }
 
+    void validateAndRegister(ComponentMetadata metadata)
+    {
+        final ComponentLogger componentLogger = new ComponentLogger(metadata, logger);
+        ComponentRegistryKey key = null;
+        try
+        {
+            // validate the component metadata
+            metadata.validate();
+
+            // check and reserve the component name (validate ensures it's never null)
+            key = m_componentRegistry.checkComponentName(m_bundle, metadata.getName());
+
+            // Request creation of the component manager
+            ComponentHolder<?> holder = m_componentRegistry.createComponentHolder(this,
+                metadata, componentLogger);
+
+            // register the component after validation
+            m_componentRegistry.registerComponentHolder(key, holder);
+            m_holders.add(holder);
+
+            componentLogger.log(LogService.LOG_DEBUG,
+                "BundleComponentActivator : ComponentHolder created.", null);
+
+        }
+        catch (Throwable t)
+        {
+            // There is a problem with this particular component, we'll log the error
+            // and proceed to the next one
+            componentLogger.log(LogService.LOG_ERROR, "Cannot register component", t);
+
+            // make sure the name is not reserved any more
+            if (key != null)
+            {
+                m_componentRegistry.unregisterComponentHolder(key);
+            }
+        }
+    }
+
     /**
     * Dispose of this component activator instance and all the component
     * managers.
@@ -649,7 +669,7 @@ public class BundleComponentActivator im
      *      to the <code>name</code> parameter or <code>null</code> if no
      *      component manager with the given name is currently registered.
      */
-    private List<ComponentHolder<?>> getSelectedComponents(String name)
+    List<ComponentHolder<?>> getSelectedComponents(String name)
     {
         // if all components are selected
         if ( name == null )

Modified: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfigurationImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfigurationImpl.java?rev=1862491&r1=1862490&r2=1862491&view=diff
==============================================================================
--- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfigurationImpl.java (original)
+++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfigurationImpl.java Wed Jul  3 16:35:54 2019
@@ -81,6 +81,8 @@ public class ScrConfigurationImpl implem
 
     private boolean infoAsService;
 
+    private boolean cacheMetadata;
+
     private long lockTimeout = DEFAULT_LOCK_TIMEOUT_MILLISECONDS;
 
     private long stopTimeout = DEFAULT_STOP_TIMEOUT_MILLISECONDS;
@@ -175,6 +177,7 @@ public class ScrConfigurationImpl implem
                         stopTimeout = DEFAULT_STOP_TIMEOUT_MILLISECONDS;
                         serviceChangecountTimeout = DEFAULT_SERVICE_CHANGECOUNT_TIMEOUT_MILLISECONDS;
                         newGlobalExtender = false;
+                        cacheMetadata = false;
                     }
                     else
                     {
@@ -186,6 +189,7 @@ public class ScrConfigurationImpl implem
                         stopTimeout = getDefaultStopTimeout();
                         serviceChangecountTimeout = getServiceChangecountTimeout();
                         newGlobalExtender = getDefaultGlobalExtender();
+                        cacheMetadata = getDefaultCacheMetadata();
                     }
                 }
                 else
@@ -204,6 +208,8 @@ public class ScrConfigurationImpl implem
                 timeout = ( Long ) config.get( PROP_STOP_TIMEOUT );
                 stopTimeout = timeout == null? DEFAULT_STOP_TIMEOUT_MILLISECONDS: timeout;
                 newGlobalExtender = VALUE_TRUE.equalsIgnoreCase( String.valueOf( config.get( PROP_GLOBAL_EXTENDER) ) );
+                cacheMetadata = VALUE_TRUE.equalsIgnoreCase(
+                    String.valueOf(config.get(PROP_CACHE_METADATA)));
             }
             if ( scrCommand != null )
             {
@@ -268,6 +274,12 @@ public class ScrConfigurationImpl implem
     }
 
     @Override
+    public boolean cacheMetadata()
+    {
+        return cacheMetadata;
+    }
+
+    @Override
     public long serviceChangecountTimeout()
     {
         return serviceChangecountTimeout;
@@ -330,6 +342,12 @@ public class ScrConfigurationImpl implem
         return VALUE_TRUE.equalsIgnoreCase( bundleContext.getProperty( PROP_GLOBAL_EXTENDER) );
     }
 
+    private boolean getDefaultCacheMetadata()
+    {
+        return VALUE_TRUE.equalsIgnoreCase(
+            bundleContext.getProperty(PROP_CACHE_METADATA));
+    }
+
     private int getLogLevel( final Object levelObject )
     {
         if ( levelObject != null )

Modified: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ScrConfiguration.java
URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ScrConfiguration.java?rev=1862491&r1=1862490&r2=1862491&view=diff
==============================================================================
--- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ScrConfiguration.java (original)
+++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ScrConfiguration.java Wed Jul  3 16:35:54 2019
@@ -68,6 +68,8 @@ public interface ScrConfiguration
 
     String PROP_SERVICE_CHANGECOUNT_TIMEOUT = "ds.service.changecount.timeout";
 
+    String PROP_CACHE_METADATA = "ds.cache.metadata";
+
     /**
      * Returns the current log level.
      * @return
@@ -97,4 +99,6 @@ public interface ScrConfiguration
      */
     long serviceChangecountTimeout();
 
+    boolean cacheMetadata();
+
 }

Modified: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java
URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java?rev=1862491&r1=1862490&r2=1862491&view=diff
==============================================================================
--- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java (original)
+++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java Wed Jul  3 16:35:54 2019
@@ -18,6 +18,11 @@
  */
 package org.apache.felix.scr.impl.metadata;
 
+import static org.apache.felix.scr.impl.metadata.MetadataStoreHelper.addString;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -25,9 +30,12 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.TreeSet;
 
+import org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataReader;
+import org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataWriter;
 import org.apache.felix.scr.impl.metadata.ServiceMetadata.Scope;
 import org.osgi.service.component.ComponentException;
 
@@ -1084,4 +1092,548 @@ public class ComponentMetadata
     {
         return new ComponentException( "Component " + getName() + " validation failed: " + reason );
     }
+
+    public void collectStrings(Set<String> strings)
+    {
+        addString(m_dsVersion.toString(), strings);
+        addString(m_activate, strings);
+        if (m_activationFields != null)
+        {
+            for (String s : m_activationFields)
+            {
+                addString(s, strings);
+            }
+        }
+        if (m_configurationPid != null)
+        {
+            for (String s : m_configurationPid)
+            {
+                addString(s, strings);
+            }
+        }
+        addString(m_configurationPolicy, strings);
+        addString(m_deactivate, strings);
+        addString(m_factory, strings);
+        addString(m_implementationClassName, strings);
+        addString(m_init, strings);
+        addString(m_modified, strings);
+        addString(m_name, strings);
+        for (Entry<String, Object> entry : m_factoryProperties.entrySet())
+        {
+            collectStrings(entry, strings);
+        }
+        for (Entry<String, Object> entry : m_properties.entrySet())
+        {
+            collectStrings(entry, strings);
+        }
+        for (ReferenceMetadata rMeta : m_references)
+        {
+            rMeta.collectStrings(strings);
+        }
+        if (m_service != null)
+        {
+            m_service.collectStrings(strings);
+        }
+    }
+
+    private void collectStrings(Entry<String, Object> entry, Set<String> strings)
+    {
+        addString(entry.getKey(), strings);
+        Object v = entry.getValue();
+        if (v instanceof String)
+        {
+            addString((String) v, strings);
+        }
+        else if (v instanceof String[])
+        {
+            for (String s : (String[]) v)
+            {
+                addString(s, strings);
+            }
+        }
+    }
+
+    public void store(DataOutputStream out, MetaDataWriter metaDataWriter)
+        throws IOException
+    {
+        metaDataWriter.writeString(m_dsVersion.toString(), out);
+        metaDataWriter.writeString(m_activate, out);
+        out.writeBoolean(m_activationFields != null);
+        if (m_activationFields != null)
+        {
+            out.writeInt(m_activationFields.size());
+            for (String s : m_activationFields)
+            {
+                metaDataWriter.writeString(s, out);
+            }
+        }
+        out.writeBoolean(m_configurationPid != null);
+        if (m_configurationPid != null)
+        {
+            out.writeInt(m_configurationPid.size());
+            for (String s : m_configurationPid)
+            {
+                metaDataWriter.writeString(s, out);
+            }
+        }
+        metaDataWriter.writeString(m_configurationPolicy, out);
+        metaDataWriter.writeString(m_deactivate, out);
+        metaDataWriter.writeString(m_factory, out);
+        metaDataWriter.writeString(m_implementationClassName, out);
+        metaDataWriter.writeString(m_init, out);
+        metaDataWriter.writeString(m_modified, out);
+        metaDataWriter.writeString(m_name, out);
+        out.writeInt(m_factoryProperties.size());
+        for (Entry<String, Object> prop : m_factoryProperties.entrySet())
+        {
+            metaDataWriter.writeString(prop.getKey(), out);
+            storePropertyValue(prop.getValue(), out, metaDataWriter);
+        }
+        out.writeInt(m_properties.size());
+        for (Entry<String, Object> prop : m_properties.entrySet())
+        {
+            metaDataWriter.writeString(prop.getKey(), out);
+            storePropertyValue(prop.getValue(), out, metaDataWriter);
+        }
+        out.writeInt(m_references.size());
+        for (ReferenceMetadata rMeta : m_references)
+        {
+            rMeta.store(out, metaDataWriter);
+        }
+        out.writeBoolean(m_service != null);
+        if (m_service != null)
+        {
+            m_service.store(out, metaDataWriter);
+        }
+        out.writeBoolean(m_activateDeclared);
+        out.writeBoolean(m_configurableServiceProperties);
+        out.writeBoolean(m_configureWithInterfaces);
+        out.writeBoolean(m_deactivateDeclared);
+        out.writeBoolean(m_delayedKeepInstances);
+        out.writeBoolean(m_deleteCallsModify);
+        out.writeBoolean(m_enabled);
+        out.writeBoolean(m_immediate != null);
+        if (m_immediate != null)
+        {
+            out.writeBoolean(m_immediate.booleanValue());
+        }
+    }
+
+    public static ComponentMetadata load(DataInputStream in,
+        MetaDataReader metaDataReader)
+        throws IOException
+    {
+        ComponentMetadata result = new ComponentMetadata(
+            DSVersion.valueOf(metaDataReader.readString(in)));
+        result.m_activate = metaDataReader.readString(in);
+        if (in.readBoolean())
+        {
+            int size = in.readInt();
+            String[] activationFields = new String[size];
+            for (int i = 0; i < size; i++)
+            {
+                activationFields[i] = metaDataReader.readString(in);
+            }
+            result.setActivationFields(activationFields);
+        }
+        if (in.readBoolean())
+        {
+            int size = in.readInt();
+            String[] configPids = new String[size];
+            for (int i = 0; i < size; i++)
+            {
+                configPids[i] = metaDataReader.readString(in);
+            }
+            result.setConfigurationPid(configPids);
+        }
+        result.m_configurationPolicy = metaDataReader.readString(in);
+        result.m_deactivate = metaDataReader.readString(in);
+        result.m_factory = metaDataReader.readString(in);
+        result.m_implementationClassName = metaDataReader.readString(in);
+        result.m_init = metaDataReader.readString(in);
+        result.m_modified = metaDataReader.readString(in);
+        result.m_name = metaDataReader.readString(in);
+        int numFProps = in.readInt();
+        for (int i = 0; i < numFProps; i++)
+        {
+            result.m_factoryProperties.put(metaDataReader.readString(in),
+                loadPropertyValue(in, metaDataReader));
+        }
+        int numProps = in.readInt();
+        for (int i = 0; i < numProps; i++)
+        {
+            result.m_properties.put(metaDataReader.readString(in),
+                loadPropertyValue(in, metaDataReader));
+        }
+        int numRefs = in.readInt();
+        for (int i = 0; i < numRefs; i++)
+        {
+            result.addDependency(ReferenceMetadata.load(in, metaDataReader));
+        }
+        if (in.readBoolean())
+        {
+            result.m_service = ServiceMetadata.load(in, metaDataReader);
+        }
+        result.m_activateDeclared = in.readBoolean();
+        result.m_configurableServiceProperties = in.readBoolean();
+        result.m_configureWithInterfaces = in.readBoolean();
+        result.m_deactivateDeclared = in.readBoolean();
+        result.m_delayedKeepInstances = in.readBoolean();
+        result.m_deleteCallsModify = in.readBoolean();
+        result.m_enabled = in.readBoolean();
+        if (in.readBoolean())
+        {
+            result.m_immediate = in.readBoolean();
+        }
+        // we only store valid metadata
+        result.m_validated = true;
+        return result;
+    }
+
+    private static final byte TypeString = 1;
+    private static final byte TypeLong = 2;
+    private static final byte TypeDouble = 3;
+    private static final byte TypeFloat = 4;
+    private static final byte TypeInteger = 5;
+    private static final byte TypeByte = 6;
+    private static final byte TypeChar = 7;
+    private static final byte TypeBoolean = 8;
+    private static final byte TypeShort = 9;
+
+    static Object loadPropertyValue(DataInputStream in, MetaDataReader metaDataReader)
+        throws IOException
+    {
+        boolean isArray = in.readBoolean();
+        byte valueType = in.readByte();
+        switch (valueType)
+        {
+            case TypeBoolean:
+                if (isArray)
+                {
+                    boolean[] vArray = new boolean[in.readInt()];
+                    for (int i = 0; i < vArray.length; i++)
+                    {
+                        vArray[i] = in.readBoolean();
+                    }
+                    return vArray;
+                }
+                else
+                {
+                    return Boolean.valueOf(in.readBoolean());
+                }
+            case TypeByte:
+                if (isArray)
+                {
+                    byte[] vArray = new byte[in.readInt()];
+                    for (int i = 0; i < vArray.length; i++)
+                    {
+                        vArray[i] = in.readByte();
+                    }
+                    return vArray;
+                }
+                else
+                {
+                    return Byte.valueOf(in.readByte());
+                }
+            case TypeChar:
+                if (isArray)
+                {
+                    char[] vArray = new char[in.readInt()];
+                    for (int i = 0; i < vArray.length; i++)
+                    {
+                        vArray[i] = in.readChar();
+                    }
+                    return vArray;
+                }
+                else
+                {
+                    return Character.valueOf(in.readChar());
+                }
+            case TypeDouble:
+                if (isArray)
+                {
+                    double[] vArray = new double[in.readInt()];
+                    for (int i = 0; i < vArray.length; i++)
+                    {
+                        vArray[i] = in.readDouble();
+                    }
+                    return vArray;
+                }
+                else
+                {
+                    return Double.valueOf(in.readDouble());
+                }
+            case TypeFloat:
+                if (isArray)
+                {
+                    float[] vArray = new float[in.readInt()];
+                    for (int i = 0; i < vArray.length; i++)
+                    {
+                        vArray[i] = in.readFloat();
+                    }
+                    return vArray;
+                }
+                else
+                {
+                    return Float.valueOf(in.readFloat());
+                }
+            case TypeInteger:
+                if (isArray)
+                {
+                    int[] vArray = new int[in.readInt()];
+                    for (int i = 0; i < vArray.length; i++)
+                    {
+                        vArray[i] = in.readInt();
+                    }
+                    return vArray;
+                }
+                else
+                {
+                    return Integer.valueOf(in.readInt());
+                }
+            case TypeLong:
+                if (isArray)
+                {
+                    long[] vArray = new long[in.readInt()];
+                    for (int i = 0; i < vArray.length; i++)
+                    {
+                        vArray[i] = in.readLong();
+                    }
+                    return vArray;
+                }
+                else
+                {
+                    return Long.valueOf(in.readLong());
+                }
+            case TypeShort:
+                if (isArray)
+                {
+                    short[] vArray = new short[in.readInt()];
+                    for (int i = 0; i < vArray.length; i++)
+                    {
+                        vArray[i] = in.readShort();
+                    }
+                    return vArray;
+                }
+                else
+                {
+                    return Short.valueOf(in.readShort());
+                }
+            case TypeString:
+                if (isArray)
+                {
+                    String[] vArray = new String[in.readInt()];
+                    for (int i = 0; i < vArray.length; i++)
+                    {
+                        vArray[i] = metaDataReader.readString(in);
+                    }
+                    return vArray;
+                }
+                else
+                {
+                    return metaDataReader.readString(in);
+                }
+        }
+        return null;
+    }
+
+    void storePropertyValue(Object value, DataOutputStream out,
+        MetaDataWriter metaDataWriter) throws IOException
+    {
+        if (value == null)
+        {
+            // handle null value as a string
+            out.writeBoolean(false);
+            out.writeByte(TypeString);
+            metaDataWriter.writeString(null, out);
+            return;
+        }
+        Class<?> arrayType = value.getClass().getComponentType();
+        boolean isArray = arrayType != null;
+        out.writeBoolean(isArray);
+        byte valueType = getType(arrayType == null ? value.getClass() : arrayType);
+        out.writeByte(valueType);
+        switch (valueType)
+        {
+            case TypeBoolean:
+                if (isArray)
+                {
+                    boolean[] vArray = (boolean[]) value;
+                    out.writeInt(vArray.length);
+                    for (boolean v : vArray)
+                    {
+                        out.writeBoolean(v);
+                    }
+                }
+                else
+                {
+                    out.writeBoolean((boolean) value);
+                }
+                break;
+            case TypeByte:
+                if (isArray)
+                {
+                    byte[] vArray = (byte[]) value;
+                    out.writeInt(vArray.length);
+                    for (byte v : vArray)
+                    {
+                        out.writeByte(v);
+                    }
+                }
+                else
+                {
+                    out.writeByte((byte) value);
+                }
+                break;
+            case TypeChar:
+                if (isArray)
+                {
+                    char[] vArray = (char[]) value;
+                    out.writeInt(vArray.length);
+                    for (char v : vArray)
+                    {
+                        out.writeChar(v);
+                    }
+                }
+                else
+                {
+                    out.writeChar((char) value);
+                }
+                break;
+            case TypeDouble:
+                if (isArray)
+                {
+                    double[] vArray = (double[]) value;
+                    out.writeInt(vArray.length);
+                    for (double v : vArray)
+                    {
+                        out.writeDouble(v);
+                    }
+                }
+                else
+                {
+                    out.writeDouble((double) value);
+                }
+                break;
+            case TypeFloat:
+                if (isArray)
+                {
+                    float[] vArray = (float[]) value;
+                    out.writeInt(vArray.length);
+                    for (float v : vArray)
+                    {
+                        out.writeFloat(v);
+                    }
+                }
+                else
+                {
+                    out.writeFloat((float) value);
+                }
+                break;
+            case TypeInteger:
+                if (isArray)
+                {
+                    int[] vArray = (int[]) value;
+                    out.writeInt(vArray.length);
+                    for (int v : vArray)
+                    {
+                        out.writeInt(v);
+                    }
+                }
+                else
+                {
+                    out.writeInt((int) value);
+                }
+                break;
+            case TypeLong:
+                if (isArray)
+                {
+                    long[] vArray = (long[]) value;
+                    out.writeInt(vArray.length);
+                    for (long v : vArray)
+                    {
+                        out.writeLong(v);
+                    }
+                }
+                else
+                {
+                    out.writeLong((long) value);
+                }
+                break;
+            case TypeShort:
+                if (isArray)
+                {
+                    short[] vArray = (short[]) value;
+                    out.writeInt(vArray.length);
+                    for (short v : vArray)
+                    {
+                        out.writeShort(v);
+                    }
+                }
+                else
+                {
+                    out.writeShort((short) value);
+                }
+                break;
+            case TypeString:
+                if (isArray)
+                {
+                    String[] vArray = (String[]) value;
+                    out.writeInt(vArray.length);
+                    for (String v : vArray)
+                    {
+                        metaDataWriter.writeString(v, out);
+                    }
+
+                }
+                else
+                {
+                    metaDataWriter.writeString((String) value, out);
+                }
+                break;
+        }
+    }
+
+    private byte getType(Class<?> typeClass)
+    {
+        if (Boolean.class.equals(typeClass) || boolean.class.equals(typeClass))
+        {
+            return TypeBoolean;
+        }
+        if (Byte.class.equals(typeClass) || byte.class.equals(typeClass))
+        {
+            return TypeByte;
+        }
+        if (Character.class.equals(typeClass) || char.class.equals(typeClass))
+        {
+            return TypeChar;
+        }
+        if (Double.class.equals(typeClass) || double.class.equals(typeClass))
+        {
+            return TypeDouble;
+        }
+        if (Float.class.equals(typeClass) || float.class.equals(typeClass))
+        {
+            return TypeFloat;
+        }
+        if (Integer.class.equals(typeClass) || int.class.equals(typeClass))
+        {
+            return TypeInteger;
+        }
+        if (Long.class.equals(typeClass) || long.class.equals(typeClass))
+        {
+            return TypeLong;
+        }
+        if (Short.class.equals(typeClass) || short.class.equals(typeClass))
+        {
+            return TypeShort;
+        }
+        if (String.class.equals(typeClass))
+        {
+            return TypeString;
+        }
+        throw new IllegalArgumentException("Unsupported type: " + typeClass);
+
+    }
+
 }

Added: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/MetadataStoreHelper.java
URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/MetadataStoreHelper.java?rev=1862491&view=auto
==============================================================================
--- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/MetadataStoreHelper.java (added)
+++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/MetadataStoreHelper.java Wed Jul  3 16:35:54 2019
@@ -0,0 +1,160 @@
+/*
+ * 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.scr.impl.metadata;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class MetadataStoreHelper
+{
+    static final byte STRING_NULL = 0;
+    static final byte STRING_OBJECT = 1;
+    static final byte STRING_INDEX = 2;
+    static final byte STRING_LONG = 3;
+
+    public static class MetaDataReader
+    {
+        private final List<String> stringTable = new ArrayList<String>();
+
+        public String readIndexedString(DataInputStream in) throws IOException
+        {
+            String s = readString(in);
+            addToStringTable(s, in.readInt());
+            return s;
+        }
+
+        public String readString(DataInputStream in) throws IOException
+        {
+            byte type = in.readByte();
+            if (type == STRING_INDEX)
+            {
+                int index = in.readInt();
+                return (String) stringTable.get(index);
+            }
+            if (type == STRING_NULL)
+            {
+                return null;
+            }
+            String s;
+            if (type == STRING_LONG)
+            {
+                int length = in.readInt();
+                byte[] data = new byte[length];
+                in.readFully(data);
+                s = new String(data, "UTF-8");
+            }
+            else
+            {
+                s = in.readUTF();
+            }
+            return s;
+        }
+
+        private void addToStringTable(String s, int index)
+        {
+            if (index == stringTable.size())
+            {
+                stringTable.add(s);
+            }
+            else if (index < stringTable.size())
+            {
+                stringTable.set(index, s);
+            }
+            else
+            {
+                while (stringTable.size() < index)
+                {
+                    stringTable.add(null);
+                }
+                stringTable.add(s);
+            }
+        }
+    }
+
+    public static class MetaDataWriter
+    {
+        private final Map<String, Integer> stringTable = new HashMap<>();
+
+        public void writeIndexedString(String s, DataOutputStream out) throws IOException
+        {
+            writeString(s, out);
+            out.writeInt(addToStringTable(s));
+        }
+
+        public void writeString(String s, DataOutputStream out) throws IOException
+        {
+            Integer index = s != null ? stringTable.get(s) : null;
+            if (index != null)
+            {
+                out.writeByte(STRING_INDEX);
+                out.writeInt(index);
+                return;
+            }
+
+            if (s == null)
+                out.writeByte(STRING_NULL);
+            else
+            {
+                byte[] data = s.getBytes("UTF-8");
+
+                if (data.length > 65535)
+                {
+                    out.writeByte(STRING_LONG);
+                    out.writeInt(data.length);
+                    out.write(data);
+                }
+                else
+                {
+                    out.writeByte(STRING_OBJECT);
+                    out.writeUTF(s);
+                }
+            }
+        }
+
+        private int addToStringTable(String s)
+        {
+            if (s == null)
+            {
+                throw new NullPointerException();
+            }
+            Integer cur = stringTable.get(s);
+            if (cur != null)
+                throw new IllegalStateException(
+                    "String is already in the write table: " + s);
+            int index = stringTable.size();
+            stringTable.put(s, Integer.valueOf(index));
+            // return the index of the object just added
+            return index;
+        }
+    }
+
+    public static void addString(String s, Set<String> strings)
+    {
+        if (s != null)
+        {
+            strings.add(s);
+        }
+    }
+}

Modified: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ReferenceMetadata.java
URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ReferenceMetadata.java?rev=1862491&r1=1862490&r2=1862491&view=diff
==============================================================================
--- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ReferenceMetadata.java (original)
+++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ReferenceMetadata.java Wed Jul  3 16:35:54 2019
@@ -18,9 +18,17 @@
  */
 package org.apache.felix.scr.impl.metadata;
 
+import static org.apache.felix.scr.impl.metadata.MetadataStoreHelper.addString;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
 import java.util.Set;
 import java.util.TreeSet;
 
+import org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataReader;
+import org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataWriter;
+
 /**
  * Information associated to a dependency
  *
@@ -824,4 +832,86 @@ public class ReferenceMetadata
                 ", collection-type=" + this.getFieldCollectionType() +
                 ", parameter=" + this.getParameterIndex();
     }
+
+    void collectStrings(Set<String> strings)
+    {
+        addString(m_bind, strings);
+        addString(m_cardinality, strings);
+        addString(m_collection_type, strings);
+        addString(m_field, strings);
+        addString(m_field_option, strings);
+        addString(m_interface, strings);
+        addString(m_name, strings);
+        addString(m_parameter, strings);
+        addString(m_policy, strings);
+        addString(m_policy_option, strings);
+        addString(m_scopeName, strings);
+        addString(m_scope.toString(), strings);
+        addString(m_target, strings);
+        addString(m_unbind, strings);
+        addString(m_updated, strings);
+    }
+
+    void store(DataOutputStream out, MetaDataWriter metaDataWriter) throws IOException
+    {
+        metaDataWriter.writeString(m_bind, out);
+        metaDataWriter.writeString(m_cardinality, out);
+        metaDataWriter.writeString(m_collection_type, out);
+        metaDataWriter.writeString(m_field, out);
+        metaDataWriter.writeString(m_field_option, out);
+        metaDataWriter.writeString(m_interface, out);
+        out.writeBoolean(m_isMultiple);
+        out.writeBoolean(m_isOptional);
+        out.writeBoolean(m_isReluctant);
+        out.writeBoolean(m_isReplace);
+        out.writeBoolean(m_isStatic);
+        metaDataWriter.writeString(m_name, out);
+        metaDataWriter.writeString(m_parameter, out);
+        out.writeBoolean(m_parameterIndex != null);
+        if (m_parameterIndex != null)
+        {
+            out.writeInt(m_parameterIndex.intValue());
+        }
+        metaDataWriter.writeString(m_policy, out);
+        metaDataWriter.writeString(m_policy_option, out);
+        metaDataWriter.writeString(m_scopeName, out);
+        metaDataWriter.writeString(m_scope.toString(), out);
+        metaDataWriter.writeString(m_target, out);
+        metaDataWriter.writeString(m_unbind, out);
+        metaDataWriter.writeString(m_updated, out);
+    }
+
+    static ReferenceMetadata load(DataInputStream in, MetaDataReader metaDataReader)
+        throws IOException
+    {
+        ReferenceMetadata result = new ReferenceMetadata();
+        result.m_bind = metaDataReader.readString(in);
+        result.m_cardinality = metaDataReader.readString(in);
+        result.m_collection_type = metaDataReader.readString(in);
+        result.m_field = metaDataReader.readString(in);
+        result.m_field_option = metaDataReader.readString(in);
+        result.m_interface = metaDataReader.readString(in);
+        result.m_isMultiple = in.readBoolean();
+        result.m_isOptional = in.readBoolean();
+        result.m_isReluctant = in.readBoolean();
+        result.m_isReplace = in.readBoolean();
+        result.m_isStatic = in.readBoolean();
+        result.m_name = metaDataReader.readString(in);
+        result.m_parameter = metaDataReader.readString(in);
+        if (in.readBoolean())
+        {
+            result.m_parameterIndex = Integer.valueOf(in.readInt());
+        }
+        result.m_policy = metaDataReader.readString(in);
+        result.m_policy_option = metaDataReader.readString(in);
+        result.m_scopeName = metaDataReader.readString(in);
+        result.m_scope = ReferenceScope.valueOf(metaDataReader.readString(in));
+        result.m_target = metaDataReader.readString(in);
+        result.m_unbind = metaDataReader.readString(in);
+        result.m_updated = metaDataReader.readString(in);
+
+        // only stored valid metadata
+        result.m_validated = true;
+        return result;
+    }
 }
\ No newline at end of file

Modified: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ServiceMetadata.java
URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ServiceMetadata.java?rev=1862491&r1=1862490&r2=1862491&view=diff
==============================================================================
--- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ServiceMetadata.java (original)
+++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ServiceMetadata.java Wed Jul  3 16:35:54 2019
@@ -18,8 +18,17 @@
  */
 package org.apache.felix.scr.impl.metadata;
 
+import static org.apache.felix.scr.impl.metadata.MetadataStoreHelper.addString;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
+
+import org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataReader;
+import org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataWriter;
 
 /**
  * This class contains the metadata associated to a service that is provided
@@ -134,4 +143,50 @@ public class ServiceMetadata {
         }
         m_validated = true;
     }
+
+    void collectStrings(Set<String> strings)
+    {
+        for (String s : m_provides)
+        {
+            addString(s, strings);
+        }
+        addString(m_scopeName, strings);
+        addString(m_scope.toString(), strings);
+    }
+
+    void store(DataOutputStream out, MetaDataWriter metaDataWriter) throws IOException
+    {
+        out.writeInt(m_provides.size());
+        for (String s : m_provides)
+        {
+            metaDataWriter.writeString(s, out);
+        }
+        metaDataWriter.writeString(m_scopeName, out);
+        metaDataWriter.writeString(m_scope.toString(), out);
+        out.writeBoolean(m_serviceFactory != null);
+        if (m_serviceFactory != null)
+        {
+            out.writeBoolean(m_serviceFactory.booleanValue());
+        }
+    }
+
+    static ServiceMetadata load(DataInputStream in, MetaDataReader metaDataReader)
+        throws IOException
+    {
+        ServiceMetadata result = new ServiceMetadata();
+        int providerSize = in.readInt();
+        for (int i = 0; i < providerSize; i++)
+        {
+            result.addProvide(metaDataReader.readString(in));
+        }
+        result.m_scopeName = metaDataReader.readString(in);
+        result.m_scope = Scope.valueOf(metaDataReader.readString(in));
+        if (in.readBoolean())
+        {
+            result.m_serviceFactory = in.readBoolean();
+        }
+        // only stored valid metadata
+        result.m_validated = true;
+        return result;
+    }
 }

Modified: felix/trunk/scr/src/test/java/org/apache/felix/scr/impl/logger/MockScrLogger.java
URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/test/java/org/apache/felix/scr/impl/logger/MockScrLogger.java?rev=1862491&r1=1862490&r2=1862491&view=diff
==============================================================================
--- felix/trunk/scr/src/test/java/org/apache/felix/scr/impl/logger/MockScrLogger.java (original)
+++ felix/trunk/scr/src/test/java/org/apache/felix/scr/impl/logger/MockScrLogger.java Wed Jul  3 16:35:54 2019
@@ -78,6 +78,12 @@ public class MockScrLogger extends ScrLo
             public boolean globalExtender() {
                 return false;
             }
+
+            @Override
+            public boolean cacheMetadata()
+            {
+                return false;
+            }
         }, new MockBundleContext(new MockBundle()));
     }
 }
\ No newline at end of file



Re: svn commit: r1862491 - in /felix/trunk/scr/src: main/java/org/apache/felix/scr/impl/ main/java/org/apache/felix/scr/impl/config/ main/java/org/apache/felix/scr/impl/manager/ main/java/org/apache/felix/scr/impl/metadata/ test/java/org/apache/felix/scr/i...

Posted by Thomas Watson <tj...@gmail.com>.
Hi David,

Yes, I should have added a version number as the first entry into the
serialized data.  I will add one, thanks for the catch!

I have not compared to using some other more standard format.  I worry
about json in particular because then I need a json parser, and then I also
need to convert the string representation from the json values into the
proper data types used in DS metadata.  I admit that I have not dealt with
json parsing much, but I have doubts that would come for free and it would
make the file larger requiring more IO.  Here I am trying to get all the
possible gain I can and even losing 10 ms would not be worth it to me for
my purposes.

Tom.

On Wed, Jul 3, 2019 at 3:20 PM David Jencks <da...@gmail.com>
wrote:

> Would it be a good idea to start the serialized metadata file with a
> version number so when you discover a better format old format data can be
> ignored rather than causing a read error?
>
> How much speedup and size reduction do you get from this binary format
> compared to say one json file as the cache?
>
> Thanks
> David Jencks
>
> Sent from my iPhone
>
> > On Jul 3, 2019, at 9:35 AM, tjwatson@apache.org wrote:
> >
> > Author: tjwatson
> > Date: Wed Jul  3 16:35:54 2019
> > New Revision: 1862491
> >
> > URL: http://svn.apache.org/viewvc?rev=1862491&view=rev
> > Log:
> > FELIX-6155 - Add caching of service component metadata
> >
> > By default the caching is disabled.  The configuration property (or
> > framework launch property) ds.cache.metadata can be set to the string
> > "true" to enable the support.  Unfortunately dynamically setting this
> > with config admin is problematic because the cache is read at activation
> > time currently.  This could be enhanced to read the cache any time the
> > value is set to true dynamically.  For now it is required to be set from
> > the start with a system or framework launch property
> >
> > Added:
> >
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/MetadataStoreHelper.java
> > Modified:
> >    felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/Activator.java
> >
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java
> >
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfigurationImpl.java
> >
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ScrConfiguration.java
> >
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java
> >
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ReferenceMetadata.java
> >
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ServiceMetadata.java
> >
> felix/trunk/scr/src/test/java/org/apache/felix/scr/impl/logger/MockScrLogger.java
> >
> > Modified:
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/Activator.java
> > URL:
> http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/Activator.java?rev=1862491&r1=1862490&r2=1862491&view=diff
> >
> ==============================================================================
> > ---
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/Activator.java
> (original)
> > +++
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/Activator.java Wed
> Jul  3 16:35:54 2019
> > @@ -18,9 +18,24 @@
> >  */
> > package org.apache.felix.scr.impl;
> >
> > +import java.io.BufferedInputStream;
> > +import java.io.BufferedOutputStream;
> > +import java.io.DataInputStream;
> > +import java.io.DataOutputStream;
> > +import java.io.File;
> > +import java.io.FileInputStream;
> > +import java.io.FileOutputStream;
> > +import java.io.IOException;
> > +import java.util.ArrayList;
> > +import java.util.Collections;
> > import java.util.HashMap;
> > +import java.util.HashSet;
> > import java.util.List;
> > import java.util.Map;
> > +import java.util.Map.Entry;
> > +import java.util.Set;
> > +import java.util.concurrent.ConcurrentHashMap;
> > +import java.util.concurrent.ConcurrentMap;
> > import java.util.concurrent.TimeUnit;
> > import java.util.concurrent.locks.Lock;
> > import java.util.concurrent.locks.ReentrantLock;
> > @@ -28,9 +43,14 @@ import java.util.concurrent.locks.Reentr
> > import org.apache.felix.scr.impl.config.ScrConfigurationImpl;
> > import org.apache.felix.scr.impl.inject.ClassUtils;
> > import org.apache.felix.scr.impl.logger.ScrLogger;
> > +import org.apache.felix.scr.impl.manager.ComponentHolder;
> > +import org.apache.felix.scr.impl.metadata.ComponentMetadata;
> > +import
> org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataReader;
> > +import
> org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataWriter;
> > import org.apache.felix.scr.impl.runtime.ServiceComponentRuntimeImpl;
> > import org.osgi.framework.Bundle;
> > import org.osgi.framework.BundleContext;
> > +import org.osgi.framework.BundleEvent;
> > import org.osgi.framework.Constants;
> > import org.osgi.framework.ServiceRegistration;
> > import org.osgi.framework.wiring.BundleRevision;
> > @@ -75,6 +95,8 @@ public class Activator extends AbstractE
> >
> >     private ComponentCommands m_componentCommands;
> >
> > +    private ConcurrentMap<Long, List<ComponentMetadata>>
> m_componentMetadataStore;
> > +
> >     public Activator()
> >     {
> >         m_configuration = new ScrConfigurationImpl( this );
> > @@ -102,6 +124,8 @@ public class Activator extends AbstractE
> >
> >     public void restart(boolean globalExtender)
> >     {
> > +        m_componentMetadataStore = load(m_context, logger,
> > +            m_configuration.cacheMetadata());
> >         BundleContext context = m_globalContext;
> >         if ( globalExtender )
> >         {
> > @@ -182,6 +206,136 @@ public class Activator extends AbstractE
> >     {
> >         super.stop( context );
> >         m_configuration.stop();
> > +        store(m_componentMetadataStore, context, logger,
> m_configuration.cacheMetadata());
> > +    }
> > +
> > +    @Override
> > +    public void bundleChanged(BundleEvent event)
> > +    {
> > +        super.bundleChanged(event);
> > +        if (event.getType() == BundleEvent.UPDATED
> > +            || event.getType() == BundleEvent.UNINSTALLED)
> > +        {
> > +
> m_componentMetadataStore.remove(event.getBundle().getBundleId());
> > +        }
> > +    }
> > +
> > +    private static ConcurrentMap<Long, List<ComponentMetadata>> load(
> > +        BundleContext context,
> > +        ScrLogger logger, boolean loadFromCache)
> > +    {
> > +        try
> > +        {
> > +            ConcurrentMap<Long, List<ComponentMetadata>> result = new
> ConcurrentHashMap<>();
> > +            if (!loadFromCache)
> > +            {
> > +                return result;
> > +            }
> > +            BundleContext systemContext = context.getBundle(
> > +                Constants.SYSTEM_BUNDLE_LOCATION).getBundleContext();
> > +
> > +            File store = context.getDataFile("componentMetadataStore");
> > +            if (store.isFile())
> > +            {
> > +                try (DataInputStream in = new DataInputStream(
> > +                    new BufferedInputStream(new
> FileInputStream(store))))
> > +                {
> > +                    MetaDataReader metaDataReader = new
> MetaDataReader();
> > +                    int numStrings = in.readInt();
> > +                    for (int i = 0; i < numStrings; i++)
> > +                    {
> > +                        metaDataReader.readIndexedString(in);
> > +                    }
> > +                    int numBundles = in.readInt();
> > +                    for (int i = 0; i < numBundles; i++)
> > +                    {
> > +                        // Read all the components for the ID even if
> the bundle does not exist;
> > +                        long bundleId = in.readLong();
> > +                        int numComponents = in.readInt();
> > +                        long lastModified = in.readLong();
> > +                        List<ComponentMetadata> components = new
> ArrayList<>(
> > +                            numComponents);
> > +                        for (int j = 0; j < numComponents; j++)
> > +                        {
> > +                            components.add(ComponentMetadata.load(in,
> metaDataReader));
> > +                        }
> > +                        // Check with system context by ID to avoid
> hooks hiding;
> > +                        Bundle b = systemContext.getBundle(bundleId);
> > +                        if (b != null)
> > +                        {
> > +                            if (lastModified == b.getLastModified())
> > +                            {
> > +                                result.put(bundleId, components);
> > +                            }
> > +                        }
> > +                    }
> > +                }
> > +                catch (IOException e)
> > +                {
> > +                    logger.log(LogService.LOG_WARNING,
> > +                        "Error loading component metadata cache.", e);
> > +                }
> > +            }
> > +            return result;
> > +        }
> > +        catch (RuntimeException re)
> > +        {
> > +            // avoid failing all of SCR start on cache load bug
> > +            logger.log(LogService.LOG_ERROR,
> > +                "Error loading component metadata cache.", re);
> > +            return new ConcurrentHashMap<>();
> > +        }
> > +
> > +    }
> > +
> > +    private static void store(Map<Long, List<ComponentMetadata>>
> componentsMap,
> > +        BundleContext context, ScrLogger logger, boolean storeCache)
> > +    {
> > +        if (!storeCache)
> > +        {
> > +            return;
> > +        }
> > +        BundleContext systemContext = context.getBundle(
> > +            Constants.SYSTEM_BUNDLE_LOCATION).getBundleContext();
> > +        File store = context.getDataFile("componentMetadataStore");
> > +        try (DataOutputStream out = new DataOutputStream(
> > +            new BufferedOutputStream(new FileOutputStream(store))))
> > +        {
> > +            MetaDataWriter metaDataWriter = new MetaDataWriter();
> > +
> > +            Set<String> allStrings = new HashSet<>();
> > +            for (List<ComponentMetadata> components :
> componentsMap.values())
> > +            {
> > +                for (ComponentMetadata component : components)
> > +                {
> > +                    component.collectStrings(allStrings);
> > +                }
> > +            }
> > +            // remove possible null
> > +            allStrings.remove(null);
> > +            out.writeInt(allStrings.size());
> > +            for (String s : allStrings)
> > +            {
> > +                metaDataWriter.writeIndexedString(s, out);
> > +            }
> > +            out.writeInt(componentsMap.size());
> > +            for (Entry<Long, List<ComponentMetadata>> entry :
> componentsMap.entrySet())
> > +            {
> > +                out.writeLong(entry.getKey());
> > +                out.writeInt(entry.getValue().size());
> > +                Bundle b = systemContext.getBundle(entry.getKey());
> > +                out.writeLong(b == null ? -1 : b.getLastModified());
> > +                for (ComponentMetadata component : entry.getValue())
> > +                {
> > +                    component.store(out, metaDataWriter);
> > +                }
> > +            }
> > +        }
> > +        catch (IOException e)
> > +        {
> > +            logger.log(LogService.LOG_WARNING, "Error storing component
> metadata cache.",
> > +                e);
> > +        }
> >     }
> >
> >     /**
> > @@ -312,8 +466,20 @@ public class Activator extends AbstractE
> >      */
> >     private void loadComponents(Bundle bundle)
> >     {
> > -        if
> (bundle.getHeaders("").get(ComponentConstants.SERVICE_COMPONENT) == null)
> > +        final Long bundleId = bundle.getBundleId();
> > +        List<ComponentMetadata> cached =
> m_componentMetadataStore.get(bundleId);
> > +        if (cached != null && cached.isEmpty())
> > +        {
> > +            // Cached that there are no components for this bundle.
> > +            return;
> > +        }
> > +
> > +        if (cached == null
> > +            &&
> bundle.getHeaders("").get(ComponentConstants.SERVICE_COMPONENT) == null)
> >         {
> > +            // Cache that there are no components
> > +            m_componentMetadataStore.put(bundleId,
> > +                Collections.<ComponentMetadata> emptyList());
> >             // no components in the bundle, abandon
> >             return;
> >         }
> > @@ -352,7 +518,6 @@ public class Activator extends AbstractE
> >         // FELIX-2231 Mark bundle loaded early to prevent concurrent
> loading
> >         // if LAZY_ACTIVATION and STARTED event are fired at the same
> time
> >         final boolean loaded;
> > -        final Long bundleId = bundle.getBundleId();
> >         synchronized ( m_componentBundles )
> >         {
> >             if ( m_componentBundles.containsKey( bundleId ) )
> > @@ -378,9 +543,18 @@ public class Activator extends AbstractE
> >         try
> >         {
> >             BundleComponentActivator ga = new BundleComponentActivator(
> this.logger, m_componentRegistry, m_componentActor,
> > -                context, m_configuration );
> > +                context, m_configuration, cached);
> >             ga.initialEnable();
> > -
> > +            if (cached == null)
> > +            {
> > +                List<ComponentHolder<?>> components =
> ga.getSelectedComponents(null);
> > +                List<ComponentMetadata> metadatas = new
> ArrayList<>(components.size());
> > +                for (ComponentHolder<?> holder :
> ga.getSelectedComponents(null))
> > +                {
> > +                    metadatas.add(holder.getComponentMetadata());
> > +                }
> > +                m_componentMetadataStore.put(bundleId, metadatas);
> > +            }
> >             // replace bundle activator in the map
> >             synchronized ( m_componentBundles )
> >             {
> > @@ -466,4 +640,4 @@ public class Activator extends AbstractE
> >             }
> >         }
> >     }
> > -}
> > \ No newline at end of file
> > +}
> >
> > Modified:
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java
> > URL:
> http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java?rev=1862491&r1=1862490&r2=1862491&view=diff
> >
> ==============================================================================
> > ---
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java
> (original)
> > +++
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java
> Wed Jul  3 16:35:54 2019
> > @@ -244,7 +244,8 @@ public class BundleComponentActivator im
> >             final ComponentRegistry componentRegistry,
> >             final ComponentActorThread componentActor,
> >             final BundleContext context,
> > -            final ScrConfiguration configuration)
> > +            final ScrConfiguration configuration,
> > +            final List<ComponentMetadata> cachedComponentMetadata)
> >     throws ComponentException
> >     {
> >         // create a logger on behalf of the bundle
> > @@ -259,14 +260,7 @@ public class BundleComponentActivator im
> >
> >         logger.log( LogService.LOG_DEBUG, "BundleComponentActivator :
> Bundle active", null);
> >
> > -        // Get the Metadata-Location value from the manifest
> > -        String descriptorLocations =
> m_bundle.getHeaders("").get("Service-Component");
> > -        if ( descriptorLocations == null )
> > -        {
> > -            throw new ComponentException( "Service-Component entry not
> found in the manifest" );
> > -        }
> > -
> > -        initialize( descriptorLocations );
> > +        initialize(cachedComponentMetadata);
> >         ConfigAdminTracker tracker = null;
> >         for ( ComponentHolder<?> holder : m_holders )
> >         {
> > @@ -282,36 +276,56 @@ public class BundleComponentActivator im
> >     /**
> >      * Gets the MetaData location, parses the meta data and requests the
> processing
> >      * of binder instances
> > -     *
> > -     * @param descriptorLocations A comma separated list of locations of
> > -     *      component descriptors. This must not be <code>null</code>.
> > +     * @param cachedComponentMetadata
> >      *
> >      * @throws IllegalStateException If the bundle has already been
> uninstalled.
> >      */
> > -    protected void initialize(String descriptorLocations)
> > +    protected void initialize(List<ComponentMetadata>
> cachedComponentMetadata)
> >     {
> > -        logger.log( LogService.LOG_DEBUG, "BundleComponentActivator :
> Descriptor locations {0}", null, descriptorLocations );
> > -
> > -        // 112.4.1: The value of the the header is a comma separated
> list of XML entries within the Bundle
> > -        StringTokenizer st = new StringTokenizer( descriptorLocations,
> ", " );
> > -
> > -        while ( st.hasMoreTokens() )
> > +        if (cachedComponentMetadata != null)
> >         {
> > -            String descriptorLocation = st.nextToken();
> > -
> > -            URL[] descriptorURLs = findDescriptors( m_bundle,
> descriptorLocation );
> > -            if ( descriptorURLs.length == 0 )
> > +            for (ComponentMetadata metadata : cachedComponentMetadata)
> >             {
> > -                // 112.4.1 If an XML document specified by the header
> cannot be located in the bundle and its attached
> > -                // fragments, SCR must log an error message with the
> Log Service, if present, and continue.
> > -                logger.log( LogService.LOG_ERROR, "Component descriptor
> entry ''{0}'' not found", null, descriptorLocation);
> > -                continue;
> > +                validateAndRegister(metadata);
> >             }
> > +        }
> > +        else
> > +        {
> > +            // Get the Metadata-Location value from the manifest
> > +            String descriptorLocations =
> m_bundle.getHeaders("").get("Service-Component");
> > +            if (descriptorLocations == null)
> > +            {
> > +                throw new ComponentException(
> > +                    "Service-Component entry not found in the
> manifest");
> > +            }
> > +
> > +            logger.log(LogService.LOG_DEBUG,
> > +                "BundleComponentActivator : Descriptor locations {0}",
> null,
> > +                descriptorLocations);
> >
> > -            // load from the descriptors
> > -            for ( URL descriptorURL : descriptorURLs )
> > +            // 112.4.1: The value of the the header is a comma
> separated list of XML entries within the Bundle
> > +            StringTokenizer st = new
> StringTokenizer(descriptorLocations, ", ");
> > +
> > +            while (st.hasMoreTokens())
> >             {
> > -                loadDescriptor( descriptorURL );
> > +                String descriptorLocation = st.nextToken();
> > +
> > +                URL[] descriptorURLs = findDescriptors(m_bundle,
> descriptorLocation);
> > +                if (descriptorURLs.length == 0)
> > +                {
> > +                    // 112.4.1 If an XML document specified by the
> header cannot be located in the bundle and its attached
> > +                    // fragments, SCR must log an error message with
> the Log Service, if present, and continue.
> > +                    logger.log(LogService.LOG_ERROR,
> > +                        "Component descriptor entry ''{0}'' not found",
> null,
> > +                        descriptorLocation);
> > +                    continue;
> > +                }
> > +
> > +                // load from the descriptors
> > +                for (URL descriptorURL : descriptorURLs)
> > +                {
> > +                    loadDescriptor(descriptorURL);
> > +                }
> >             }
> >         }
> >     }
> > @@ -431,39 +445,7 @@ public class BundleComponentActivator im
> >             // or one or more component elements embedded in a larger
> document
> >             for ( ComponentMetadata metadata :
> handler.getComponentMetadataList() )
> >             {
> > -                final ComponentLogger componentLogger = new
> ComponentLogger(metadata, logger);
> > -                ComponentRegistryKey key = null;
> > -                try
> > -                {
> > -                    // validate the component metadata
> > -                    metadata.validate( );
> > -
> > -                    // check and reserve the component name (validate
> ensures it's never null)
> > -                    key = m_componentRegistry.checkComponentName(
> m_bundle, metadata.getName() );
> > -
> > -                    // Request creation of the component manager
> > -                    ComponentHolder<?> holder =
> m_componentRegistry.createComponentHolder( this, metadata, componentLogger
> );
> > -
> > -                    // register the component after validation
> > -                    m_componentRegistry.registerComponentHolder( key,
> holder );
> > -                    m_holders.add( holder );
> > -
> > -                    componentLogger.log( LogService.LOG_DEBUG,
> > -                        "BundleComponentActivator : ComponentHolder
> created.", null );
> > -
> > -                }
> > -                catch ( Throwable t )
> > -                {
> > -                    // There is a problem with this particular
> component, we'll log the error
> > -                    // and proceed to the next one
> > -                    componentLogger.log( LogService.LOG_ERROR, "Cannot
> register component", t );
> > -
> > -                    // make sure the name is not reserved any more
> > -                    if ( key != null )
> > -                    {
> > -                        m_componentRegistry.unregisterComponentHolder(
> key );
> > -                    }
> > -                }
> > +                validateAndRegister(metadata);
> >             }
> >         }
> >         catch ( IOException ex )
> > @@ -492,6 +474,44 @@ public class BundleComponentActivator im
> >         }
> >     }
> >
> > +    void validateAndRegister(ComponentMetadata metadata)
> > +    {
> > +        final ComponentLogger componentLogger = new
> ComponentLogger(metadata, logger);
> > +        ComponentRegistryKey key = null;
> > +        try
> > +        {
> > +            // validate the component metadata
> > +            metadata.validate();
> > +
> > +            // check and reserve the component name (validate ensures
> it's never null)
> > +            key = m_componentRegistry.checkComponentName(m_bundle,
> metadata.getName());
> > +
> > +            // Request creation of the component manager
> > +            ComponentHolder<?> holder =
> m_componentRegistry.createComponentHolder(this,
> > +                metadata, componentLogger);
> > +
> > +            // register the component after validation
> > +            m_componentRegistry.registerComponentHolder(key, holder);
> > +            m_holders.add(holder);
> > +
> > +            componentLogger.log(LogService.LOG_DEBUG,
> > +                "BundleComponentActivator : ComponentHolder created.",
> null);
> > +
> > +        }
> > +        catch (Throwable t)
> > +        {
> > +            // There is a problem with this particular component, we'll
> log the error
> > +            // and proceed to the next one
> > +            componentLogger.log(LogService.LOG_ERROR, "Cannot register
> component", t);
> > +
> > +            // make sure the name is not reserved any more
> > +            if (key != null)
> > +            {
> > +                m_componentRegistry.unregisterComponentHolder(key);
> > +            }
> > +        }
> > +    }
> > +
> >     /**
> >     * Dispose of this component activator instance and all the component
> >     * managers.
> > @@ -649,7 +669,7 @@ public class BundleComponentActivator im
> >      *      to the <code>name</code> parameter or <code>null</code> if no
> >      *      component manager with the given name is currently
> registered.
> >      */
> > -    private List<ComponentHolder<?>> getSelectedComponents(String name)
> > +    List<ComponentHolder<?>> getSelectedComponents(String name)
> >     {
> >         // if all components are selected
> >         if ( name == null )
> >
> > Modified:
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfigurationImpl.java
> > URL:
> http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfigurationImpl.java?rev=1862491&r1=1862490&r2=1862491&view=diff
> >
> ==============================================================================
> > ---
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfigurationImpl.java
> (original)
> > +++
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfigurationImpl.java
> Wed Jul  3 16:35:54 2019
> > @@ -81,6 +81,8 @@ public class ScrConfigurationImpl implem
> >
> >     private boolean infoAsService;
> >
> > +    private boolean cacheMetadata;
> > +
> >     private long lockTimeout = DEFAULT_LOCK_TIMEOUT_MILLISECONDS;
> >
> >     private long stopTimeout = DEFAULT_STOP_TIMEOUT_MILLISECONDS;
> > @@ -175,6 +177,7 @@ public class ScrConfigurationImpl implem
> >                         stopTimeout = DEFAULT_STOP_TIMEOUT_MILLISECONDS;
> >                         serviceChangecountTimeout =
> DEFAULT_SERVICE_CHANGECOUNT_TIMEOUT_MILLISECONDS;
> >                         newGlobalExtender = false;
> > +                        cacheMetadata = false;
> >                     }
> >                     else
> >                     {
> > @@ -186,6 +189,7 @@ public class ScrConfigurationImpl implem
> >                         stopTimeout = getDefaultStopTimeout();
> >                         serviceChangecountTimeout =
> getServiceChangecountTimeout();
> >                         newGlobalExtender = getDefaultGlobalExtender();
> > +                        cacheMetadata = getDefaultCacheMetadata();
> >                     }
> >                 }
> >                 else
> > @@ -204,6 +208,8 @@ public class ScrConfigurationImpl implem
> >                 timeout = ( Long ) config.get( PROP_STOP_TIMEOUT );
> >                 stopTimeout = timeout == null?
> DEFAULT_STOP_TIMEOUT_MILLISECONDS: timeout;
> >                 newGlobalExtender = VALUE_TRUE.equalsIgnoreCase(
> String.valueOf( config.get( PROP_GLOBAL_EXTENDER) ) );
> > +                cacheMetadata = VALUE_TRUE.equalsIgnoreCase(
> > +                    String.valueOf(config.get(PROP_CACHE_METADATA)));
> >             }
> >             if ( scrCommand != null )
> >             {
> > @@ -268,6 +274,12 @@ public class ScrConfigurationImpl implem
> >     }
> >
> >     @Override
> > +    public boolean cacheMetadata()
> > +    {
> > +        return cacheMetadata;
> > +    }
> > +
> > +    @Override
> >     public long serviceChangecountTimeout()
> >     {
> >         return serviceChangecountTimeout;
> > @@ -330,6 +342,12 @@ public class ScrConfigurationImpl implem
> >         return VALUE_TRUE.equalsIgnoreCase( bundleContext.getProperty(
> PROP_GLOBAL_EXTENDER) );
> >     }
> >
> > +    private boolean getDefaultCacheMetadata()
> > +    {
> > +        return VALUE_TRUE.equalsIgnoreCase(
> > +            bundleContext.getProperty(PROP_CACHE_METADATA));
> > +    }
> > +
> >     private int getLogLevel( final Object levelObject )
> >     {
> >         if ( levelObject != null )
> >
> > Modified:
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ScrConfiguration.java
> > URL:
> http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ScrConfiguration.java?rev=1862491&r1=1862490&r2=1862491&view=diff
> >
> ==============================================================================
> > ---
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ScrConfiguration.java
> (original)
> > +++
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ScrConfiguration.java
> Wed Jul  3 16:35:54 2019
> > @@ -68,6 +68,8 @@ public interface ScrConfiguration
> >
> >     String PROP_SERVICE_CHANGECOUNT_TIMEOUT =
> "ds.service.changecount.timeout";
> >
> > +    String PROP_CACHE_METADATA = "ds.cache.metadata";
> > +
> >     /**
> >      * Returns the current log level.
> >      * @return
> > @@ -97,4 +99,6 @@ public interface ScrConfiguration
> >      */
> >     long serviceChangecountTimeout();
> >
> > +    boolean cacheMetadata();
> > +
> > }
> >
> > Modified:
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java
> > URL:
> http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java?rev=1862491&r1=1862490&r2=1862491&view=diff
> >
> ==============================================================================
> > ---
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java
> (original)
> > +++
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java
> Wed Jul  3 16:35:54 2019
> > @@ -18,6 +18,11 @@
> >  */
> > package org.apache.felix.scr.impl.metadata;
> >
> > +import static
> org.apache.felix.scr.impl.metadata.MetadataStoreHelper.addString;
> > +
> > +import java.io.DataInputStream;
> > +import java.io.DataOutputStream;
> > +import java.io.IOException;
> > import java.util.ArrayList;
> > import java.util.Arrays;
> > import java.util.Collections;
> > @@ -25,9 +30,12 @@ import java.util.HashMap;
> > import java.util.HashSet;
> > import java.util.List;
> > import java.util.Map;
> > +import java.util.Map.Entry;
> > import java.util.Set;
> > import java.util.TreeSet;
> >
> > +import
> org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataReader;
> > +import
> org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataWriter;
> > import org.apache.felix.scr.impl.metadata.ServiceMetadata.Scope;
> > import org.osgi.service.component.ComponentException;
> >
> > @@ -1084,4 +1092,548 @@ public class ComponentMetadata
> >     {
> >         return new ComponentException( "Component " + getName() + "
> validation failed: " + reason );
> >     }
> > +
> > +    public void collectStrings(Set<String> strings)
> > +    {
> > +        addString(m_dsVersion.toString(), strings);
> > +        addString(m_activate, strings);
> > +        if (m_activationFields != null)
> > +        {
> > +            for (String s : m_activationFields)
> > +            {
> > +                addString(s, strings);
> > +            }
> > +        }
> > +        if (m_configurationPid != null)
> > +        {
> > +            for (String s : m_configurationPid)
> > +            {
> > +                addString(s, strings);
> > +            }
> > +        }
> > +        addString(m_configurationPolicy, strings);
> > +        addString(m_deactivate, strings);
> > +        addString(m_factory, strings);
> > +        addString(m_implementationClassName, strings);
> > +        addString(m_init, strings);
> > +        addString(m_modified, strings);
> > +        addString(m_name, strings);
> > +        for (Entry<String, Object> entry :
> m_factoryProperties.entrySet())
> > +        {
> > +            collectStrings(entry, strings);
> > +        }
> > +        for (Entry<String, Object> entry : m_properties.entrySet())
> > +        {
> > +            collectStrings(entry, strings);
> > +        }
> > +        for (ReferenceMetadata rMeta : m_references)
> > +        {
> > +            rMeta.collectStrings(strings);
> > +        }
> > +        if (m_service != null)
> > +        {
> > +            m_service.collectStrings(strings);
> > +        }
> > +    }
> > +
> > +    private void collectStrings(Entry<String, Object> entry,
> Set<String> strings)
> > +    {
> > +        addString(entry.getKey(), strings);
> > +        Object v = entry.getValue();
> > +        if (v instanceof String)
> > +        {
> > +            addString((String) v, strings);
> > +        }
> > +        else if (v instanceof String[])
> > +        {
> > +            for (String s : (String[]) v)
> > +            {
> > +                addString(s, strings);
> > +            }
> > +        }
> > +    }
> > +
> > +    public void store(DataOutputStream out, MetaDataWriter
> metaDataWriter)
> > +        throws IOException
> > +    {
> > +        metaDataWriter.writeString(m_dsVersion.toString(), out);
> > +        metaDataWriter.writeString(m_activate, out);
> > +        out.writeBoolean(m_activationFields != null);
> > +        if (m_activationFields != null)
> > +        {
> > +            out.writeInt(m_activationFields.size());
> > +            for (String s : m_activationFields)
> > +            {
> > +                metaDataWriter.writeString(s, out);
> > +            }
> > +        }
> > +        out.writeBoolean(m_configurationPid != null);
> > +        if (m_configurationPid != null)
> > +        {
> > +            out.writeInt(m_configurationPid.size());
> > +            for (String s : m_configurationPid)
> > +            {
> > +                metaDataWriter.writeString(s, out);
> > +            }
> > +        }
> > +        metaDataWriter.writeString(m_configurationPolicy, out);
> > +        metaDataWriter.writeString(m_deactivate, out);
> > +        metaDataWriter.writeString(m_factory, out);
> > +        metaDataWriter.writeString(m_implementationClassName, out);
> > +        metaDataWriter.writeString(m_init, out);
> > +        metaDataWriter.writeString(m_modified, out);
> > +        metaDataWriter.writeString(m_name, out);
> > +        out.writeInt(m_factoryProperties.size());
> > +        for (Entry<String, Object> prop :
> m_factoryProperties.entrySet())
> > +        {
> > +            metaDataWriter.writeString(prop.getKey(), out);
> > +            storePropertyValue(prop.getValue(), out, metaDataWriter);
> > +        }
> > +        out.writeInt(m_properties.size());
> > +        for (Entry<String, Object> prop : m_properties.entrySet())
> > +        {
> > +            metaDataWriter.writeString(prop.getKey(), out);
> > +            storePropertyValue(prop.getValue(), out, metaDataWriter);
> > +        }
> > +        out.writeInt(m_references.size());
> > +        for (ReferenceMetadata rMeta : m_references)
> > +        {
> > +            rMeta.store(out, metaDataWriter);
> > +        }
> > +        out.writeBoolean(m_service != null);
> > +        if (m_service != null)
> > +        {
> > +            m_service.store(out, metaDataWriter);
> > +        }
> > +        out.writeBoolean(m_activateDeclared);
> > +        out.writeBoolean(m_configurableServiceProperties);
> > +        out.writeBoolean(m_configureWithInterfaces);
> > +        out.writeBoolean(m_deactivateDeclared);
> > +        out.writeBoolean(m_delayedKeepInstances);
> > +        out.writeBoolean(m_deleteCallsModify);
> > +        out.writeBoolean(m_enabled);
> > +        out.writeBoolean(m_immediate != null);
> > +        if (m_immediate != null)
> > +        {
> > +            out.writeBoolean(m_immediate.booleanValue());
> > +        }
> > +    }
> > +
> > +    public static ComponentMetadata load(DataInputStream in,
> > +        MetaDataReader metaDataReader)
> > +        throws IOException
> > +    {
> > +        ComponentMetadata result = new ComponentMetadata(
> > +            DSVersion.valueOf(metaDataReader.readString(in)));
> > +        result.m_activate = metaDataReader.readString(in);
> > +        if (in.readBoolean())
> > +        {
> > +            int size = in.readInt();
> > +            String[] activationFields = new String[size];
> > +            for (int i = 0; i < size; i++)
> > +            {
> > +                activationFields[i] = metaDataReader.readString(in);
> > +            }
> > +            result.setActivationFields(activationFields);
> > +        }
> > +        if (in.readBoolean())
> > +        {
> > +            int size = in.readInt();
> > +            String[] configPids = new String[size];
> > +            for (int i = 0; i < size; i++)
> > +            {
> > +                configPids[i] = metaDataReader.readString(in);
> > +            }
> > +            result.setConfigurationPid(configPids);
> > +        }
> > +        result.m_configurationPolicy = metaDataReader.readString(in);
> > +        result.m_deactivate = metaDataReader.readString(in);
> > +        result.m_factory = metaDataReader.readString(in);
> > +        result.m_implementationClassName =
> metaDataReader.readString(in);
> > +        result.m_init = metaDataReader.readString(in);
> > +        result.m_modified = metaDataReader.readString(in);
> > +        result.m_name = metaDataReader.readString(in);
> > +        int numFProps = in.readInt();
> > +        for (int i = 0; i < numFProps; i++)
> > +        {
> > +
> result.m_factoryProperties.put(metaDataReader.readString(in),
> > +                loadPropertyValue(in, metaDataReader));
> > +        }
> > +        int numProps = in.readInt();
> > +        for (int i = 0; i < numProps; i++)
> > +        {
> > +            result.m_properties.put(metaDataReader.readString(in),
> > +                loadPropertyValue(in, metaDataReader));
> > +        }
> > +        int numRefs = in.readInt();
> > +        for (int i = 0; i < numRefs; i++)
> > +        {
> > +            result.addDependency(ReferenceMetadata.load(in,
> metaDataReader));
> > +        }
> > +        if (in.readBoolean())
> > +        {
> > +            result.m_service = ServiceMetadata.load(in, metaDataReader);
> > +        }
> > +        result.m_activateDeclared = in.readBoolean();
> > +        result.m_configurableServiceProperties = in.readBoolean();
> > +        result.m_configureWithInterfaces = in.readBoolean();
> > +        result.m_deactivateDeclared = in.readBoolean();
> > +        result.m_delayedKeepInstances = in.readBoolean();
> > +        result.m_deleteCallsModify = in.readBoolean();
> > +        result.m_enabled = in.readBoolean();
> > +        if (in.readBoolean())
> > +        {
> > +            result.m_immediate = in.readBoolean();
> > +        }
> > +        // we only store valid metadata
> > +        result.m_validated = true;
> > +        return result;
> > +    }
> > +
> > +    private static final byte TypeString = 1;
> > +    private static final byte TypeLong = 2;
> > +    private static final byte TypeDouble = 3;
> > +    private static final byte TypeFloat = 4;
> > +    private static final byte TypeInteger = 5;
> > +    private static final byte TypeByte = 6;
> > +    private static final byte TypeChar = 7;
> > +    private static final byte TypeBoolean = 8;
> > +    private static final byte TypeShort = 9;
> > +
> > +    static Object loadPropertyValue(DataInputStream in, MetaDataReader
> metaDataReader)
> > +        throws IOException
> > +    {
> > +        boolean isArray = in.readBoolean();
> > +        byte valueType = in.readByte();
> > +        switch (valueType)
> > +        {
> > +            case TypeBoolean:
> > +                if (isArray)
> > +                {
> > +                    boolean[] vArray = new boolean[in.readInt()];
> > +                    for (int i = 0; i < vArray.length; i++)
> > +                    {
> > +                        vArray[i] = in.readBoolean();
> > +                    }
> > +                    return vArray;
> > +                }
> > +                else
> > +                {
> > +                    return Boolean.valueOf(in.readBoolean());
> > +                }
> > +            case TypeByte:
> > +                if (isArray)
> > +                {
> > +                    byte[] vArray = new byte[in.readInt()];
> > +                    for (int i = 0; i < vArray.length; i++)
> > +                    {
> > +                        vArray[i] = in.readByte();
> > +                    }
> > +                    return vArray;
> > +                }
> > +                else
> > +                {
> > +                    return Byte.valueOf(in.readByte());
> > +                }
> > +            case TypeChar:
> > +                if (isArray)
> > +                {
> > +                    char[] vArray = new char[in.readInt()];
> > +                    for (int i = 0; i < vArray.length; i++)
> > +                    {
> > +                        vArray[i] = in.readChar();
> > +                    }
> > +                    return vArray;
> > +                }
> > +                else
> > +                {
> > +                    return Character.valueOf(in.readChar());
> > +                }
> > +            case TypeDouble:
> > +                if (isArray)
> > +                {
> > +                    double[] vArray = new double[in.readInt()];
> > +                    for (int i = 0; i < vArray.length; i++)
> > +                    {
> > +                        vArray[i] = in.readDouble();
> > +                    }
> > +                    return vArray;
> > +                }
> > +                else
> > +                {
> > +                    return Double.valueOf(in.readDouble());
> > +                }
> > +            case TypeFloat:
> > +                if (isArray)
> > +                {
> > +                    float[] vArray = new float[in.readInt()];
> > +                    for (int i = 0; i < vArray.length; i++)
> > +                    {
> > +                        vArray[i] = in.readFloat();
> > +                    }
> > +                    return vArray;
> > +                }
> > +                else
> > +                {
> > +                    return Float.valueOf(in.readFloat());
> > +                }
> > +            case TypeInteger:
> > +                if (isArray)
> > +                {
> > +                    int[] vArray = new int[in.readInt()];
> > +                    for (int i = 0; i < vArray.length; i++)
> > +                    {
> > +                        vArray[i] = in.readInt();
> > +                    }
> > +                    return vArray;
> > +                }
> > +                else
> > +                {
> > +                    return Integer.valueOf(in.readInt());
> > +                }
> > +            case TypeLong:
> > +                if (isArray)
> > +                {
> > +                    long[] vArray = new long[in.readInt()];
> > +                    for (int i = 0; i < vArray.length; i++)
> > +                    {
> > +                        vArray[i] = in.readLong();
> > +                    }
> > +                    return vArray;
> > +                }
> > +                else
> > +                {
> > +                    return Long.valueOf(in.readLong());
> > +                }
> > +            case TypeShort:
> > +                if (isArray)
> > +                {
> > +                    short[] vArray = new short[in.readInt()];
> > +                    for (int i = 0; i < vArray.length; i++)
> > +                    {
> > +                        vArray[i] = in.readShort();
> > +                    }
> > +                    return vArray;
> > +                }
> > +                else
> > +                {
> > +                    return Short.valueOf(in.readShort());
> > +                }
> > +            case TypeString:
> > +                if (isArray)
> > +                {
> > +                    String[] vArray = new String[in.readInt()];
> > +                    for (int i = 0; i < vArray.length; i++)
> > +                    {
> > +                        vArray[i] = metaDataReader.readString(in);
> > +                    }
> > +                    return vArray;
> > +                }
> > +                else
> > +                {
> > +                    return metaDataReader.readString(in);
> > +                }
> > +        }
> > +        return null;
> > +    }
> > +
> > +    void storePropertyValue(Object value, DataOutputStream out,
> > +        MetaDataWriter metaDataWriter) throws IOException
> > +    {
> > +        if (value == null)
> > +        {
> > +            // handle null value as a string
> > +            out.writeBoolean(false);
> > +            out.writeByte(TypeString);
> > +            metaDataWriter.writeString(null, out);
> > +            return;
> > +        }
> > +        Class<?> arrayType = value.getClass().getComponentType();
> > +        boolean isArray = arrayType != null;
> > +        out.writeBoolean(isArray);
> > +        byte valueType = getType(arrayType == null ? value.getClass() :
> arrayType);
> > +        out.writeByte(valueType);
> > +        switch (valueType)
> > +        {
> > +            case TypeBoolean:
> > +                if (isArray)
> > +                {
> > +                    boolean[] vArray = (boolean[]) value;
> > +                    out.writeInt(vArray.length);
> > +                    for (boolean v : vArray)
> > +                    {
> > +                        out.writeBoolean(v);
> > +                    }
> > +                }
> > +                else
> > +                {
> > +                    out.writeBoolean((boolean) value);
> > +                }
> > +                break;
> > +            case TypeByte:
> > +                if (isArray)
> > +                {
> > +                    byte[] vArray = (byte[]) value;
> > +                    out.writeInt(vArray.length);
> > +                    for (byte v : vArray)
> > +                    {
> > +                        out.writeByte(v);
> > +                    }
> > +                }
> > +                else
> > +                {
> > +                    out.writeByte((byte) value);
> > +                }
> > +                break;
> > +            case TypeChar:
> > +                if (isArray)
> > +                {
> > +                    char[] vArray = (char[]) value;
> > +                    out.writeInt(vArray.length);
> > +                    for (char v : vArray)
> > +                    {
> > +                        out.writeChar(v);
> > +                    }
> > +                }
> > +                else
> > +                {
> > +                    out.writeChar((char) value);
> > +                }
> > +                break;
> > +            case TypeDouble:
> > +                if (isArray)
> > +                {
> > +                    double[] vArray = (double[]) value;
> > +                    out.writeInt(vArray.length);
> > +                    for (double v : vArray)
> > +                    {
> > +                        out.writeDouble(v);
> > +                    }
> > +                }
> > +                else
> > +                {
> > +                    out.writeDouble((double) value);
> > +                }
> > +                break;
> > +            case TypeFloat:
> > +                if (isArray)
> > +                {
> > +                    float[] vArray = (float[]) value;
> > +                    out.writeInt(vArray.length);
> > +                    for (float v : vArray)
> > +                    {
> > +                        out.writeFloat(v);
> > +                    }
> > +                }
> > +                else
> > +                {
> > +                    out.writeFloat((float) value);
> > +                }
> > +                break;
> > +            case TypeInteger:
> > +                if (isArray)
> > +                {
> > +                    int[] vArray = (int[]) value;
> > +                    out.writeInt(vArray.length);
> > +                    for (int v : vArray)
> > +                    {
> > +                        out.writeInt(v);
> > +                    }
> > +                }
> > +                else
> > +                {
> > +                    out.writeInt((int) value);
> > +                }
> > +                break;
> > +            case TypeLong:
> > +                if (isArray)
> > +                {
> > +                    long[] vArray = (long[]) value;
> > +                    out.writeInt(vArray.length);
> > +                    for (long v : vArray)
> > +                    {
> > +                        out.writeLong(v);
> > +                    }
> > +                }
> > +                else
> > +                {
> > +                    out.writeLong((long) value);
> > +                }
> > +                break;
> > +            case TypeShort:
> > +                if (isArray)
> > +                {
> > +                    short[] vArray = (short[]) value;
> > +                    out.writeInt(vArray.length);
> > +                    for (short v : vArray)
> > +                    {
> > +                        out.writeShort(v);
> > +                    }
> > +                }
> > +                else
> > +                {
> > +                    out.writeShort((short) value);
> > +                }
> > +                break;
> > +            case TypeString:
> > +                if (isArray)
> > +                {
> > +                    String[] vArray = (String[]) value;
> > +                    out.writeInt(vArray.length);
> > +                    for (String v : vArray)
> > +                    {
> > +                        metaDataWriter.writeString(v, out);
> > +                    }
> > +
> > +                }
> > +                else
> > +                {
> > +                    metaDataWriter.writeString((String) value, out);
> > +                }
> > +                break;
> > +        }
> > +    }
> > +
> > +    private byte getType(Class<?> typeClass)
> > +    {
> > +        if (Boolean.class.equals(typeClass) ||
> boolean.class.equals(typeClass))
> > +        {
> > +            return TypeBoolean;
> > +        }
> > +        if (Byte.class.equals(typeClass) ||
> byte.class.equals(typeClass))
> > +        {
> > +            return TypeByte;
> > +        }
> > +        if (Character.class.equals(typeClass) ||
> char.class.equals(typeClass))
> > +        {
> > +            return TypeChar;
> > +        }
> > +        if (Double.class.equals(typeClass) ||
> double.class.equals(typeClass))
> > +        {
> > +            return TypeDouble;
> > +        }
> > +        if (Float.class.equals(typeClass) ||
> float.class.equals(typeClass))
> > +        {
> > +            return TypeFloat;
> > +        }
> > +        if (Integer.class.equals(typeClass) ||
> int.class.equals(typeClass))
> > +        {
> > +            return TypeInteger;
> > +        }
> > +        if (Long.class.equals(typeClass) ||
> long.class.equals(typeClass))
> > +        {
> > +            return TypeLong;
> > +        }
> > +        if (Short.class.equals(typeClass) ||
> short.class.equals(typeClass))
> > +        {
> > +            return TypeShort;
> > +        }
> > +        if (String.class.equals(typeClass))
> > +        {
> > +            return TypeString;
> > +        }
> > +        throw new IllegalArgumentException("Unsupported type: " +
> typeClass);
> > +
> > +    }
> > +
> > }
> >
> > Added:
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/MetadataStoreHelper.java
> > URL:
> http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/MetadataStoreHelper.java?rev=1862491&view=auto
> >
> ==============================================================================
> > ---
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/MetadataStoreHelper.java
> (added)
> > +++
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/MetadataStoreHelper.java
> Wed Jul  3 16:35:54 2019
> > @@ -0,0 +1,160 @@
> > +/*
> > + * 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.scr.impl.metadata;
> > +
> > +import java.io.DataInputStream;
> > +import java.io.DataOutputStream;
> > +import java.io.IOException;
> > +import java.util.ArrayList;
> > +import java.util.HashMap;
> > +import java.util.List;
> > +import java.util.Map;
> > +import java.util.Set;
> > +
> > +public class MetadataStoreHelper
> > +{
> > +    static final byte STRING_NULL = 0;
> > +    static final byte STRING_OBJECT = 1;
> > +    static final byte STRING_INDEX = 2;
> > +    static final byte STRING_LONG = 3;
> > +
> > +    public static class MetaDataReader
> > +    {
> > +        private final List<String> stringTable = new
> ArrayList<String>();
> > +
> > +        public String readIndexedString(DataInputStream in) throws
> IOException
> > +        {
> > +            String s = readString(in);
> > +            addToStringTable(s, in.readInt());
> > +            return s;
> > +        }
> > +
> > +        public String readString(DataInputStream in) throws IOException
> > +        {
> > +            byte type = in.readByte();
> > +            if (type == STRING_INDEX)
> > +            {
> > +                int index = in.readInt();
> > +                return (String) stringTable.get(index);
> > +            }
> > +            if (type == STRING_NULL)
> > +            {
> > +                return null;
> > +            }
> > +            String s;
> > +            if (type == STRING_LONG)
> > +            {
> > +                int length = in.readInt();
> > +                byte[] data = new byte[length];
> > +                in.readFully(data);
> > +                s = new String(data, "UTF-8");
> > +            }
> > +            else
> > +            {
> > +                s = in.readUTF();
> > +            }
> > +            return s;
> > +        }
> > +
> > +        private void addToStringTable(String s, int index)
> > +        {
> > +            if (index == stringTable.size())
> > +            {
> > +                stringTable.add(s);
> > +            }
> > +            else if (index < stringTable.size())
> > +            {
> > +                stringTable.set(index, s);
> > +            }
> > +            else
> > +            {
> > +                while (stringTable.size() < index)
> > +                {
> > +                    stringTable.add(null);
> > +                }
> > +                stringTable.add(s);
> > +            }
> > +        }
> > +    }
> > +
> > +    public static class MetaDataWriter
> > +    {
> > +        private final Map<String, Integer> stringTable = new
> HashMap<>();
> > +
> > +        public void writeIndexedString(String s, DataOutputStream out)
> throws IOException
> > +        {
> > +            writeString(s, out);
> > +            out.writeInt(addToStringTable(s));
> > +        }
> > +
> > +        public void writeString(String s, DataOutputStream out) throws
> IOException
> > +        {
> > +            Integer index = s != null ? stringTable.get(s) : null;
> > +            if (index != null)
> > +            {
> > +                out.writeByte(STRING_INDEX);
> > +                out.writeInt(index);
> > +                return;
> > +            }
> > +
> > +            if (s == null)
> > +                out.writeByte(STRING_NULL);
> > +            else
> > +            {
> > +                byte[] data = s.getBytes("UTF-8");
> > +
> > +                if (data.length > 65535)
> > +                {
> > +                    out.writeByte(STRING_LONG);
> > +                    out.writeInt(data.length);
> > +                    out.write(data);
> > +                }
> > +                else
> > +                {
> > +                    out.writeByte(STRING_OBJECT);
> > +                    out.writeUTF(s);
> > +                }
> > +            }
> > +        }
> > +
> > +        private int addToStringTable(String s)
> > +        {
> > +            if (s == null)
> > +            {
> > +                throw new NullPointerException();
> > +            }
> > +            Integer cur = stringTable.get(s);
> > +            if (cur != null)
> > +                throw new IllegalStateException(
> > +                    "String is already in the write table: " + s);
> > +            int index = stringTable.size();
> > +            stringTable.put(s, Integer.valueOf(index));
> > +            // return the index of the object just added
> > +            return index;
> > +        }
> > +    }
> > +
> > +    public static void addString(String s, Set<String> strings)
> > +    {
> > +        if (s != null)
> > +        {
> > +            strings.add(s);
> > +        }
> > +    }
> > +}
> >
> > Modified:
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ReferenceMetadata.java
> > URL:
> http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ReferenceMetadata.java?rev=1862491&r1=1862490&r2=1862491&view=diff
> >
> ==============================================================================
> > ---
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ReferenceMetadata.java
> (original)
> > +++
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ReferenceMetadata.java
> Wed Jul  3 16:35:54 2019
> > @@ -18,9 +18,17 @@
> >  */
> > package org.apache.felix.scr.impl.metadata;
> >
> > +import static
> org.apache.felix.scr.impl.metadata.MetadataStoreHelper.addString;
> > +
> > +import java.io.DataInputStream;
> > +import java.io.DataOutputStream;
> > +import java.io.IOException;
> > import java.util.Set;
> > import java.util.TreeSet;
> >
> > +import
> org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataReader;
> > +import
> org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataWriter;
> > +
> > /**
> >  * Information associated to a dependency
> >  *
> > @@ -824,4 +832,86 @@ public class ReferenceMetadata
> >                 ", collection-type=" + this.getFieldCollectionType() +
> >                 ", parameter=" + this.getParameterIndex();
> >     }
> > +
> > +    void collectStrings(Set<String> strings)
> > +    {
> > +        addString(m_bind, strings);
> > +        addString(m_cardinality, strings);
> > +        addString(m_collection_type, strings);
> > +        addString(m_field, strings);
> > +        addString(m_field_option, strings);
> > +        addString(m_interface, strings);
> > +        addString(m_name, strings);
> > +        addString(m_parameter, strings);
> > +        addString(m_policy, strings);
> > +        addString(m_policy_option, strings);
> > +        addString(m_scopeName, strings);
> > +        addString(m_scope.toString(), strings);
> > +        addString(m_target, strings);
> > +        addString(m_unbind, strings);
> > +        addString(m_updated, strings);
> > +    }
> > +
> > +    void store(DataOutputStream out, MetaDataWriter metaDataWriter)
> throws IOException
> > +    {
> > +        metaDataWriter.writeString(m_bind, out);
> > +        metaDataWriter.writeString(m_cardinality, out);
> > +        metaDataWriter.writeString(m_collection_type, out);
> > +        metaDataWriter.writeString(m_field, out);
> > +        metaDataWriter.writeString(m_field_option, out);
> > +        metaDataWriter.writeString(m_interface, out);
> > +        out.writeBoolean(m_isMultiple);
> > +        out.writeBoolean(m_isOptional);
> > +        out.writeBoolean(m_isReluctant);
> > +        out.writeBoolean(m_isReplace);
> > +        out.writeBoolean(m_isStatic);
> > +        metaDataWriter.writeString(m_name, out);
> > +        metaDataWriter.writeString(m_parameter, out);
> > +        out.writeBoolean(m_parameterIndex != null);
> > +        if (m_parameterIndex != null)
> > +        {
> > +            out.writeInt(m_parameterIndex.intValue());
> > +        }
> > +        metaDataWriter.writeString(m_policy, out);
> > +        metaDataWriter.writeString(m_policy_option, out);
> > +        metaDataWriter.writeString(m_scopeName, out);
> > +        metaDataWriter.writeString(m_scope.toString(), out);
> > +        metaDataWriter.writeString(m_target, out);
> > +        metaDataWriter.writeString(m_unbind, out);
> > +        metaDataWriter.writeString(m_updated, out);
> > +    }
> > +
> > +    static ReferenceMetadata load(DataInputStream in, MetaDataReader
> metaDataReader)
> > +        throws IOException
> > +    {
> > +        ReferenceMetadata result = new ReferenceMetadata();
> > +        result.m_bind = metaDataReader.readString(in);
> > +        result.m_cardinality = metaDataReader.readString(in);
> > +        result.m_collection_type = metaDataReader.readString(in);
> > +        result.m_field = metaDataReader.readString(in);
> > +        result.m_field_option = metaDataReader.readString(in);
> > +        result.m_interface = metaDataReader.readString(in);
> > +        result.m_isMultiple = in.readBoolean();
> > +        result.m_isOptional = in.readBoolean();
> > +        result.m_isReluctant = in.readBoolean();
> > +        result.m_isReplace = in.readBoolean();
> > +        result.m_isStatic = in.readBoolean();
> > +        result.m_name = metaDataReader.readString(in);
> > +        result.m_parameter = metaDataReader.readString(in);
> > +        if (in.readBoolean())
> > +        {
> > +            result.m_parameterIndex = Integer.valueOf(in.readInt());
> > +        }
> > +        result.m_policy = metaDataReader.readString(in);
> > +        result.m_policy_option = metaDataReader.readString(in);
> > +        result.m_scopeName = metaDataReader.readString(in);
> > +        result.m_scope =
> ReferenceScope.valueOf(metaDataReader.readString(in));
> > +        result.m_target = metaDataReader.readString(in);
> > +        result.m_unbind = metaDataReader.readString(in);
> > +        result.m_updated = metaDataReader.readString(in);
> > +
> > +        // only stored valid metadata
> > +        result.m_validated = true;
> > +        return result;
> > +    }
> > }
> > \ No newline at end of file
> >
> > Modified:
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ServiceMetadata.java
> > URL:
> http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ServiceMetadata.java?rev=1862491&r1=1862490&r2=1862491&view=diff
> >
> ==============================================================================
> > ---
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ServiceMetadata.java
> (original)
> > +++
> felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ServiceMetadata.java
> Wed Jul  3 16:35:54 2019
> > @@ -18,8 +18,17 @@
> >  */
> > package org.apache.felix.scr.impl.metadata;
> >
> > +import static
> org.apache.felix.scr.impl.metadata.MetadataStoreHelper.addString;
> > +
> > +import java.io.DataInputStream;
> > +import java.io.DataOutputStream;
> > +import java.io.IOException;
> > import java.util.ArrayList;
> > import java.util.List;
> > +import java.util.Set;
> > +
> > +import
> org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataReader;
> > +import
> org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataWriter;
> >
> > /**
> >  * This class contains the metadata associated to a service that is
> provided
> > @@ -134,4 +143,50 @@ public class ServiceMetadata {
> >         }
> >         m_validated = true;
> >     }
> > +
> > +    void collectStrings(Set<String> strings)
> > +    {
> > +        for (String s : m_provides)
> > +        {
> > +            addString(s, strings);
> > +        }
> > +        addString(m_scopeName, strings);
> > +        addString(m_scope.toString(), strings);
> > +    }
> > +
> > +    void store(DataOutputStream out, MetaDataWriter metaDataWriter)
> throws IOException
> > +    {
> > +        out.writeInt(m_provides.size());
> > +        for (String s : m_provides)
> > +        {
> > +            metaDataWriter.writeString(s, out);
> > +        }
> > +        metaDataWriter.writeString(m_scopeName, out);
> > +        metaDataWriter.writeString(m_scope.toString(), out);
> > +        out.writeBoolean(m_serviceFactory != null);
> > +        if (m_serviceFactory != null)
> > +        {
> > +            out.writeBoolean(m_serviceFactory.booleanValue());
> > +        }
> > +    }
> > +
> > +    static ServiceMetadata load(DataInputStream in, MetaDataReader
> metaDataReader)
> > +        throws IOException
> > +    {
> > +        ServiceMetadata result = new ServiceMetadata();
> > +        int providerSize = in.readInt();
> > +        for (int i = 0; i < providerSize; i++)
> > +        {
> > +            result.addProvide(metaDataReader.readString(in));
> > +        }
> > +        result.m_scopeName = metaDataReader.readString(in);
> > +        result.m_scope = Scope.valueOf(metaDataReader.readString(in));
> > +        if (in.readBoolean())
> > +        {
> > +            result.m_serviceFactory = in.readBoolean();
> > +        }
> > +        // only stored valid metadata
> > +        result.m_validated = true;
> > +        return result;
> > +    }
> > }
> >
> > Modified:
> felix/trunk/scr/src/test/java/org/apache/felix/scr/impl/logger/MockScrLogger.java
> > URL:
> http://svn.apache.org/viewvc/felix/trunk/scr/src/test/java/org/apache/felix/scr/impl/logger/MockScrLogger.java?rev=1862491&r1=1862490&r2=1862491&view=diff
> >
> ==============================================================================
> > ---
> felix/trunk/scr/src/test/java/org/apache/felix/scr/impl/logger/MockScrLogger.java
> (original)
> > +++
> felix/trunk/scr/src/test/java/org/apache/felix/scr/impl/logger/MockScrLogger.java
> Wed Jul  3 16:35:54 2019
> > @@ -78,6 +78,12 @@ public class MockScrLogger extends ScrLo
> >             public boolean globalExtender() {
> >                 return false;
> >             }
> > +
> > +            @Override
> > +            public boolean cacheMetadata()
> > +            {
> > +                return false;
> > +            }
> >         }, new MockBundleContext(new MockBundle()));
> >     }
> > }
> > \ No newline at end of file
> >
> >
>

Re: svn commit: r1862491 - in /felix/trunk/scr/src: main/java/org/apache/felix/scr/impl/ main/java/org/apache/felix/scr/impl/config/ main/java/org/apache/felix/scr/impl/manager/ main/java/org/apache/felix/scr/impl/metadata/ test/java/org/apache/felix/scr/i...

Posted by David Jencks <da...@gmail.com>.
Would it be a good idea to start the serialized metadata file with a version number so when you discover a better format old format data can be ignored rather than causing a read error?

How much speedup and size reduction do you get from this binary format compared to say one json file as the cache?

Thanks
David Jencks 

Sent from my iPhone

> On Jul 3, 2019, at 9:35 AM, tjwatson@apache.org wrote:
> 
> Author: tjwatson
> Date: Wed Jul  3 16:35:54 2019
> New Revision: 1862491
> 
> URL: http://svn.apache.org/viewvc?rev=1862491&view=rev
> Log:
> FELIX-6155 - Add caching of service component metadata
> 
> By default the caching is disabled.  The configuration property (or
> framework launch property) ds.cache.metadata can be set to the string
> "true" to enable the support.  Unfortunately dynamically setting this
> with config admin is problematic because the cache is read at activation
> time currently.  This could be enhanced to read the cache any time the
> value is set to true dynamically.  For now it is required to be set from
> the start with a system or framework launch property
> 
> Added:
>    felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/MetadataStoreHelper.java
> Modified:
>    felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/Activator.java
>    felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java
>    felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfigurationImpl.java
>    felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ScrConfiguration.java
>    felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java
>    felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ReferenceMetadata.java
>    felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ServiceMetadata.java
>    felix/trunk/scr/src/test/java/org/apache/felix/scr/impl/logger/MockScrLogger.java
> 
> Modified: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/Activator.java
> URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/Activator.java?rev=1862491&r1=1862490&r2=1862491&view=diff
> ==============================================================================
> --- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/Activator.java (original)
> +++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/Activator.java Wed Jul  3 16:35:54 2019
> @@ -18,9 +18,24 @@
>  */
> package org.apache.felix.scr.impl;
> 
> +import java.io.BufferedInputStream;
> +import java.io.BufferedOutputStream;
> +import java.io.DataInputStream;
> +import java.io.DataOutputStream;
> +import java.io.File;
> +import java.io.FileInputStream;
> +import java.io.FileOutputStream;
> +import java.io.IOException;
> +import java.util.ArrayList;
> +import java.util.Collections;
> import java.util.HashMap;
> +import java.util.HashSet;
> import java.util.List;
> import java.util.Map;
> +import java.util.Map.Entry;
> +import java.util.Set;
> +import java.util.concurrent.ConcurrentHashMap;
> +import java.util.concurrent.ConcurrentMap;
> import java.util.concurrent.TimeUnit;
> import java.util.concurrent.locks.Lock;
> import java.util.concurrent.locks.ReentrantLock;
> @@ -28,9 +43,14 @@ import java.util.concurrent.locks.Reentr
> import org.apache.felix.scr.impl.config.ScrConfigurationImpl;
> import org.apache.felix.scr.impl.inject.ClassUtils;
> import org.apache.felix.scr.impl.logger.ScrLogger;
> +import org.apache.felix.scr.impl.manager.ComponentHolder;
> +import org.apache.felix.scr.impl.metadata.ComponentMetadata;
> +import org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataReader;
> +import org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataWriter;
> import org.apache.felix.scr.impl.runtime.ServiceComponentRuntimeImpl;
> import org.osgi.framework.Bundle;
> import org.osgi.framework.BundleContext;
> +import org.osgi.framework.BundleEvent;
> import org.osgi.framework.Constants;
> import org.osgi.framework.ServiceRegistration;
> import org.osgi.framework.wiring.BundleRevision;
> @@ -75,6 +95,8 @@ public class Activator extends AbstractE
> 
>     private ComponentCommands m_componentCommands;
> 
> +    private ConcurrentMap<Long, List<ComponentMetadata>> m_componentMetadataStore;
> +
>     public Activator()
>     {
>         m_configuration = new ScrConfigurationImpl( this );
> @@ -102,6 +124,8 @@ public class Activator extends AbstractE
> 
>     public void restart(boolean globalExtender)
>     {
> +        m_componentMetadataStore = load(m_context, logger,
> +            m_configuration.cacheMetadata());
>         BundleContext context = m_globalContext;
>         if ( globalExtender )
>         {
> @@ -182,6 +206,136 @@ public class Activator extends AbstractE
>     {
>         super.stop( context );
>         m_configuration.stop();
> +        store(m_componentMetadataStore, context, logger, m_configuration.cacheMetadata());
> +    }
> +
> +    @Override
> +    public void bundleChanged(BundleEvent event)
> +    {
> +        super.bundleChanged(event);
> +        if (event.getType() == BundleEvent.UPDATED
> +            || event.getType() == BundleEvent.UNINSTALLED)
> +        {
> +            m_componentMetadataStore.remove(event.getBundle().getBundleId());
> +        }
> +    }
> +
> +    private static ConcurrentMap<Long, List<ComponentMetadata>> load(
> +        BundleContext context,
> +        ScrLogger logger, boolean loadFromCache)
> +    {
> +        try
> +        {
> +            ConcurrentMap<Long, List<ComponentMetadata>> result = new ConcurrentHashMap<>();
> +            if (!loadFromCache)
> +            {
> +                return result;
> +            }
> +            BundleContext systemContext = context.getBundle(
> +                Constants.SYSTEM_BUNDLE_LOCATION).getBundleContext();
> +
> +            File store = context.getDataFile("componentMetadataStore");
> +            if (store.isFile())
> +            {
> +                try (DataInputStream in = new DataInputStream(
> +                    new BufferedInputStream(new FileInputStream(store))))
> +                {
> +                    MetaDataReader metaDataReader = new MetaDataReader();
> +                    int numStrings = in.readInt();
> +                    for (int i = 0; i < numStrings; i++)
> +                    {
> +                        metaDataReader.readIndexedString(in);
> +                    }
> +                    int numBundles = in.readInt();
> +                    for (int i = 0; i < numBundles; i++)
> +                    {
> +                        // Read all the components for the ID even if the bundle does not exist;
> +                        long bundleId = in.readLong();
> +                        int numComponents = in.readInt();
> +                        long lastModified = in.readLong();
> +                        List<ComponentMetadata> components = new ArrayList<>(
> +                            numComponents);
> +                        for (int j = 0; j < numComponents; j++)
> +                        {
> +                            components.add(ComponentMetadata.load(in, metaDataReader));
> +                        }
> +                        // Check with system context by ID to avoid hooks hiding;
> +                        Bundle b = systemContext.getBundle(bundleId);
> +                        if (b != null)
> +                        {
> +                            if (lastModified == b.getLastModified())
> +                            {
> +                                result.put(bundleId, components);
> +                            }
> +                        }
> +                    }
> +                }
> +                catch (IOException e)
> +                {
> +                    logger.log(LogService.LOG_WARNING,
> +                        "Error loading component metadata cache.", e);
> +                }
> +            }
> +            return result;
> +        }
> +        catch (RuntimeException re)
> +        {
> +            // avoid failing all of SCR start on cache load bug
> +            logger.log(LogService.LOG_ERROR,
> +                "Error loading component metadata cache.", re);
> +            return new ConcurrentHashMap<>();
> +        }
> +
> +    }
> +
> +    private static void store(Map<Long, List<ComponentMetadata>> componentsMap,
> +        BundleContext context, ScrLogger logger, boolean storeCache)
> +    {
> +        if (!storeCache)
> +        {
> +            return;
> +        }
> +        BundleContext systemContext = context.getBundle(
> +            Constants.SYSTEM_BUNDLE_LOCATION).getBundleContext();
> +        File store = context.getDataFile("componentMetadataStore");
> +        try (DataOutputStream out = new DataOutputStream(
> +            new BufferedOutputStream(new FileOutputStream(store))))
> +        {
> +            MetaDataWriter metaDataWriter = new MetaDataWriter();
> +
> +            Set<String> allStrings = new HashSet<>();
> +            for (List<ComponentMetadata> components : componentsMap.values())
> +            {
> +                for (ComponentMetadata component : components)
> +                {
> +                    component.collectStrings(allStrings);
> +                }
> +            }
> +            // remove possible null
> +            allStrings.remove(null);
> +            out.writeInt(allStrings.size());
> +            for (String s : allStrings)
> +            {
> +                metaDataWriter.writeIndexedString(s, out);
> +            }
> +            out.writeInt(componentsMap.size());
> +            for (Entry<Long, List<ComponentMetadata>> entry : componentsMap.entrySet())
> +            {
> +                out.writeLong(entry.getKey());
> +                out.writeInt(entry.getValue().size());
> +                Bundle b = systemContext.getBundle(entry.getKey());
> +                out.writeLong(b == null ? -1 : b.getLastModified());
> +                for (ComponentMetadata component : entry.getValue())
> +                {
> +                    component.store(out, metaDataWriter);
> +                }
> +            }
> +        }
> +        catch (IOException e)
> +        {
> +            logger.log(LogService.LOG_WARNING, "Error storing component metadata cache.",
> +                e);
> +        }
>     }
> 
>     /**
> @@ -312,8 +466,20 @@ public class Activator extends AbstractE
>      */
>     private void loadComponents(Bundle bundle)
>     {
> -        if (bundle.getHeaders("").get(ComponentConstants.SERVICE_COMPONENT) == null)
> +        final Long bundleId = bundle.getBundleId();
> +        List<ComponentMetadata> cached = m_componentMetadataStore.get(bundleId);
> +        if (cached != null && cached.isEmpty())
> +        {
> +            // Cached that there are no components for this bundle.
> +            return;
> +        }
> +
> +        if (cached == null
> +            && bundle.getHeaders("").get(ComponentConstants.SERVICE_COMPONENT) == null)
>         {
> +            // Cache that there are no components
> +            m_componentMetadataStore.put(bundleId,
> +                Collections.<ComponentMetadata> emptyList());
>             // no components in the bundle, abandon
>             return;
>         }
> @@ -352,7 +518,6 @@ public class Activator extends AbstractE
>         // FELIX-2231 Mark bundle loaded early to prevent concurrent loading
>         // if LAZY_ACTIVATION and STARTED event are fired at the same time
>         final boolean loaded;
> -        final Long bundleId = bundle.getBundleId();
>         synchronized ( m_componentBundles )
>         {
>             if ( m_componentBundles.containsKey( bundleId ) )
> @@ -378,9 +543,18 @@ public class Activator extends AbstractE
>         try
>         {
>             BundleComponentActivator ga = new BundleComponentActivator( this.logger, m_componentRegistry, m_componentActor,
> -                context, m_configuration );
> +                context, m_configuration, cached);
>             ga.initialEnable();
> -
> +            if (cached == null)
> +            {
> +                List<ComponentHolder<?>> components = ga.getSelectedComponents(null);
> +                List<ComponentMetadata> metadatas = new ArrayList<>(components.size());
> +                for (ComponentHolder<?> holder : ga.getSelectedComponents(null))
> +                {
> +                    metadatas.add(holder.getComponentMetadata());
> +                }
> +                m_componentMetadataStore.put(bundleId, metadatas);
> +            }
>             // replace bundle activator in the map
>             synchronized ( m_componentBundles )
>             {
> @@ -466,4 +640,4 @@ public class Activator extends AbstractE
>             }
>         }
>     }
> -}
> \ No newline at end of file
> +}
> 
> Modified: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java
> URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java?rev=1862491&r1=1862490&r2=1862491&view=diff
> ==============================================================================
> --- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java (original)
> +++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/BundleComponentActivator.java Wed Jul  3 16:35:54 2019
> @@ -244,7 +244,8 @@ public class BundleComponentActivator im
>             final ComponentRegistry componentRegistry,
>             final ComponentActorThread componentActor,
>             final BundleContext context,
> -            final ScrConfiguration configuration)
> +            final ScrConfiguration configuration,
> +            final List<ComponentMetadata> cachedComponentMetadata)
>     throws ComponentException
>     {
>         // create a logger on behalf of the bundle
> @@ -259,14 +260,7 @@ public class BundleComponentActivator im
> 
>         logger.log( LogService.LOG_DEBUG, "BundleComponentActivator : Bundle active", null);
> 
> -        // Get the Metadata-Location value from the manifest
> -        String descriptorLocations = m_bundle.getHeaders("").get("Service-Component");
> -        if ( descriptorLocations == null )
> -        {
> -            throw new ComponentException( "Service-Component entry not found in the manifest" );
> -        }
> -
> -        initialize( descriptorLocations );
> +        initialize(cachedComponentMetadata);
>         ConfigAdminTracker tracker = null;
>         for ( ComponentHolder<?> holder : m_holders )
>         {
> @@ -282,36 +276,56 @@ public class BundleComponentActivator im
>     /**
>      * Gets the MetaData location, parses the meta data and requests the processing
>      * of binder instances
> -     *
> -     * @param descriptorLocations A comma separated list of locations of
> -     *      component descriptors. This must not be <code>null</code>.
> +     * @param cachedComponentMetadata 
>      *
>      * @throws IllegalStateException If the bundle has already been uninstalled.
>      */
> -    protected void initialize(String descriptorLocations)
> +    protected void initialize(List<ComponentMetadata> cachedComponentMetadata)
>     {
> -        logger.log( LogService.LOG_DEBUG, "BundleComponentActivator : Descriptor locations {0}", null, descriptorLocations );
> -
> -        // 112.4.1: The value of the the header is a comma separated list of XML entries within the Bundle
> -        StringTokenizer st = new StringTokenizer( descriptorLocations, ", " );
> -
> -        while ( st.hasMoreTokens() )
> +        if (cachedComponentMetadata != null)
>         {
> -            String descriptorLocation = st.nextToken();
> -
> -            URL[] descriptorURLs = findDescriptors( m_bundle, descriptorLocation );
> -            if ( descriptorURLs.length == 0 )
> +            for (ComponentMetadata metadata : cachedComponentMetadata)
>             {
> -                // 112.4.1 If an XML document specified by the header cannot be located in the bundle and its attached
> -                // fragments, SCR must log an error message with the Log Service, if present, and continue.
> -                logger.log( LogService.LOG_ERROR, "Component descriptor entry ''{0}'' not found", null, descriptorLocation);
> -                continue;
> +                validateAndRegister(metadata);
>             }
> +        }
> +        else
> +        {
> +            // Get the Metadata-Location value from the manifest
> +            String descriptorLocations = m_bundle.getHeaders("").get("Service-Component");
> +            if (descriptorLocations == null)
> +            {
> +                throw new ComponentException(
> +                    "Service-Component entry not found in the manifest");
> +            }
> +
> +            logger.log(LogService.LOG_DEBUG,
> +                "BundleComponentActivator : Descriptor locations {0}", null,
> +                descriptorLocations);
> 
> -            // load from the descriptors
> -            for ( URL descriptorURL : descriptorURLs )
> +            // 112.4.1: The value of the the header is a comma separated list of XML entries within the Bundle
> +            StringTokenizer st = new StringTokenizer(descriptorLocations, ", ");
> +
> +            while (st.hasMoreTokens())
>             {
> -                loadDescriptor( descriptorURL );
> +                String descriptorLocation = st.nextToken();
> +
> +                URL[] descriptorURLs = findDescriptors(m_bundle, descriptorLocation);
> +                if (descriptorURLs.length == 0)
> +                {
> +                    // 112.4.1 If an XML document specified by the header cannot be located in the bundle and its attached
> +                    // fragments, SCR must log an error message with the Log Service, if present, and continue.
> +                    logger.log(LogService.LOG_ERROR,
> +                        "Component descriptor entry ''{0}'' not found", null,
> +                        descriptorLocation);
> +                    continue;
> +                }
> +
> +                // load from the descriptors
> +                for (URL descriptorURL : descriptorURLs)
> +                {
> +                    loadDescriptor(descriptorURL);
> +                }
>             }
>         }
>     }
> @@ -431,39 +445,7 @@ public class BundleComponentActivator im
>             // or one or more component elements embedded in a larger document
>             for ( ComponentMetadata metadata : handler.getComponentMetadataList() )
>             {
> -                final ComponentLogger componentLogger = new ComponentLogger(metadata, logger);
> -                ComponentRegistryKey key = null;
> -                try
> -                {
> -                    // validate the component metadata
> -                    metadata.validate( );
> -
> -                    // check and reserve the component name (validate ensures it's never null)
> -                    key = m_componentRegistry.checkComponentName( m_bundle, metadata.getName() );
> -
> -                    // Request creation of the component manager
> -                    ComponentHolder<?> holder = m_componentRegistry.createComponentHolder( this, metadata, componentLogger );
> -
> -                    // register the component after validation
> -                    m_componentRegistry.registerComponentHolder( key, holder );
> -                    m_holders.add( holder );
> -
> -                    componentLogger.log( LogService.LOG_DEBUG,
> -                        "BundleComponentActivator : ComponentHolder created.", null );
> -
> -                }
> -                catch ( Throwable t )
> -                {
> -                    // There is a problem with this particular component, we'll log the error
> -                    // and proceed to the next one
> -                    componentLogger.log( LogService.LOG_ERROR, "Cannot register component", t );
> -
> -                    // make sure the name is not reserved any more
> -                    if ( key != null )
> -                    {
> -                        m_componentRegistry.unregisterComponentHolder( key );
> -                    }
> -                }
> +                validateAndRegister(metadata);
>             }
>         }
>         catch ( IOException ex )
> @@ -492,6 +474,44 @@ public class BundleComponentActivator im
>         }
>     }
> 
> +    void validateAndRegister(ComponentMetadata metadata)
> +    {
> +        final ComponentLogger componentLogger = new ComponentLogger(metadata, logger);
> +        ComponentRegistryKey key = null;
> +        try
> +        {
> +            // validate the component metadata
> +            metadata.validate();
> +
> +            // check and reserve the component name (validate ensures it's never null)
> +            key = m_componentRegistry.checkComponentName(m_bundle, metadata.getName());
> +
> +            // Request creation of the component manager
> +            ComponentHolder<?> holder = m_componentRegistry.createComponentHolder(this,
> +                metadata, componentLogger);
> +
> +            // register the component after validation
> +            m_componentRegistry.registerComponentHolder(key, holder);
> +            m_holders.add(holder);
> +
> +            componentLogger.log(LogService.LOG_DEBUG,
> +                "BundleComponentActivator : ComponentHolder created.", null);
> +
> +        }
> +        catch (Throwable t)
> +        {
> +            // There is a problem with this particular component, we'll log the error
> +            // and proceed to the next one
> +            componentLogger.log(LogService.LOG_ERROR, "Cannot register component", t);
> +
> +            // make sure the name is not reserved any more
> +            if (key != null)
> +            {
> +                m_componentRegistry.unregisterComponentHolder(key);
> +            }
> +        }
> +    }
> +
>     /**
>     * Dispose of this component activator instance and all the component
>     * managers.
> @@ -649,7 +669,7 @@ public class BundleComponentActivator im
>      *      to the <code>name</code> parameter or <code>null</code> if no
>      *      component manager with the given name is currently registered.
>      */
> -    private List<ComponentHolder<?>> getSelectedComponents(String name)
> +    List<ComponentHolder<?>> getSelectedComponents(String name)
>     {
>         // if all components are selected
>         if ( name == null )
> 
> Modified: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfigurationImpl.java
> URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfigurationImpl.java?rev=1862491&r1=1862490&r2=1862491&view=diff
> ==============================================================================
> --- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfigurationImpl.java (original)
> +++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/config/ScrConfigurationImpl.java Wed Jul  3 16:35:54 2019
> @@ -81,6 +81,8 @@ public class ScrConfigurationImpl implem
> 
>     private boolean infoAsService;
> 
> +    private boolean cacheMetadata;
> +
>     private long lockTimeout = DEFAULT_LOCK_TIMEOUT_MILLISECONDS;
> 
>     private long stopTimeout = DEFAULT_STOP_TIMEOUT_MILLISECONDS;
> @@ -175,6 +177,7 @@ public class ScrConfigurationImpl implem
>                         stopTimeout = DEFAULT_STOP_TIMEOUT_MILLISECONDS;
>                         serviceChangecountTimeout = DEFAULT_SERVICE_CHANGECOUNT_TIMEOUT_MILLISECONDS;
>                         newGlobalExtender = false;
> +                        cacheMetadata = false;
>                     }
>                     else
>                     {
> @@ -186,6 +189,7 @@ public class ScrConfigurationImpl implem
>                         stopTimeout = getDefaultStopTimeout();
>                         serviceChangecountTimeout = getServiceChangecountTimeout();
>                         newGlobalExtender = getDefaultGlobalExtender();
> +                        cacheMetadata = getDefaultCacheMetadata();
>                     }
>                 }
>                 else
> @@ -204,6 +208,8 @@ public class ScrConfigurationImpl implem
>                 timeout = ( Long ) config.get( PROP_STOP_TIMEOUT );
>                 stopTimeout = timeout == null? DEFAULT_STOP_TIMEOUT_MILLISECONDS: timeout;
>                 newGlobalExtender = VALUE_TRUE.equalsIgnoreCase( String.valueOf( config.get( PROP_GLOBAL_EXTENDER) ) );
> +                cacheMetadata = VALUE_TRUE.equalsIgnoreCase(
> +                    String.valueOf(config.get(PROP_CACHE_METADATA)));
>             }
>             if ( scrCommand != null )
>             {
> @@ -268,6 +274,12 @@ public class ScrConfigurationImpl implem
>     }
> 
>     @Override
> +    public boolean cacheMetadata()
> +    {
> +        return cacheMetadata;
> +    }
> +
> +    @Override
>     public long serviceChangecountTimeout()
>     {
>         return serviceChangecountTimeout;
> @@ -330,6 +342,12 @@ public class ScrConfigurationImpl implem
>         return VALUE_TRUE.equalsIgnoreCase( bundleContext.getProperty( PROP_GLOBAL_EXTENDER) );
>     }
> 
> +    private boolean getDefaultCacheMetadata()
> +    {
> +        return VALUE_TRUE.equalsIgnoreCase(
> +            bundleContext.getProperty(PROP_CACHE_METADATA));
> +    }
> +
>     private int getLogLevel( final Object levelObject )
>     {
>         if ( levelObject != null )
> 
> Modified: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ScrConfiguration.java
> URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ScrConfiguration.java?rev=1862491&r1=1862490&r2=1862491&view=diff
> ==============================================================================
> --- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ScrConfiguration.java (original)
> +++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/manager/ScrConfiguration.java Wed Jul  3 16:35:54 2019
> @@ -68,6 +68,8 @@ public interface ScrConfiguration
> 
>     String PROP_SERVICE_CHANGECOUNT_TIMEOUT = "ds.service.changecount.timeout";
> 
> +    String PROP_CACHE_METADATA = "ds.cache.metadata";
> +
>     /**
>      * Returns the current log level.
>      * @return
> @@ -97,4 +99,6 @@ public interface ScrConfiguration
>      */
>     long serviceChangecountTimeout();
> 
> +    boolean cacheMetadata();
> +
> }
> 
> Modified: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java
> URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java?rev=1862491&r1=1862490&r2=1862491&view=diff
> ==============================================================================
> --- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java (original)
> +++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ComponentMetadata.java Wed Jul  3 16:35:54 2019
> @@ -18,6 +18,11 @@
>  */
> package org.apache.felix.scr.impl.metadata;
> 
> +import static org.apache.felix.scr.impl.metadata.MetadataStoreHelper.addString;
> +
> +import java.io.DataInputStream;
> +import java.io.DataOutputStream;
> +import java.io.IOException;
> import java.util.ArrayList;
> import java.util.Arrays;
> import java.util.Collections;
> @@ -25,9 +30,12 @@ import java.util.HashMap;
> import java.util.HashSet;
> import java.util.List;
> import java.util.Map;
> +import java.util.Map.Entry;
> import java.util.Set;
> import java.util.TreeSet;
> 
> +import org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataReader;
> +import org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataWriter;
> import org.apache.felix.scr.impl.metadata.ServiceMetadata.Scope;
> import org.osgi.service.component.ComponentException;
> 
> @@ -1084,4 +1092,548 @@ public class ComponentMetadata
>     {
>         return new ComponentException( "Component " + getName() + " validation failed: " + reason );
>     }
> +
> +    public void collectStrings(Set<String> strings)
> +    {
> +        addString(m_dsVersion.toString(), strings);
> +        addString(m_activate, strings);
> +        if (m_activationFields != null)
> +        {
> +            for (String s : m_activationFields)
> +            {
> +                addString(s, strings);
> +            }
> +        }
> +        if (m_configurationPid != null)
> +        {
> +            for (String s : m_configurationPid)
> +            {
> +                addString(s, strings);
> +            }
> +        }
> +        addString(m_configurationPolicy, strings);
> +        addString(m_deactivate, strings);
> +        addString(m_factory, strings);
> +        addString(m_implementationClassName, strings);
> +        addString(m_init, strings);
> +        addString(m_modified, strings);
> +        addString(m_name, strings);
> +        for (Entry<String, Object> entry : m_factoryProperties.entrySet())
> +        {
> +            collectStrings(entry, strings);
> +        }
> +        for (Entry<String, Object> entry : m_properties.entrySet())
> +        {
> +            collectStrings(entry, strings);
> +        }
> +        for (ReferenceMetadata rMeta : m_references)
> +        {
> +            rMeta.collectStrings(strings);
> +        }
> +        if (m_service != null)
> +        {
> +            m_service.collectStrings(strings);
> +        }
> +    }
> +
> +    private void collectStrings(Entry<String, Object> entry, Set<String> strings)
> +    {
> +        addString(entry.getKey(), strings);
> +        Object v = entry.getValue();
> +        if (v instanceof String)
> +        {
> +            addString((String) v, strings);
> +        }
> +        else if (v instanceof String[])
> +        {
> +            for (String s : (String[]) v)
> +            {
> +                addString(s, strings);
> +            }
> +        }
> +    }
> +
> +    public void store(DataOutputStream out, MetaDataWriter metaDataWriter)
> +        throws IOException
> +    {
> +        metaDataWriter.writeString(m_dsVersion.toString(), out);
> +        metaDataWriter.writeString(m_activate, out);
> +        out.writeBoolean(m_activationFields != null);
> +        if (m_activationFields != null)
> +        {
> +            out.writeInt(m_activationFields.size());
> +            for (String s : m_activationFields)
> +            {
> +                metaDataWriter.writeString(s, out);
> +            }
> +        }
> +        out.writeBoolean(m_configurationPid != null);
> +        if (m_configurationPid != null)
> +        {
> +            out.writeInt(m_configurationPid.size());
> +            for (String s : m_configurationPid)
> +            {
> +                metaDataWriter.writeString(s, out);
> +            }
> +        }
> +        metaDataWriter.writeString(m_configurationPolicy, out);
> +        metaDataWriter.writeString(m_deactivate, out);
> +        metaDataWriter.writeString(m_factory, out);
> +        metaDataWriter.writeString(m_implementationClassName, out);
> +        metaDataWriter.writeString(m_init, out);
> +        metaDataWriter.writeString(m_modified, out);
> +        metaDataWriter.writeString(m_name, out);
> +        out.writeInt(m_factoryProperties.size());
> +        for (Entry<String, Object> prop : m_factoryProperties.entrySet())
> +        {
> +            metaDataWriter.writeString(prop.getKey(), out);
> +            storePropertyValue(prop.getValue(), out, metaDataWriter);
> +        }
> +        out.writeInt(m_properties.size());
> +        for (Entry<String, Object> prop : m_properties.entrySet())
> +        {
> +            metaDataWriter.writeString(prop.getKey(), out);
> +            storePropertyValue(prop.getValue(), out, metaDataWriter);
> +        }
> +        out.writeInt(m_references.size());
> +        for (ReferenceMetadata rMeta : m_references)
> +        {
> +            rMeta.store(out, metaDataWriter);
> +        }
> +        out.writeBoolean(m_service != null);
> +        if (m_service != null)
> +        {
> +            m_service.store(out, metaDataWriter);
> +        }
> +        out.writeBoolean(m_activateDeclared);
> +        out.writeBoolean(m_configurableServiceProperties);
> +        out.writeBoolean(m_configureWithInterfaces);
> +        out.writeBoolean(m_deactivateDeclared);
> +        out.writeBoolean(m_delayedKeepInstances);
> +        out.writeBoolean(m_deleteCallsModify);
> +        out.writeBoolean(m_enabled);
> +        out.writeBoolean(m_immediate != null);
> +        if (m_immediate != null)
> +        {
> +            out.writeBoolean(m_immediate.booleanValue());
> +        }
> +    }
> +
> +    public static ComponentMetadata load(DataInputStream in,
> +        MetaDataReader metaDataReader)
> +        throws IOException
> +    {
> +        ComponentMetadata result = new ComponentMetadata(
> +            DSVersion.valueOf(metaDataReader.readString(in)));
> +        result.m_activate = metaDataReader.readString(in);
> +        if (in.readBoolean())
> +        {
> +            int size = in.readInt();
> +            String[] activationFields = new String[size];
> +            for (int i = 0; i < size; i++)
> +            {
> +                activationFields[i] = metaDataReader.readString(in);
> +            }
> +            result.setActivationFields(activationFields);
> +        }
> +        if (in.readBoolean())
> +        {
> +            int size = in.readInt();
> +            String[] configPids = new String[size];
> +            for (int i = 0; i < size; i++)
> +            {
> +                configPids[i] = metaDataReader.readString(in);
> +            }
> +            result.setConfigurationPid(configPids);
> +        }
> +        result.m_configurationPolicy = metaDataReader.readString(in);
> +        result.m_deactivate = metaDataReader.readString(in);
> +        result.m_factory = metaDataReader.readString(in);
> +        result.m_implementationClassName = metaDataReader.readString(in);
> +        result.m_init = metaDataReader.readString(in);
> +        result.m_modified = metaDataReader.readString(in);
> +        result.m_name = metaDataReader.readString(in);
> +        int numFProps = in.readInt();
> +        for (int i = 0; i < numFProps; i++)
> +        {
> +            result.m_factoryProperties.put(metaDataReader.readString(in),
> +                loadPropertyValue(in, metaDataReader));
> +        }
> +        int numProps = in.readInt();
> +        for (int i = 0; i < numProps; i++)
> +        {
> +            result.m_properties.put(metaDataReader.readString(in),
> +                loadPropertyValue(in, metaDataReader));
> +        }
> +        int numRefs = in.readInt();
> +        for (int i = 0; i < numRefs; i++)
> +        {
> +            result.addDependency(ReferenceMetadata.load(in, metaDataReader));
> +        }
> +        if (in.readBoolean())
> +        {
> +            result.m_service = ServiceMetadata.load(in, metaDataReader);
> +        }
> +        result.m_activateDeclared = in.readBoolean();
> +        result.m_configurableServiceProperties = in.readBoolean();
> +        result.m_configureWithInterfaces = in.readBoolean();
> +        result.m_deactivateDeclared = in.readBoolean();
> +        result.m_delayedKeepInstances = in.readBoolean();
> +        result.m_deleteCallsModify = in.readBoolean();
> +        result.m_enabled = in.readBoolean();
> +        if (in.readBoolean())
> +        {
> +            result.m_immediate = in.readBoolean();
> +        }
> +        // we only store valid metadata
> +        result.m_validated = true;
> +        return result;
> +    }
> +
> +    private static final byte TypeString = 1;
> +    private static final byte TypeLong = 2;
> +    private static final byte TypeDouble = 3;
> +    private static final byte TypeFloat = 4;
> +    private static final byte TypeInteger = 5;
> +    private static final byte TypeByte = 6;
> +    private static final byte TypeChar = 7;
> +    private static final byte TypeBoolean = 8;
> +    private static final byte TypeShort = 9;
> +
> +    static Object loadPropertyValue(DataInputStream in, MetaDataReader metaDataReader)
> +        throws IOException
> +    {
> +        boolean isArray = in.readBoolean();
> +        byte valueType = in.readByte();
> +        switch (valueType)
> +        {
> +            case TypeBoolean:
> +                if (isArray)
> +                {
> +                    boolean[] vArray = new boolean[in.readInt()];
> +                    for (int i = 0; i < vArray.length; i++)
> +                    {
> +                        vArray[i] = in.readBoolean();
> +                    }
> +                    return vArray;
> +                }
> +                else
> +                {
> +                    return Boolean.valueOf(in.readBoolean());
> +                }
> +            case TypeByte:
> +                if (isArray)
> +                {
> +                    byte[] vArray = new byte[in.readInt()];
> +                    for (int i = 0; i < vArray.length; i++)
> +                    {
> +                        vArray[i] = in.readByte();
> +                    }
> +                    return vArray;
> +                }
> +                else
> +                {
> +                    return Byte.valueOf(in.readByte());
> +                }
> +            case TypeChar:
> +                if (isArray)
> +                {
> +                    char[] vArray = new char[in.readInt()];
> +                    for (int i = 0; i < vArray.length; i++)
> +                    {
> +                        vArray[i] = in.readChar();
> +                    }
> +                    return vArray;
> +                }
> +                else
> +                {
> +                    return Character.valueOf(in.readChar());
> +                }
> +            case TypeDouble:
> +                if (isArray)
> +                {
> +                    double[] vArray = new double[in.readInt()];
> +                    for (int i = 0; i < vArray.length; i++)
> +                    {
> +                        vArray[i] = in.readDouble();
> +                    }
> +                    return vArray;
> +                }
> +                else
> +                {
> +                    return Double.valueOf(in.readDouble());
> +                }
> +            case TypeFloat:
> +                if (isArray)
> +                {
> +                    float[] vArray = new float[in.readInt()];
> +                    for (int i = 0; i < vArray.length; i++)
> +                    {
> +                        vArray[i] = in.readFloat();
> +                    }
> +                    return vArray;
> +                }
> +                else
> +                {
> +                    return Float.valueOf(in.readFloat());
> +                }
> +            case TypeInteger:
> +                if (isArray)
> +                {
> +                    int[] vArray = new int[in.readInt()];
> +                    for (int i = 0; i < vArray.length; i++)
> +                    {
> +                        vArray[i] = in.readInt();
> +                    }
> +                    return vArray;
> +                }
> +                else
> +                {
> +                    return Integer.valueOf(in.readInt());
> +                }
> +            case TypeLong:
> +                if (isArray)
> +                {
> +                    long[] vArray = new long[in.readInt()];
> +                    for (int i = 0; i < vArray.length; i++)
> +                    {
> +                        vArray[i] = in.readLong();
> +                    }
> +                    return vArray;
> +                }
> +                else
> +                {
> +                    return Long.valueOf(in.readLong());
> +                }
> +            case TypeShort:
> +                if (isArray)
> +                {
> +                    short[] vArray = new short[in.readInt()];
> +                    for (int i = 0; i < vArray.length; i++)
> +                    {
> +                        vArray[i] = in.readShort();
> +                    }
> +                    return vArray;
> +                }
> +                else
> +                {
> +                    return Short.valueOf(in.readShort());
> +                }
> +            case TypeString:
> +                if (isArray)
> +                {
> +                    String[] vArray = new String[in.readInt()];
> +                    for (int i = 0; i < vArray.length; i++)
> +                    {
> +                        vArray[i] = metaDataReader.readString(in);
> +                    }
> +                    return vArray;
> +                }
> +                else
> +                {
> +                    return metaDataReader.readString(in);
> +                }
> +        }
> +        return null;
> +    }
> +
> +    void storePropertyValue(Object value, DataOutputStream out,
> +        MetaDataWriter metaDataWriter) throws IOException
> +    {
> +        if (value == null)
> +        {
> +            // handle null value as a string
> +            out.writeBoolean(false);
> +            out.writeByte(TypeString);
> +            metaDataWriter.writeString(null, out);
> +            return;
> +        }
> +        Class<?> arrayType = value.getClass().getComponentType();
> +        boolean isArray = arrayType != null;
> +        out.writeBoolean(isArray);
> +        byte valueType = getType(arrayType == null ? value.getClass() : arrayType);
> +        out.writeByte(valueType);
> +        switch (valueType)
> +        {
> +            case TypeBoolean:
> +                if (isArray)
> +                {
> +                    boolean[] vArray = (boolean[]) value;
> +                    out.writeInt(vArray.length);
> +                    for (boolean v : vArray)
> +                    {
> +                        out.writeBoolean(v);
> +                    }
> +                }
> +                else
> +                {
> +                    out.writeBoolean((boolean) value);
> +                }
> +                break;
> +            case TypeByte:
> +                if (isArray)
> +                {
> +                    byte[] vArray = (byte[]) value;
> +                    out.writeInt(vArray.length);
> +                    for (byte v : vArray)
> +                    {
> +                        out.writeByte(v);
> +                    }
> +                }
> +                else
> +                {
> +                    out.writeByte((byte) value);
> +                }
> +                break;
> +            case TypeChar:
> +                if (isArray)
> +                {
> +                    char[] vArray = (char[]) value;
> +                    out.writeInt(vArray.length);
> +                    for (char v : vArray)
> +                    {
> +                        out.writeChar(v);
> +                    }
> +                }
> +                else
> +                {
> +                    out.writeChar((char) value);
> +                }
> +                break;
> +            case TypeDouble:
> +                if (isArray)
> +                {
> +                    double[] vArray = (double[]) value;
> +                    out.writeInt(vArray.length);
> +                    for (double v : vArray)
> +                    {
> +                        out.writeDouble(v);
> +                    }
> +                }
> +                else
> +                {
> +                    out.writeDouble((double) value);
> +                }
> +                break;
> +            case TypeFloat:
> +                if (isArray)
> +                {
> +                    float[] vArray = (float[]) value;
> +                    out.writeInt(vArray.length);
> +                    for (float v : vArray)
> +                    {
> +                        out.writeFloat(v);
> +                    }
> +                }
> +                else
> +                {
> +                    out.writeFloat((float) value);
> +                }
> +                break;
> +            case TypeInteger:
> +                if (isArray)
> +                {
> +                    int[] vArray = (int[]) value;
> +                    out.writeInt(vArray.length);
> +                    for (int v : vArray)
> +                    {
> +                        out.writeInt(v);
> +                    }
> +                }
> +                else
> +                {
> +                    out.writeInt((int) value);
> +                }
> +                break;
> +            case TypeLong:
> +                if (isArray)
> +                {
> +                    long[] vArray = (long[]) value;
> +                    out.writeInt(vArray.length);
> +                    for (long v : vArray)
> +                    {
> +                        out.writeLong(v);
> +                    }
> +                }
> +                else
> +                {
> +                    out.writeLong((long) value);
> +                }
> +                break;
> +            case TypeShort:
> +                if (isArray)
> +                {
> +                    short[] vArray = (short[]) value;
> +                    out.writeInt(vArray.length);
> +                    for (short v : vArray)
> +                    {
> +                        out.writeShort(v);
> +                    }
> +                }
> +                else
> +                {
> +                    out.writeShort((short) value);
> +                }
> +                break;
> +            case TypeString:
> +                if (isArray)
> +                {
> +                    String[] vArray = (String[]) value;
> +                    out.writeInt(vArray.length);
> +                    for (String v : vArray)
> +                    {
> +                        metaDataWriter.writeString(v, out);
> +                    }
> +
> +                }
> +                else
> +                {
> +                    metaDataWriter.writeString((String) value, out);
> +                }
> +                break;
> +        }
> +    }
> +
> +    private byte getType(Class<?> typeClass)
> +    {
> +        if (Boolean.class.equals(typeClass) || boolean.class.equals(typeClass))
> +        {
> +            return TypeBoolean;
> +        }
> +        if (Byte.class.equals(typeClass) || byte.class.equals(typeClass))
> +        {
> +            return TypeByte;
> +        }
> +        if (Character.class.equals(typeClass) || char.class.equals(typeClass))
> +        {
> +            return TypeChar;
> +        }
> +        if (Double.class.equals(typeClass) || double.class.equals(typeClass))
> +        {
> +            return TypeDouble;
> +        }
> +        if (Float.class.equals(typeClass) || float.class.equals(typeClass))
> +        {
> +            return TypeFloat;
> +        }
> +        if (Integer.class.equals(typeClass) || int.class.equals(typeClass))
> +        {
> +            return TypeInteger;
> +        }
> +        if (Long.class.equals(typeClass) || long.class.equals(typeClass))
> +        {
> +            return TypeLong;
> +        }
> +        if (Short.class.equals(typeClass) || short.class.equals(typeClass))
> +        {
> +            return TypeShort;
> +        }
> +        if (String.class.equals(typeClass))
> +        {
> +            return TypeString;
> +        }
> +        throw new IllegalArgumentException("Unsupported type: " + typeClass);
> +
> +    }
> +
> }
> 
> Added: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/MetadataStoreHelper.java
> URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/MetadataStoreHelper.java?rev=1862491&view=auto
> ==============================================================================
> --- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/MetadataStoreHelper.java (added)
> +++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/MetadataStoreHelper.java Wed Jul  3 16:35:54 2019
> @@ -0,0 +1,160 @@
> +/*
> + * 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.scr.impl.metadata;
> +
> +import java.io.DataInputStream;
> +import java.io.DataOutputStream;
> +import java.io.IOException;
> +import java.util.ArrayList;
> +import java.util.HashMap;
> +import java.util.List;
> +import java.util.Map;
> +import java.util.Set;
> +
> +public class MetadataStoreHelper
> +{
> +    static final byte STRING_NULL = 0;
> +    static final byte STRING_OBJECT = 1;
> +    static final byte STRING_INDEX = 2;
> +    static final byte STRING_LONG = 3;
> +
> +    public static class MetaDataReader
> +    {
> +        private final List<String> stringTable = new ArrayList<String>();
> +
> +        public String readIndexedString(DataInputStream in) throws IOException
> +        {
> +            String s = readString(in);
> +            addToStringTable(s, in.readInt());
> +            return s;
> +        }
> +
> +        public String readString(DataInputStream in) throws IOException
> +        {
> +            byte type = in.readByte();
> +            if (type == STRING_INDEX)
> +            {
> +                int index = in.readInt();
> +                return (String) stringTable.get(index);
> +            }
> +            if (type == STRING_NULL)
> +            {
> +                return null;
> +            }
> +            String s;
> +            if (type == STRING_LONG)
> +            {
> +                int length = in.readInt();
> +                byte[] data = new byte[length];
> +                in.readFully(data);
> +                s = new String(data, "UTF-8");
> +            }
> +            else
> +            {
> +                s = in.readUTF();
> +            }
> +            return s;
> +        }
> +
> +        private void addToStringTable(String s, int index)
> +        {
> +            if (index == stringTable.size())
> +            {
> +                stringTable.add(s);
> +            }
> +            else if (index < stringTable.size())
> +            {
> +                stringTable.set(index, s);
> +            }
> +            else
> +            {
> +                while (stringTable.size() < index)
> +                {
> +                    stringTable.add(null);
> +                }
> +                stringTable.add(s);
> +            }
> +        }
> +    }
> +
> +    public static class MetaDataWriter
> +    {
> +        private final Map<String, Integer> stringTable = new HashMap<>();
> +
> +        public void writeIndexedString(String s, DataOutputStream out) throws IOException
> +        {
> +            writeString(s, out);
> +            out.writeInt(addToStringTable(s));
> +        }
> +
> +        public void writeString(String s, DataOutputStream out) throws IOException
> +        {
> +            Integer index = s != null ? stringTable.get(s) : null;
> +            if (index != null)
> +            {
> +                out.writeByte(STRING_INDEX);
> +                out.writeInt(index);
> +                return;
> +            }
> +
> +            if (s == null)
> +                out.writeByte(STRING_NULL);
> +            else
> +            {
> +                byte[] data = s.getBytes("UTF-8");
> +
> +                if (data.length > 65535)
> +                {
> +                    out.writeByte(STRING_LONG);
> +                    out.writeInt(data.length);
> +                    out.write(data);
> +                }
> +                else
> +                {
> +                    out.writeByte(STRING_OBJECT);
> +                    out.writeUTF(s);
> +                }
> +            }
> +        }
> +
> +        private int addToStringTable(String s)
> +        {
> +            if (s == null)
> +            {
> +                throw new NullPointerException();
> +            }
> +            Integer cur = stringTable.get(s);
> +            if (cur != null)
> +                throw new IllegalStateException(
> +                    "String is already in the write table: " + s);
> +            int index = stringTable.size();
> +            stringTable.put(s, Integer.valueOf(index));
> +            // return the index of the object just added
> +            return index;
> +        }
> +    }
> +
> +    public static void addString(String s, Set<String> strings)
> +    {
> +        if (s != null)
> +        {
> +            strings.add(s);
> +        }
> +    }
> +}
> 
> Modified: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ReferenceMetadata.java
> URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ReferenceMetadata.java?rev=1862491&r1=1862490&r2=1862491&view=diff
> ==============================================================================
> --- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ReferenceMetadata.java (original)
> +++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ReferenceMetadata.java Wed Jul  3 16:35:54 2019
> @@ -18,9 +18,17 @@
>  */
> package org.apache.felix.scr.impl.metadata;
> 
> +import static org.apache.felix.scr.impl.metadata.MetadataStoreHelper.addString;
> +
> +import java.io.DataInputStream;
> +import java.io.DataOutputStream;
> +import java.io.IOException;
> import java.util.Set;
> import java.util.TreeSet;
> 
> +import org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataReader;
> +import org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataWriter;
> +
> /**
>  * Information associated to a dependency
>  *
> @@ -824,4 +832,86 @@ public class ReferenceMetadata
>                 ", collection-type=" + this.getFieldCollectionType() +
>                 ", parameter=" + this.getParameterIndex();
>     }
> +
> +    void collectStrings(Set<String> strings)
> +    {
> +        addString(m_bind, strings);
> +        addString(m_cardinality, strings);
> +        addString(m_collection_type, strings);
> +        addString(m_field, strings);
> +        addString(m_field_option, strings);
> +        addString(m_interface, strings);
> +        addString(m_name, strings);
> +        addString(m_parameter, strings);
> +        addString(m_policy, strings);
> +        addString(m_policy_option, strings);
> +        addString(m_scopeName, strings);
> +        addString(m_scope.toString(), strings);
> +        addString(m_target, strings);
> +        addString(m_unbind, strings);
> +        addString(m_updated, strings);
> +    }
> +
> +    void store(DataOutputStream out, MetaDataWriter metaDataWriter) throws IOException
> +    {
> +        metaDataWriter.writeString(m_bind, out);
> +        metaDataWriter.writeString(m_cardinality, out);
> +        metaDataWriter.writeString(m_collection_type, out);
> +        metaDataWriter.writeString(m_field, out);
> +        metaDataWriter.writeString(m_field_option, out);
> +        metaDataWriter.writeString(m_interface, out);
> +        out.writeBoolean(m_isMultiple);
> +        out.writeBoolean(m_isOptional);
> +        out.writeBoolean(m_isReluctant);
> +        out.writeBoolean(m_isReplace);
> +        out.writeBoolean(m_isStatic);
> +        metaDataWriter.writeString(m_name, out);
> +        metaDataWriter.writeString(m_parameter, out);
> +        out.writeBoolean(m_parameterIndex != null);
> +        if (m_parameterIndex != null)
> +        {
> +            out.writeInt(m_parameterIndex.intValue());
> +        }
> +        metaDataWriter.writeString(m_policy, out);
> +        metaDataWriter.writeString(m_policy_option, out);
> +        metaDataWriter.writeString(m_scopeName, out);
> +        metaDataWriter.writeString(m_scope.toString(), out);
> +        metaDataWriter.writeString(m_target, out);
> +        metaDataWriter.writeString(m_unbind, out);
> +        metaDataWriter.writeString(m_updated, out);
> +    }
> +
> +    static ReferenceMetadata load(DataInputStream in, MetaDataReader metaDataReader)
> +        throws IOException
> +    {
> +        ReferenceMetadata result = new ReferenceMetadata();
> +        result.m_bind = metaDataReader.readString(in);
> +        result.m_cardinality = metaDataReader.readString(in);
> +        result.m_collection_type = metaDataReader.readString(in);
> +        result.m_field = metaDataReader.readString(in);
> +        result.m_field_option = metaDataReader.readString(in);
> +        result.m_interface = metaDataReader.readString(in);
> +        result.m_isMultiple = in.readBoolean();
> +        result.m_isOptional = in.readBoolean();
> +        result.m_isReluctant = in.readBoolean();
> +        result.m_isReplace = in.readBoolean();
> +        result.m_isStatic = in.readBoolean();
> +        result.m_name = metaDataReader.readString(in);
> +        result.m_parameter = metaDataReader.readString(in);
> +        if (in.readBoolean())
> +        {
> +            result.m_parameterIndex = Integer.valueOf(in.readInt());
> +        }
> +        result.m_policy = metaDataReader.readString(in);
> +        result.m_policy_option = metaDataReader.readString(in);
> +        result.m_scopeName = metaDataReader.readString(in);
> +        result.m_scope = ReferenceScope.valueOf(metaDataReader.readString(in));
> +        result.m_target = metaDataReader.readString(in);
> +        result.m_unbind = metaDataReader.readString(in);
> +        result.m_updated = metaDataReader.readString(in);
> +
> +        // only stored valid metadata
> +        result.m_validated = true;
> +        return result;
> +    }
> }
> \ No newline at end of file
> 
> Modified: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ServiceMetadata.java
> URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ServiceMetadata.java?rev=1862491&r1=1862490&r2=1862491&view=diff
> ==============================================================================
> --- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ServiceMetadata.java (original)
> +++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/metadata/ServiceMetadata.java Wed Jul  3 16:35:54 2019
> @@ -18,8 +18,17 @@
>  */
> package org.apache.felix.scr.impl.metadata;
> 
> +import static org.apache.felix.scr.impl.metadata.MetadataStoreHelper.addString;
> +
> +import java.io.DataInputStream;
> +import java.io.DataOutputStream;
> +import java.io.IOException;
> import java.util.ArrayList;
> import java.util.List;
> +import java.util.Set;
> +
> +import org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataReader;
> +import org.apache.felix.scr.impl.metadata.MetadataStoreHelper.MetaDataWriter;
> 
> /**
>  * This class contains the metadata associated to a service that is provided
> @@ -134,4 +143,50 @@ public class ServiceMetadata {
>         }
>         m_validated = true;
>     }
> +
> +    void collectStrings(Set<String> strings)
> +    {
> +        for (String s : m_provides)
> +        {
> +            addString(s, strings);
> +        }
> +        addString(m_scopeName, strings);
> +        addString(m_scope.toString(), strings);
> +    }
> +
> +    void store(DataOutputStream out, MetaDataWriter metaDataWriter) throws IOException
> +    {
> +        out.writeInt(m_provides.size());
> +        for (String s : m_provides)
> +        {
> +            metaDataWriter.writeString(s, out);
> +        }
> +        metaDataWriter.writeString(m_scopeName, out);
> +        metaDataWriter.writeString(m_scope.toString(), out);
> +        out.writeBoolean(m_serviceFactory != null);
> +        if (m_serviceFactory != null)
> +        {
> +            out.writeBoolean(m_serviceFactory.booleanValue());
> +        }
> +    }
> +
> +    static ServiceMetadata load(DataInputStream in, MetaDataReader metaDataReader)
> +        throws IOException
> +    {
> +        ServiceMetadata result = new ServiceMetadata();
> +        int providerSize = in.readInt();
> +        for (int i = 0; i < providerSize; i++)
> +        {
> +            result.addProvide(metaDataReader.readString(in));
> +        }
> +        result.m_scopeName = metaDataReader.readString(in);
> +        result.m_scope = Scope.valueOf(metaDataReader.readString(in));
> +        if (in.readBoolean())
> +        {
> +            result.m_serviceFactory = in.readBoolean();
> +        }
> +        // only stored valid metadata
> +        result.m_validated = true;
> +        return result;
> +    }
> }
> 
> Modified: felix/trunk/scr/src/test/java/org/apache/felix/scr/impl/logger/MockScrLogger.java
> URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/test/java/org/apache/felix/scr/impl/logger/MockScrLogger.java?rev=1862491&r1=1862490&r2=1862491&view=diff
> ==============================================================================
> --- felix/trunk/scr/src/test/java/org/apache/felix/scr/impl/logger/MockScrLogger.java (original)
> +++ felix/trunk/scr/src/test/java/org/apache/felix/scr/impl/logger/MockScrLogger.java Wed Jul  3 16:35:54 2019
> @@ -78,6 +78,12 @@ public class MockScrLogger extends ScrLo
>             public boolean globalExtender() {
>                 return false;
>             }
> +
> +            @Override
> +            public boolean cacheMetadata()
> +            {
> +                return false;
> +            }
>         }, new MockBundleContext(new MockBundle()));
>     }
> }
> \ No newline at end of file
> 
>