This is an automated email from the ASF dual-hosted git repository.

cziegeler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/felix-dev.git


The following commit(s) were added to refs/heads/master by this push:
     new 09c897e  FELIX-6436 : Exclude default values (from metatype) in 
Configuration
09c897e is described below

commit 09c897e34d7d4f00084a89cc6113bc233b98f70a
Author: Carsten Ziegeler <[email protected]>
AuthorDate: Mon Jun 28 09:17:58 2021 +0200

    FELIX-6436 : Exclude default values (from metatype) in Configuration
---
 .../org/apache/felix/webconsole/internal/Util.java |  10 +
 .../internal/configuration/ConfigAdminSupport.java | 284 ++++++++++++---------
 .../internal/configuration/ConfigManager.java      | 111 ++++----
 .../configuration/MetatypePropertyDescriptor.java  |   6 +
 .../internal/configuration/PropertyDescriptor.java |   4 +
 5 files changed, 250 insertions(+), 165 deletions(-)

diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/Util.java 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/Util.java
index 115f8ef..398f0aa 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/Util.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/Util.java
@@ -17,12 +17,15 @@
 package org.apache.felix.webconsole.internal;
 
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.Enumeration;
 import java.util.Locale;
 
+import javax.servlet.http.HttpServletResponse;
+
 import org.osgi.framework.Bundle;
 import org.osgi.framework.Constants;
 import org.osgi.framework.Version;
@@ -242,4 +245,11 @@ public class Util
             return 1;
         }
     }
+
+    public static void sendJsonOk(final HttpServletResponse response) throws 
IOException
+    {
+        response.setContentType( "application/json" ); //$NON-NLS-1$
+        response.setCharacterEncoding( "UTF-8" ); //$NON-NLS-1$
+        response.getWriter().print( "{ \"status\": true }" ); //$NON-NLS-1$
+    }
 }
\ No newline at end of file
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigAdminSupport.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigAdminSupport.java
index fb6a2ec..b876f26 100644
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigAdminSupport.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigAdminSupport.java
@@ -23,6 +23,7 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.reflect.Array;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Dictionary;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -35,7 +36,6 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.SortedMap;
-import java.util.StringTokenizer;
 import java.util.TreeMap;
 import java.util.Vector;
 import java.util.regex.Matcher;
@@ -162,175 +162,227 @@ class ConfigAdminSupport
         return ConfigManager.PLACEHOLDER_PID;
     }
 
-    String applyConfiguration( final HttpServletRequest request, final String 
pid, final boolean isUpdate )
-            throws IOException
+    boolean shouldSet(final PropertyDescriptor ad, final String value, final 
boolean isUpdate) 
     {
-        if ( request.getParameter( ConfigManager.ACTION_DELETE ) != null ) 
//$NON-NLS-1$
+        if ( ad.hasMetatype() && !isUpdate )
         {
-            // only delete if the PID is not our place holder
-            if ( !ConfigManager.PLACEHOLDER_PID.equals( pid ) )
+            if ( value.isEmpty() && ad.getDefaultValue() == null )
+            {
+                return false;
+            }
+            if ( ad.getDefaultValue() != null && 
value.equals(ad.getDefaultValue()[0]) )
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    boolean shouldSet(final PropertyDescriptor ad, final String[] values, 
final boolean isUpdate) 
+    {
+        if ( ad.hasMetatype() && !isUpdate )
+        {
+            if ( ad.getDefaultValue() == null )
+            {
+                if ( values.length == 0 || (values.length == 1 && 
values[0].isEmpty() ) )
+                {
+                    return false;
+                }
+            }
+            if ( ad.getDefaultValue() != null && 
Arrays.equals(ad.getDefaultValue(), values) )
             {
-                configManager.log( "applyConfiguration: Deleting configuration 
" + pid );
-                Configuration config = service.getConfiguration( pid, null );
-                config.delete();
+                return false;
             }
-            return null; // return request.getHeader( "Referer" );
         }
+        return true;
+    }
 
+    boolean applyConfiguration( final HttpServletRequest request, final String 
pid, final String propertyList, final boolean isUpdate )
+            throws IOException
+    {
         final String factoryPid = request.getParameter( 
ConfigManager.FACTORY_PID );
-        Configuration config = null;
+        final Configuration config = getConfiguration( pid, factoryPid );
 
-        String propertyList = request.getParameter( 
ConfigManager.PROPERTY_LIST ); //$NON-NLS-1$
-        if ( propertyList == null )
+        Dictionary<String, Object> props = config.getProperties();
+        if ( props == null )
         {
-            // FIXME: this would be a bug !!
+            props = new Hashtable<>();
         }
-        else
+
+        final MetaTypeServiceSupport mtss = getMetaTypeSupport();
+        final Map<String, MetatypePropertyDescriptor> adMap = ( mtss != null ) 
? mtss.getAttributeDefinitionMap( config, null ) : new HashMap<>();
+        final List<String> propsToKeep = new ArrayList<>();
+        for(final String propName : propertyList.split(","))
         {
-            config = getConfiguration( pid, factoryPid );
+            final String paramName = "action".equals(propName) //$NON-NLS-1$
+                    || ConfigManager.ACTION_DELETE.equals(propName)
+                    || ConfigManager.ACTION_APPLY.equals(propName)
+                    || ConfigManager.PROPERTY_LIST.equals(propName)
+                    ? '$' + propName : propName;
+            propsToKeep.add(propName);
 
-            Dictionary<String, Object> props = config.getProperties();
-            if ( props == null )
-            {
-                props = new Hashtable<>();
-            }
+            PropertyDescriptor ad = adMap.get( propName );
 
-            final MetaTypeServiceSupport mtss = getMetaTypeSupport();
-            final Map<String, MetatypePropertyDescriptor> adMap = ( mtss != 
null ) ? mtss.getAttributeDefinitionMap( config, null ) : new HashMap<>();
-            final StringTokenizer propTokens = new StringTokenizer( 
propertyList, "," ); //$NON-NLS-1$
-            final List<String> propsToKeep = new ArrayList<>();
-            while ( propTokens.hasMoreTokens() )
-            {
-                String propName = propTokens.nextToken();
-                String paramName = "action".equals(propName) //$NON-NLS-1$
-                        || ConfigManager.ACTION_DELETE.equals(propName)
-                        || ConfigManager.ACTION_APPLY.equals(propName)
-                        || ConfigManager.PROPERTY_LIST.equals(propName)
-                        ? '$' + propName : propName;
-                propsToKeep.add(propName);
-
-                PropertyDescriptor ad = adMap.get( propName );
-
-                // try to derive from current value
-                if (ad == null) {
-                    Object currentValue = props.get( propName );
-                    ad = MetaTypeSupport.createAttributeDefinition( propName, 
currentValue );
-                }
+            // try to derive from current value
+            if (ad == null) {
+                Object currentValue = props.get( propName );
+                ad = MetaTypeSupport.createAttributeDefinition( propName, 
currentValue );
+            }
 
-                int attributeType = MetaTypeSupport.getAttributeType( ad );
+            final int attributeType = MetaTypeSupport.getAttributeType( ad );
 
-                if ( ad == null
-                        || ( ad.getCardinality() == 0 && ( attributeType == 
AttributeDefinition.STRING || attributeType == AttributeDefinition.PASSWORD ) ) 
)
+            if ( ad.getCardinality() == 0 && ( attributeType == 
AttributeDefinition.STRING || attributeType == AttributeDefinition.PASSWORD ) )
+            {
+                final String value = request.getParameter( paramName );
+                if ( value != null
+                    && ( attributeType != AttributeDefinition.PASSWORD || 
!MetaTypeSupport.PASSWORD_PLACEHOLDER_VALUE.equals( value ) ) )
                 {
-                    String prop = request.getParameter( paramName );
-                    if ( prop != null
-                            && ( attributeType != AttributeDefinition.PASSWORD 
|| !MetaTypeSupport.PASSWORD_PLACEHOLDER_VALUE.equals( prop ) ) )
+                    if ( shouldSet(ad, value, isUpdate) ) 
                     {
-                        props.put( propName, prop );
+                        props.put( propName, value );
+                    }
+                    else
+                    {
+                        props.remove( propName );
                     }
                 }
-                else if ( ad.getCardinality() == 0 )
+            }
+            else if ( ad.getCardinality() == 0 )
+            {
+                // scalar of non-string
+                final String value = request.getParameter( paramName );
+                if ( value != null )
                 {
-                    // scalar of non-string
-                    String prop = request.getParameter( paramName );
-                    if ( prop != null )
+                    if ( shouldSet(ad, value, isUpdate) ) 
                     {
                         try
                         {
-                            props.put( propName, MetaTypeSupport.toType( 
attributeType, prop ) );
+                            props.put( propName, MetaTypeSupport.toType( 
attributeType, value ) );
                         }
-                        catch ( NumberFormatException nfe )
+                        catch ( final NumberFormatException nfe )
                         {
                             // the value is put as a string, for example this 
could be a placeholder etc
-                            props.put( propName, prop);
+                            props.put( propName, value);
                         }
                     }
+                    else
+                    {
+                        props.remove( propName );
+                    }
                 }
-                else
-                {
-                    // array or vector of any type
-                    Vector<Object> vec = new Vector<>();
-                    boolean formatError = false;
+            }
+            else
+            {
+                // array or vector of any type
+                Vector<Object> vec = new Vector<>();
+                boolean formatError = false;
 
-                    String[] properties = request.getParameterValues( 
paramName );
-                    if ( properties != null )
+                final String[] values = request.getParameterValues( paramName 
);
+                if ( values != null )
+                {
+                    if ( attributeType == AttributeDefinition.PASSWORD )
                     {
-                        if ( attributeType == AttributeDefinition.PASSWORD )
-                        {
-                            MetaTypeSupport.setPasswordProps( vec, properties, 
props.get( propName ) );
-                        }
-                        else
+                        MetaTypeSupport.setPasswordProps( vec, values, 
props.get( propName ) );
+                    }
+                    else
+                    {
+                        for ( int i = 0; i < values.length; i++ )
                         {
-                            for ( int i = 0; i < properties.length; i++ )
+                            try
                             {
-                                try
-                                {
-                                    vec.add( MetaTypeSupport.toType( 
attributeType, properties[i] ) );
-                                }
-                                catch ( NumberFormatException nfe )
-                                {
-                                    // the value is put as a string, for 
example this could be a placeholder etc
-                                    vec.add( properties[i] );
-                                    formatError = true;
-                                }
+                                vec.add( MetaTypeSupport.toType( 
attributeType, values[i] ) );
+                            }
+                            catch ( NumberFormatException nfe )
+                            {
+                                // the value is put as a string, for example 
this could be a placeholder etc
+                                vec.add( values[i] );
+                                formatError = true;
                             }
                         }
                     }
+                }
 
-                    // if a format error occurred revert to String!
-                    if ( formatError )
+                // if a format error occurred revert to String!
+                if ( formatError )
+                {
+                    Vector<Object> newVec = new Vector<Object>();
+                    for(final Object v : vec)
                     {
-                        Vector<Object> newVec = new Vector<Object>();
-                        for(final Object v : vec)
-                        {
-                            newVec.add(v.toString());
-                        }
-                        vec = newVec;
+                        newVec.add(v.toString());
                     }
+                    vec = newVec;
+                }
+
+                // but ensure size (check for positive value since
+                // abs(Integer.MIN_VALUE) is still INTEGER.MIN_VALUE)
+                int maxSize = Math.abs( ad.getCardinality() );
+                if ( vec.size() > maxSize && maxSize > 0 )
+                {
+                    vec.setSize( maxSize );
+                }
 
-                    // but ensure size (check for positive value since
-                    // abs(Integer.MIN_VALUE) is still INTEGER.MIN_VALUE)
-                    int maxSize = Math.abs( ad.getCardinality() );
-                    if ( vec.size() > maxSize && maxSize > 0 )
+                // create array to compare
+                final String[] valueArray = new String[vec.size()];
+                for(int i=0; i<vec.size();i++) 
+                {
+                    valueArray[i] = vec.get(i).toString();
+                }
+                
+                final boolean shouldSet = shouldSet(ad, valueArray, isUpdate);
+
+                if ( ad.getCardinality() < 0 )
+                {
+                    // keep the vector, but only add if not empty
+                    if ( !shouldSet || vec.isEmpty() )
                     {
-                        vec.setSize( maxSize );
+                        props.remove( propName );
                     }
-
-                    if ( ad.getCardinality() < 0 )
+                    else
                     {
-                        // keep the vector, but only add if not empty
-                        if ( vec.isEmpty() )
-                        {
-                            props.remove( propName );
-                        }
-                        else
+                        if ( shouldSet )
                         {
                             props.put( propName, vec );
-                        }
+                        }                        
                     }
-                    else
+                }
+                else
+                {
+                    if ( shouldSet )
                     {
                         // convert to an array
                         props.put( propName, MetaTypeSupport.toArray( 
formatError ? AttributeDefinition.STRING : attributeType, vec ) );
                     }
+                    else
+                    {
+                        props.remove( propName );
+                    }
                 }
             }
+        }
 
-            if ( !isUpdate ) {
-                // remove the properties that are not specified in the request
-                final Dictionary<String, Object> updateProps = new 
Hashtable<>(props.size());
-                for ( Enumeration<String> e = props.keys(); 
e.hasMoreElements(); )
+        if ( !isUpdate ) 
+        {
+            // remove the properties that are not specified in the request
+            final Dictionary<String, Object> updateProps = new 
Hashtable<>(props.size());
+            for ( Enumeration<String> e = props.keys(); e.hasMoreElements(); )
+            {
+                final String key = e.nextElement();
+                if ( propsToKeep.contains(key) && props.get(key) != null )
                 {
-                    final String key = e.nextElement();
-                    if ( propsToKeep.contains(key) )
-                    {
-                        updateProps.put(key, props.get(key));
-                    }
+                    updateProps.put(key, props.get(key));
                 }
-                props = updateProps;
             }
+            props = updateProps;
+        }
 
+        if ( props.isEmpty() )
+        {
+            config.delete();
 
+            return false;
+        }
+        else
+        {
             final String location = 
request.getParameter(ConfigManager.LOCATION);
             if ( location == null || location.trim().length() == 0 || 
ConfigManager.UNBOUND_LOCATION.equals(location) )
             {
@@ -347,7 +399,8 @@ class ConfigAdminSupport
                         config.setBundleLocation( null );
                     }
                 }
-            } else
+            } 
+            else
             {
                 if ( config.getBundleLocation() == null || 
!config.getBundleLocation().equals(location) )
                 {
@@ -355,10 +408,9 @@ class ConfigAdminSupport
                 }
             }
             config.update( props );
+    
+            return true;
         }
-
-        // redirect to the new configuration (if existing)
-        return (config != null) ? config.getPid() : ""; //$NON-NLS-1$
     }
 
 
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigManager.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigManager.java
index 8a8284a..37145c0 100644
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigManager.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigManager.java
@@ -28,11 +28,11 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.felix.utils.json.JSONWriter;
-import org.apache.felix.webconsole.DefaultVariableResolver;
 import org.apache.felix.webconsole.SimpleWebConsolePlugin;
 import org.apache.felix.webconsole.WebConsoleConstants;
 import org.apache.felix.webconsole.WebConsoleUtil;
 import org.apache.felix.webconsole.internal.OsgiManagerPlugin;
+import org.apache.felix.webconsole.internal.Util;
 import org.osgi.framework.Constants;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.service.cm.Configuration;
@@ -118,6 +118,14 @@ public class ConfigManager extends SimpleWebConsolePlugin 
implements OsgiManager
     @Override
     protected void doPost( HttpServletRequest request, HttpServletResponse 
response ) throws IOException
     {
+        // service unavailable if config admin is not available
+        final ConfigAdminSupport cas = getConfigurationAdminSupport();
+        if ( cas == null )
+        {
+            response.sendError(503);
+            return;
+        }
+
         // needed multiple times below
         String pid = request.getParameter( ConfigManager.PID );
         if ( pid == null )
@@ -125,62 +133,71 @@ public class ConfigManager extends SimpleWebConsolePlugin 
implements OsgiManager
             String info = request.getPathInfo();
             pid = WebConsoleUtil.urlDecode( info.substring( info.lastIndexOf( 
'/' ) + 1 ) );
         }
-
-        // the filter to select part of the configurations
-        String pidFilter = request.getParameter( PID_FILTER );
-
-        final ConfigAdminSupport cas = getConfigurationAdminSupport();
-
-        // ignore this request if the PID and/or configuration admin is missing
-        if ( pid == null || pid.length() == 0 || cas == null )
+        // ignore this request if the PID is invalid / not provided
+        if ( pid == null || pid.length() == 0 || !isAllowedPid(pid))
         {
-            // should log this here !!
+            response.sendError(400);
             return;
         }
 
-        // ignore this request, if the PID is invalid
-        if ( ! isAllowedPid(pid) )
+        // the filter to select part of the configurations
+        final String pidFilter = request.getParameter( PID_FILTER );
+        if ( pidFilter != null && ! isAllowedPid(pidFilter) )
         {
-            response.sendError(500);
+            response.sendError(400);
             return;
         }
-        if ( pidFilter != null && ! isAllowedPid(pidFilter) )
+
+        if ( request.getParameter( ACTION_APPLY ) != null )
         {
-            response.sendError(500);
+            if ( request.getParameter( ConfigManager.ACTION_DELETE ) != null ) 
//$NON-NLS-1$
+            {
+                // only delete if the PID is not our place holder
+                if ( !ConfigManager.PLACEHOLDER_PID.equals( pid ) )
+                {
+                    this.log( "applyConfiguration: Deleting configuration " + 
pid );
+                    cas.getConfiguration( pid, null ).delete();
+                }            
+
+                Util.sendJsonOk(response);
+            } 
+            else 
+            {
+                final String propertyList = request.getParameter( 
ConfigManager.PROPERTY_LIST ); //$NON-NLS-1$
+                if ( propertyList == null )
+                {
+                    response.sendError(400);
+                    return;        
+                }
+
+                if (cas.applyConfiguration( request, pid, propertyList, 
ACTION_UPDATE.equals(request.getParameter(ACTION_APPLY)) ) )
+                {
+                    String redirect = pid;
+                    if (pidFilter != null) {
+                        redirect += '?' + PID_FILTER + '=' + pidFilter;
+                    }
+    
+                    WebConsoleUtil.sendRedirect(request, response, redirect);  
  
+                }
+                else
+                {
+                    Util.sendJsonOk(response);
+                }
+            }
+            
             return;
         }
 
         // the configuration to operate on (to be created or "missing")
-        Configuration config = null;
+        final Configuration config;
 
         // should actually apply the configuration before redirecting
         if ( request.getParameter( ACTION_CREATE ) != null )
         {
-            config = cas.getPlaceholderConfiguration( pid ); // 
ca.createFactoryConfiguration( pid, null );
+            config = cas.getPlaceholderConfiguration( pid );
             pid = config.getPid();
         }
-        else if ( request.getParameter( ACTION_APPLY ) != null )
-        {
-            String redirect = cas.applyConfiguration( request, pid, 
ACTION_UPDATE.equals(request.getParameter(ACTION_APPLY)) );
-            if ( redirect != null )
-            {
-                if (pidFilter != null) {
-                    redirect += '?' + PID_FILTER + '=' + pidFilter;
-                }
-
-                WebConsoleUtil.sendRedirect(request, response, redirect);
-            }
-            else
-            {
-                response.setContentType( "application/json" ); //$NON-NLS-1$
-                response.setCharacterEncoding( "UTF-8" ); //$NON-NLS-1$
-                response.getWriter().print( "{ \"status\": true }" ); 
//$NON-NLS-1$
-            }
-
-            return;
-        }
-
-        if ( config == null )
+        else
         {
             config = cas.getConfiguration( pid );
         }
@@ -193,9 +210,7 @@ public class ConfigManager extends SimpleWebConsolePlugin 
implements OsgiManager
                 config.setBundleLocation( UNBOUND_LOCATION );
 
             }
-            response.setContentType( "application/json" ); //$NON-NLS-1$
-            response.setCharacterEncoding( "UTF-8" ); //$NON-NLS-1$
-            response.getWriter().print( "{ \"status\": true }" ); //$NON-NLS-1$
+            Util.sendJsonOk(response);
             return;
         }
 
@@ -257,11 +272,11 @@ public class ConfigManager extends SimpleWebConsolePlugin 
implements OsgiManager
             // check both PID and PID filter
             if ( pid != null && !isAllowedPid(pid) )
             {
-                response.sendError(500);
+                response.sendError(400);
             }
             if ( pidFilter != null && !isAllowedPid(pidFilter) )
             {
-                response.sendError(500);
+                response.sendError(400);
             }
 
 
@@ -381,11 +396,11 @@ public class ConfigManager extends SimpleWebConsolePlugin 
implements OsgiManager
         // check both PID and PID filter
         if ( pid != null && !isAllowedPid(pid) )
         {
-            response.sendError(500);
+            response.sendError(400);
         }
         if ( pidFilter != null && !isAllowedPid(pidFilter) )
         {
-            response.sendError(500);
+            response.sendError(400);
         }
 
         final Locale loc = getLocale( request );
@@ -427,7 +442,7 @@ public class ConfigManager extends SimpleWebConsolePlugin 
implements OsgiManager
         // prepare variables
         final String referer = request.getParameter( REFERER );
         final boolean factoryCreate = "true".equals( 
request.getParameter(FACTORY_CREATE) ); //$NON-NLS-1$
-        DefaultVariableResolver vars = ( ( DefaultVariableResolver ) 
WebConsoleUtil.getVariableResolver( request ) );
+        final Map<String, Object> vars = ( ( Map<String, Object> ) 
WebConsoleUtil.getVariableResolver( request ) );
         vars.put( "__data__", json.toString() ); //$NON-NLS-1$
         vars.put( "selectedPid", pid != null ? pid : "" ); //$NON-NLS-1$ 
//$NON-NLS-2$
         vars.put( "configurationReferer", referer != null ? referer : "" ); 
//$NON-NLS-1$ //$NON-NLS-2$
@@ -451,7 +466,5 @@ public class ConfigManager extends SimpleWebConsolePlugin 
implements OsgiManager
         }
         return null;
     }
-
-
 }
 
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/MetatypePropertyDescriptor.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/MetatypePropertyDescriptor.java
index 96adeb0..64c53fa 100644
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/MetatypePropertyDescriptor.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/MetatypePropertyDescriptor.java
@@ -86,4 +86,10 @@ public class MetatypePropertyDescriptor extends 
PropertyDescriptor
     {
         return optional;
     }
+
+    
+    public boolean hasMetatype()
+    {
+        return true;
+    }
 }
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/PropertyDescriptor.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/PropertyDescriptor.java
index 9ea82e4..e06f385 100644
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/PropertyDescriptor.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/PropertyDescriptor.java
@@ -100,4 +100,8 @@ class PropertyDescriptor
         return false;
     }
     
+    public boolean hasMetatype()
+    {
+        return false;
+    }
 }
\ No newline at end of file

Reply via email to