FELIX-110 completion of the current Felix SCR implementation to take into account components <properties> elements

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@580966 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/main/java/org/apache/felix/scr/BundleComponentActivator.java b/scr/src/main/java/org/apache/felix/scr/BundleComponentActivator.java
index 3080aa3..4fb57c8 100644
--- a/scr/src/main/java/org/apache/felix/scr/BundleComponentActivator.java
+++ b/scr/src/main/java/org/apache/felix/scr/BundleComponentActivator.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -77,7 +77,7 @@
      *      register components with to ensure uniqueness of component names
      *      and to ensure configuration updates.
      * @param   context  The bundle context owning the components
-     * 
+     *
      * @throws ComponentException if any error occurrs initializing this class
      */
     BundleComponentActivator( ComponentRegistry componentRegistry, ComponentActorThread componentActor,
@@ -114,16 +114,16 @@
     /**
      * 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>.
-     *      
+     *
      * @throws IllegalStateException If the bundle has already been uninstalled.
      */
     private void initialize( String descriptorLocations )
     {
 
-        // 112.4.1: The value of the the header is a comma separated list of XML entries within the Bundle 
+        // 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() )
@@ -146,7 +146,7 @@
                 stream = descriptorURL.openStream();
 
                 BufferedReader in = new BufferedReader( new InputStreamReader( stream ) );
-                XmlHandler handler = new XmlHandler();
+                XmlHandler handler = new XmlHandler( m_context.getBundle() );
                 KXml2SAXParser parser;
 
                 parser = new KXml2SAXParser( in );
@@ -334,8 +334,8 @@
      * then starting a thread to actually enable all components found.
      * <p>
      * If no component matching the given name is found the thread is not
-     * started and the method does nothing. 
-     * 
+     * started and the method does nothing.
+     *
      * @param name The name of the component to enable or <code>null</code> to
      *      enable all components.
      */
@@ -367,8 +367,8 @@
      * then starting a thread to actually disable all components found.
      * <p>
      * If no component matching the given name is found the thread is not
-     * started and the method does nothing. 
-     * 
+     * started and the method does nothing.
+     *
      * @param name The name of the component to disable or <code>null</code> to
      *      disable all components.
      */
@@ -401,10 +401,10 @@
      * an array containing a single component manager matching the name is
      * returned if one is registered. Finally, if no component manager with the
      * given name is registered, <code>null</code> is returned.
-     *  
+     *
      * @param name The name of the component manager to return or
      *      <code>null</code> to return an array of all component managers.
-     *      
+     *
      * @return An array containing one or more component managers according
      *      to the <code>name</code> parameter or <code>null</code> if no
      *      component manager with the given name is currently registered.
@@ -443,7 +443,7 @@
      * Schedules the given <code>task</code> for asynchrounous execution or
      * synchronously runs the task if the thread is not running. If this instance
      * is {@link #isActive() not active}, the task is not executed.
-     * 
+     *
      * @param task The component task to execute
      */
     void schedule( Runnable task )
@@ -482,7 +482,7 @@
      * Method to actually emit the log message. If the LogService is available,
      * the message will be logged through the LogService. Otherwise the message
      * is logged to stdout (or stderr in case of LOG_ERROR level messages),
-     * 
+     *
      * @param level The log level to log the message at
      * @param message The message to log
      * @param ex An optional <code>Throwable</code> whose stack trace is written,
diff --git a/scr/src/main/java/org/apache/felix/scr/XmlHandler.java b/scr/src/main/java/org/apache/felix/scr/XmlHandler.java
index 78b0613..338e44d 100644
--- a/scr/src/main/java/org/apache/felix/scr/XmlHandler.java
+++ b/scr/src/main/java/org/apache/felix/scr/XmlHandler.java
@@ -19,12 +19,18 @@
 package org.apache.felix.scr;
 
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Properties;
 
 import org.apache.felix.scr.parser.KXml2SAXHandler;
 import org.apache.felix.scr.parser.ParseException;
+import org.osgi.framework.Bundle;
 
 
 /**
@@ -36,6 +42,9 @@
 
     public static final String NAMESPACE_URI = "http://www.osgi.org/xmlns/scr/v1.0.0";
 
+    // the bundle containing the XML resource being parsed
+    private Bundle m_bundle;
+
     // A reference to the current component
     private ComponentMetadata m_currentComponent;
 
@@ -55,6 +64,14 @@
     protected String overrideNamespace;
 
 
+    // creates an instance with the bundle owning the component descriptor
+    // file parsed by this instance
+    XmlHandler( Bundle bundle )
+    {
+        m_bundle = bundle;
+    }
+
+
     /**
      * Method called when a tag opens
      *
@@ -124,7 +141,7 @@
                     // Set the implementation class name (mandatory)
                     m_currentComponent.setImplementationClassName( attrib.getProperty( "class" ) );
                 }
-                // 112.4.5 Properties and Property Elements
+                // 112.4.5 [...] Property Elements
                 else if ( localName.equals( "property" ) )
                 {
                     PropertyMetadata prop = new PropertyMetadata();
@@ -149,11 +166,11 @@
                         // hold the metadata pending
                         m_pendingProperty = prop;
                     }
-                    // TODO: treat the case where a properties file name is provided (p. 292)
                 }
+                // 112.4.5 Properties [...] Elements
                 else if ( localName.equals( "properties" ) )
                 {
-                    // TODO: implement the properties tag
+                    readPropertiesEntry( attrib.getProperty( "entry" ) );
                 }
                 // 112.4.6 Service Element
                 else if ( localName.equals( "service" ) )
@@ -293,4 +310,67 @@
     {
         // Not used
     }
+
+
+    /**
+     * Reads the name property file from the bundle owning this descriptor. All
+     * properties read from the properties file are added to the current
+     * component's property meta data list.
+     *
+     * @param entryName The name of the bundle entry containing the propertes
+     *      to be added. This must not be <code>null</code>.
+     *
+     * @throws ParseException If the entry name is <code>null</code> or no
+     *      entry with the given name exists in the bundle or an error occurrs
+     *      reading the properties file.
+     */
+    private void readPropertiesEntry( String entryName ) throws ParseException
+    {
+        if ( entryName == null )
+        {
+            throw new ParseException( "Missing entry attribute of properties element", null );
+        }
+
+        URL entryURL = m_bundle.getEntry( entryName );
+        if ( entryURL == null )
+        {
+            throw new ParseException( "Missing bundle entry " + entryName, null );
+        }
+
+        Properties props = new Properties();
+        InputStream entryStream = null;
+        try
+        {
+            entryStream = entryURL.openStream();
+            props.load( entryStream );
+        }
+        catch ( IOException ioe )
+        {
+            throw new ParseException( "Failed to read properties entry " + entryName, ioe );
+        }
+        finally
+        {
+            if ( entryStream != null )
+            {
+                try
+                {
+                    entryStream.close();
+                }
+                catch ( IOException ignore )
+                {
+                    // don't care
+                }
+            }
+        }
+
+        // create PropertyMetadata for the properties from the file
+        for ( Iterator pi = props.entrySet().iterator(); pi.hasNext(); )
+        {
+            Map.Entry pEntry = ( Map.Entry ) pi.next();
+            PropertyMetadata prop = new PropertyMetadata();
+            prop.setName( String.valueOf( pEntry.getKey() ) );
+            prop.setValue( String.valueOf( pEntry.getValue() ) );
+            m_currentComponent.addProperty( prop );
+        }
+    }
 }