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 b298317  FELIX-6367 : Provide SPI for configuration management (WiP)
b298317 is described below

commit b298317cae647210b87c1a629501100e71607bdf
Author: Carsten Ziegeler <[email protected]>
AuthorDate: Sun Aug 29 11:35:29 2021 +0200

    FELIX-6367 : Provide SPI for configuration management (WiP)
---
 .../org/apache/felix/webconsole/internal/Util.java |  11 ++
 .../internal/configuration/ConfigAdminSupport.java |  24 ++---
 .../internal/configuration/ConfigJsonSupport.java  |   4 +-
 .../internal/configuration/ConfigManager.java      | 115 ++++++++++-----------
 .../internal/configuration/ConfigurationUtil.java  |  13 ++-
 .../felix/webconsole/spi/ConfigurationHandler.java |  20 ++--
 .../felix/webconsole/spi/ValidationException.java  |   8 +-
 7 files changed, 109 insertions(+), 86 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 398f0aa..2b2818c 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
@@ -24,6 +24,7 @@ import java.util.Comparator;
 import java.util.Enumeration;
 import java.util.Locale;
 
+import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.osgi.framework.Bundle;
@@ -252,4 +253,14 @@ public class Util
         response.setCharacterEncoding( "UTF-8" ); //$NON-NLS-1$
         response.getWriter().print( "{ \"status\": true }" ); //$NON-NLS-1$
     }
+
+    public static final Locale getLocale( final HttpServletRequest request ) {
+        try {
+            return request.getLocale();
+        } catch ( Throwable t ) {
+            // expected in standard OSGi Servlet 2.1 environments
+            // fallback to using the default locale
+            return Locale.getDefault();
+        }
+    }
 }
\ 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 33dab7a..c0f769d 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
@@ -37,6 +37,7 @@ import javax.servlet.http.HttpServletRequest;
 
 import org.apache.felix.webconsole.internal.misc.ServletSupport;
 import org.apache.felix.webconsole.spi.ConfigurationHandler;
+import org.apache.felix.webconsole.spi.ValidationException;
 import org.osgi.framework.Constants;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.service.cm.Configuration;
@@ -133,8 +134,8 @@ class ConfigAdminSupport {
      * @param isUpdate {@code true} if this is a rest call, false if it is 
done via the webconsole UI
      * @throws IOException
      */
-    void applyConfiguration( final HttpServletRequest request, final String 
pid, final String propertyList, final boolean isUpdate )
-            throws IOException
+    void applyConfiguration( final HttpServletRequest request, final String 
pid, final String[] propertyList, final boolean isUpdate )
+            throws ValidationException, IOException
     {
         final String factoryPid = request.getParameter( 
ConfigManager.FACTORY_PID );
         final Configuration config = 
ConfigurationUtil.getOrCreateConfiguration( this.service, 
this.configurationHandlers, pid, factoryPid );
@@ -147,7 +148,7 @@ class ConfigAdminSupport {
         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(","))
+        for(final String propName : propertyList)
         {
             final String paramName = "action".equals(propName) //$NON-NLS-1$
                     || ConfigManager.ACTION_DELETE.equals(propName)
@@ -342,21 +343,16 @@ class ConfigAdminSupport {
         config.update( props );
     }
 
-    public void deleteConfiguration(final String pid) throws IOException {
+    public void deleteConfiguration(final String pid) throws 
ValidationException, IOException {
         // only delete if the PID is not our place holder
         if ( !ConfigurationUtil.getPlaceholderPid().equals( pid ) ) {
             final Configuration config = 
ConfigurationUtil.findConfiguration(this.service, pid);
-            this.deleteConfiguration(config);
-        }            
-    }
-
-    void deleteConfiguration(final Configuration config) throws IOException {
-        // only delete if the PID is not our place holder
-        if ( config != null && !ConfigurationUtil.getPlaceholderPid().equals( 
config.getPid() ) ) {
-            for(final ConfigurationHandler h : this.configurationHandlers) {
-                h.deleteConfiguration(config.getFactoryPid(), config.getPid());
+            if ( config != null ) {
+                for(final ConfigurationHandler h : this.configurationHandlers) 
{
+                    h.deleteConfiguration(config.getFactoryPid(), 
config.getPid());
+                }
+                config.delete();
             }
-            config.delete();
         }            
     }
 
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigJsonSupport.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigJsonSupport.java
index a3a50d5..dfb7faa 100644
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigJsonSupport.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigJsonSupport.java
@@ -241,7 +241,7 @@ class ConfigJsonSupport {
                 // ignore configuration object if an entry already exists in 
the map
                 // or if it is invalid
                 final String pid = cfgs[i].getPid();
-                if (optionsPlain.containsKey(pid) || 
!ConfigManager.isAllowedPid(pid) )
+                if (optionsPlain.containsKey(pid) || 
!ConfigurationUtil.isAllowedPid(pid) )
                 {
                     continue;
                 }
@@ -445,7 +445,7 @@ class ConfigJsonSupport {
         for ( int i = 0; refs != null && i < refs.length; i++ ) {
             Object pidObject = refs[i].getProperty( Constants.SERVICE_PID );
             // only include valid PIDs
-            if ( pidObject instanceof String && 
ConfigManager.isAllowedPid((String)pidObject) ) {
+            if ( pidObject instanceof String && 
ConfigurationUtil.isAllowedPid((String)pidObject) ) {
                 String pid = ( String ) pidObject;
                 String name = pid;
                 boolean haveOcd = !ocdRequired;
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 2e5baf0..221871f 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
@@ -19,7 +19,8 @@ package org.apache.felix.webconsole.internal.configuration;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
-import java.util.Collections;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
@@ -34,10 +35,13 @@ import org.apache.felix.webconsole.WebConsoleUtil;
 import org.apache.felix.webconsole.internal.OsgiManagerPlugin;
 import org.apache.felix.webconsole.internal.Util;
 import org.apache.felix.webconsole.internal.misc.ServletSupport;
+import org.apache.felix.webconsole.spi.ConfigurationHandler;
+import org.apache.felix.webconsole.spi.ValidationException;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.service.cm.Configuration;
+import org.osgi.util.tracker.ServiceTracker;
 
 
 /**
@@ -75,86 +79,73 @@ public class ConfigManager extends SimpleWebConsolePlugin 
implements OsgiManager
     // templates
     private final String TEMPLATE;
 
+    // service tracker for SPI
+    private ServiceTracker<ConfigurationHandler, ConfigurationHandler> 
spiTracker;
+
     /** Default constructor */
-    public ConfigManager()
-    {
+    public ConfigManager() {
         super(LABEL, TITLE, CATEGORY_OSGI, CSS);
 
         // load templates
         TEMPLATE = readTemplateFile( "/templates/config.html" ); //$NON-NLS-1$
     }
 
-    static final boolean isAllowedPid(final String pid)
-    {
-        for(int i = 0; i < pid.length(); i++)
-        {
-            final char c = pid.charAt(i);
-            if ( c == '&' || c == '<' || c == '>' || c == '"' || c == '\'' )
-            {
-                return false;
-            }
-        }
-        return true;
+    @Override
+    public void activate(final BundleContext bundleContext) {
+        super.activate(bundleContext);
+        this.spiTracker = new ServiceTracker<>(bundleContext, 
ConfigurationHandler.class.getName(), null);
+        this.spiTracker.open();
     }
 
 
-    private static final Locale getLocale( HttpServletRequest request )
-    {
-        try
-        {
-            return request.getLocale();
-        }
-        catch ( Throwable t )
-        {
-            // expected in standard OSGi Servlet 2.1 environments
-            // fallback to using the default locale
-            return Locale.getDefault();
+    @Override
+    public void deactivate() {
+        if ( this.spiTracker != null ) {
+            this.spiTracker.close();
+            this.spiTracker = null;
         }
+        super.deactivate();
     }
 
-
     /**
      * @see 
javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, 
javax.servlet.http.HttpServletResponse)
      */
     @Override
-    protected void doPost( HttpServletRequest request, HttpServletResponse 
response ) throws IOException
-    {
+    protected void doPost( HttpServletRequest request, HttpServletResponse 
response ) throws IOException {
         // service unavailable if config admin is not available
         final ConfigAdminSupport cas = getConfigurationAdminSupport();
-        if ( cas == null )
-        {
+        if ( cas == null ) {
             response.sendError(503);
             return;
         }
 
         // needed multiple times below
         String pid = request.getParameter( ConfigManager.PID );
-        if ( pid == null )
-        {
+        if ( pid == null ) {
             String info = request.getPathInfo();
             pid = WebConsoleUtil.urlDecode( info.substring( info.lastIndexOf( 
'/' ) + 1 ) );
         }
         // ignore this request if the PID is invalid / not provided
-        if ( pid == null || pid.length() == 0 || !isAllowedPid(pid))
-        {
+        if ( pid == null || pid.length() == 0 || 
!ConfigurationUtil.isAllowedPid(pid)) {
             response.sendError(400);
             return;
         }
 
         // the filter to select part of the configurations
         final String pidFilter = request.getParameter( PID_FILTER );
-        if ( pidFilter != null && ! isAllowedPid(pidFilter) )
-        {
+        if ( pidFilter != null && !ConfigurationUtil.isAllowedPid(pidFilter) ) 
{
             response.sendError(400);
             return;
         }
 
-        if ( request.getParameter( ACTION_APPLY ) != null )
-        {
-            if ( request.getParameter( ConfigManager.ACTION_DELETE ) != null ) 
//$NON-NLS-1$
-            {
-                cas.deleteConfiguration( pid );
-                Util.sendJsonOk(response);
+        if ( request.getParameter( ACTION_APPLY ) != null ) {
+            if ( request.getParameter( ConfigManager.ACTION_DELETE ) != null ) 
{ //$NON-NLS-1$
+                try {
+                    cas.deleteConfiguration( pid );
+                    Util.sendJsonOk(response);
+                } catch ( final ValidationException ve) {
+                    response.sendError(400, ve.getMessage());
+                }
             } 
             else 
             {
@@ -164,13 +155,17 @@ public class ConfigManager extends SimpleWebConsolePlugin 
implements OsgiManager
                     return;        
                 }
 
-                cas.applyConfiguration( request, pid, propertyList, 
ACTION_UPDATE.equals(request.getParameter(ACTION_APPLY)));
-                String redirect = pid;
-                if (pidFilter != null) {
-                    redirect = 
redirect.concat("?").concat(PID_FILTER).concat("=").concat(pidFilter);
+                try {
+                    cas.applyConfiguration( request, pid, 
propertyList.split(","), 
ACTION_UPDATE.equals(request.getParameter(ACTION_APPLY)));
+                    String redirect = pid;
+                    if (pidFilter != null) {
+                        redirect = 
redirect.concat("?").concat(PID_FILTER).concat("=").concat(pidFilter);
+                    }
+        
+                    WebConsoleUtil.sendRedirect(request, response, redirect);  
  
+                } catch ( final ValidationException ve) {
+                    response.sendError(400, ve.getMessage());
                 }
-    
-                WebConsoleUtil.sendRedirect(request, response, redirect);    
             }
             
             return;
@@ -201,7 +196,7 @@ public class ConfigManager extends SimpleWebConsolePlugin 
implements OsgiManager
         // send the result
         response.setContentType( "application/json" ); //$NON-NLS-1$
         response.setCharacterEncoding( "UTF-8" ); //$NON-NLS-1$
-        final Locale loc = getLocale( request );
+        final Locale loc = Util.getLocale( request );
         final String locale = ( loc != null ) ? loc.toString() : null;
         cas.getJsonSupport().printConfigurationJson( response.getWriter(), 
pid, config, pidFilter, locale );
     }
@@ -212,8 +207,7 @@ public class ConfigManager extends SimpleWebConsolePlugin 
implements OsgiManager
      */
     @Override
     protected void doGet( HttpServletRequest request, HttpServletResponse 
response )
-            throws ServletException, IOException
-    {
+            throws ServletException, IOException {
         // check for "post" requests from previous versions
         if ( "true".equals(request.getParameter("post")) ) { //$NON-NLS-1$ 
//$NON-NLS-2$
             this.doPost(request, response);
@@ -254,17 +248,17 @@ public class ConfigManager extends SimpleWebConsolePlugin 
implements OsgiManager
             }
 
             // check both PID and PID filter
-            if ( pid != null && !isAllowedPid(pid) )
+            if ( pid != null && !ConfigurationUtil.isAllowedPid(pid) )
             {
                 response.sendError(400);
             }
-            if ( pidFilter != null && !isAllowedPid(pidFilter) )
+            if ( pidFilter != null && 
!ConfigurationUtil.isAllowedPid(pidFilter) )
             {
                 response.sendError(400);
             }
 
 
-            final Locale loc = getLocale( request );
+            final Locale loc = Util.getLocale( request );
             final String locale = ( loc != null ) ? loc.toString() : null;
 
             final PrintWriter pw = response.getWriter();
@@ -378,16 +372,16 @@ public class ConfigManager extends SimpleWebConsolePlugin 
implements OsgiManager
         }
 
         // check both PID and PID filter
-        if ( pid != null && !isAllowedPid(pid) )
+        if ( pid != null && !ConfigurationUtil.isAllowedPid(pid) )
         {
             response.sendError(400);
         }
-        if ( pidFilter != null && !isAllowedPid(pidFilter) )
+        if ( pidFilter != null && !ConfigurationUtil.isAllowedPid(pidFilter) )
         {
             response.sendError(400);
         }
 
-        final Locale loc = getLocale( request );
+        final Locale loc = Util.getLocale( request );
         final String locale = ( loc != null ) ? loc.toString() : null;
 
 
@@ -443,11 +437,16 @@ public class ConfigManager extends SimpleWebConsolePlugin 
implements OsgiManager
     private ConfigAdminSupport getConfigurationAdminSupport() {
         Object configurationAdmin = getService( CONFIGURATION_ADMIN_NAME );
         if ( configurationAdmin != null ) {
-            return new ConfigAdminSupport( this, configurationAdmin, 
Collections.emptyList() );
+            final List<ConfigurationHandler> handlers = new ArrayList<>();
+            for(final Object o : this.spiTracker.getServices()) {
+                handlers.add((ConfigurationHandler)o);
+            }
+            return new ConfigAdminSupport( this, configurationAdmin, handlers 
);
         }
         return null;
     }
 
+    @Override
     public BundleContext getBundleContext() {
         return super.getBundleContext();
     }
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigurationUtil.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigurationUtil.java
index 5ebff02..71ae435 100644
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigurationUtil.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/internal/configuration/ConfigurationUtil.java
@@ -24,6 +24,7 @@ import java.util.Dictionary;
 import java.util.List;
 
 import org.apache.felix.webconsole.spi.ConfigurationHandler;
+import org.apache.felix.webconsole.spi.ValidationException;
 import org.osgi.framework.Constants;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.service.cm.Configuration;
@@ -58,7 +59,7 @@ public class ConfigurationUtil {
     public static Configuration getOrCreateConfiguration( final 
ConfigurationAdmin service, 
             final List<ConfigurationHandler> handlers,
             final String pid, 
-            final String factoryPid ) throws IOException {
+            final String factoryPid ) throws ValidationException, IOException {
         Configuration cfg = null;
         if ( !PLACEHOLDER_PID.equals(pid) ) {
             cfg = findConfiguration(service, pid);
@@ -79,7 +80,17 @@ public class ConfigurationUtil {
         return cfg;
     }
 
+    public static final boolean isAllowedPid(final String pid) {
+        for(int i = 0; i < pid.length(); i++) {
+            final char c = pid.charAt(i);
+            if ( c == '&' || c == '<' || c == '>' || c == '"' || c == '\'' ) {
+                return false;
+            }
+        }
+        return true;
+    }
 
+ 
     public static Configuration getPlaceholderConfiguration( final String 
factoryPid ) {
         return new PlaceholderConfiguration( factoryPid );
     }
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/spi/ConfigurationHandler.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/spi/ConfigurationHandler.java
index f35f5e6..da92ad8 100644
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/spi/ConfigurationHandler.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/spi/ConfigurationHandler.java
@@ -33,32 +33,36 @@ public interface ConfigurationHandler {
     /**
      * A new configuration with that pid should be created
      * @param pid The pid
-     * @throws IOException For an error or {@link ValidationException} if 
creation is not allowed
+     * @throws IOException For an error
+     * @throws ValidationException if creation is not allowed
      */
-    void createConfiguration(String pid) throws IOException;
+    void createConfiguration(String pid) throws ValidationException, 
IOException;
 
     /**
      * A new factory configuration with that pid should be created
      * @param factoryPid The factory pid
      * @param name Optional name, might be {@code null} if unknown
-     * @throws IOException For an error or {@link ValidationException} if 
creation is not allowed
+     * @throws IOException For an error
+     * @throws ValidationException if creation is not allowed
      */
-    void createFactoryConfiguration(String factoryPid, String name) throws 
IOException;
+    void createFactoryConfiguration(String factoryPid, String name) throws 
ValidationException, IOException;
 
     /**
      * A configuration should be deleted
      * @param factoryPid Optional factory pid
      * @param pid The pid
-     * @throws IOException For an error or {@link ValidationException} if 
deletion is not allowed
+     * @throws IOException For an error
+     * @throws ValidationException if deletion is not allowed
      */
-    void deleteConfiguration(String factoryPid, String pid) throws IOException;
+    void deleteConfiguration(String factoryPid, String pid) throws 
ValidationException, IOException;
 
     /**
      * A configuration should be updated
      * @param factoryPid Optional factory pid
      * @param pid The pid
      * @param props Mutable dictionary
-     * @throws IOException For an error or {@link ValidationException} if 
deletion is not allowed
+     * @throws IOException For an error
+     * @throws ValidationException if updating is not allowed
      */
-    void updateConfiguration(String factoryPid, String pid, Dictionary<String, 
Object> props) throws IOException;
+    void updateConfiguration(String factoryPid, String pid, Dictionary<String, 
Object> props) throws ValidationException, IOException;
 }
\ No newline at end of file
diff --git 
a/webconsole/src/main/java/org/apache/felix/webconsole/spi/ValidationException.java
 
b/webconsole/src/main/java/org/apache/felix/webconsole/spi/ValidationException.java
index a661b82..47cf2fe 100644
--- 
a/webconsole/src/main/java/org/apache/felix/webconsole/spi/ValidationException.java
+++ 
b/webconsole/src/main/java/org/apache/felix/webconsole/spi/ValidationException.java
@@ -18,9 +18,11 @@
  */
 package org.apache.felix.webconsole.spi;
 
-import java.io.IOException;
-
-public class ValidationException extends IOException {
+/**
+ * A {@link ConfigurationHandler} should throws a validation exception
+ * if an operation is not allowed or not valid.
+ */
+public class ValidationException extends Exception {
 
     /**
      * Create validation exception

Reply via email to