You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by dk...@apache.org on 2019/06/05 17:18:59 UTC

[cxf] branch master updated: Squashed commit of the following: Closes #561

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

dkulp pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cxf.git


The following commit(s) were added to refs/heads/master by this push:
     new 93a6a50  Squashed commit of the following: Closes #561
93a6a50 is described below

commit 93a6a50f59f7d2e858174efd26fa5b5ac1334cc5
Author: Romain Manni-Bucau <rm...@apache.org>
AuthorDate: Wed Jun 5 12:10:12 2019 -0400

    Squashed commit of the following:
    Closes #561
    
    commit 4594115ac69ce8ed7b80da04a5e3ff8a7b2e6176
    Author: Romain Manni-Bucau <rm...@gmail.com>
    Date:   Wed Jun 5 13:11:37 2019 +0200
    
        checkstyle
    
    commit 5a797df973104973cdd0c2269436125dcbe17afb
    Author: Romain Manni-Bucau <rm...@gmail.com>
    Date:   Wed Jun 5 12:05:25 2019 +0200
    
        moving most of duplicated delegation code of portablefeature -> feature in a shared delegating class
    
    commit 98d025d7c02648590da2d7b2da70cd2efe4595ca
    Author: Romain Manni-Bucau <rm...@gmail.com>
    Date:   Sun Jun 2 12:40:14 2019 +0200
    
        Fix var name
    
    commit 23cbbb9db73a74913fd4294f805032f923ffcf09
    Author: Romain Manni-Bucau <rm...@apache.org>
    Date:   Sun Jun 2 09:03:41 2019 +0200
    
        ensure OpenTracingTracingTest is deterministic - async tests were not
    
    commit 2104b67718840183083e394e0fb92184f1ed4878
    Author: Romain Manni-Bucau <rm...@apache.org>
    Date:   Sat Jun 1 18:34:47 2019 +0200
    
        correct delegation for LBfeature
    
    commit 35f0dfdd97c80b8cd85b13142d1b0efb9a7a8f7a
    Author: Romain Manni-Bucau <rm...@apache.org>
    Date:   Sat Jun 1 18:32:39 2019 +0200
    
        correct delegation
    
    commit 189c89e666d4fb28b94b827d3f5735c4909cb02f
    Author: Romain Manni-Bucau <rm...@apache.org>
    Date:   Sat Jun 1 18:28:38 2019 +0200
    
        avoid stackoverflow
    
    commit fd86077d94e4cdf08a4cb16f699a66f7e84c4f82
    Author: Romain Manni-Bucau <rm...@apache.org>
    Date:   Sat Jun 1 17:11:03 2019 +0200
    
        javadoc change to trigger a CI build, jenkins had a local issue
    
    commit 8a148d0a8b62dbb06861336b646350ef53bfea8e
    Author: Romain Manni-Bucau <rm...@apache.org>
    Date:   Sat Jun 1 16:59:03 2019 +0200
    
        another missed style fix
    
    commit 49f34353a7f58676bc3ba9866144b082344414a0
    Author: Romain Manni-Bucau <rm...@apache.org>
    Date:   Sat Jun 1 16:24:17 2019 +0200
    
        keep AbstractFeature#getActive method available - backward compatibility
    
    commit ced11c7050d464ad49e37778b7fa112637f80c48
    Author: Romain Manni-Bucau <rm...@apache.org>
    Date:   Sat Jun 1 10:53:58 2019 +0200
    
        style
    
    commit 12683682fe1853fc86957f3ed1cf4960ae53c00a
    Author: Romain Manni-Bucau <rm...@apache.org>
    Date:   Sat Jun 1 10:34:49 2019 +0200
    
        fixing AbstractPortableFeature/AbstractFeature delegation
    
    commit aa42e1fe999c2f429cd7f0c860dd4ccc2485bd68
    Author: Romain Manni-Bucau <rm...@apache.org>
    Date:   Sat Jun 1 10:22:04 2019 +0200
    
        some alignment on the delegate naming + better delegation for features
    
    commit 793724a0cfe8a77e23d5ddeea5347c1496f23ee1
    Author: Romain Manni-Bucau <rm...@apache.org>
    Date:   Sat Jun 1 09:59:59 2019 +0200
    
        rebase on master
---
 .../databinding/stax/StaxDataBindingFeature.java   |  40 +-
 .../org/apache/cxf/feature/AbstractFeature.java    |  46 +-
 ...ctFeature.java => AbstractPortableFeature.java} |  38 +-
 .../org/apache/cxf/feature/DelegatingFeature.java  |  70 ++
 .../org/apache/cxf/feature/FastInfosetFeature.java | 110 +--
 .../org/apache/cxf/feature/LoggingFeature.java     | 190 ++--
 .../apache/cxf/feature/StaxTransformFeature.java   | 106 ++-
 .../apache/cxf/feature/transform/XSLTFeature.java  |  52 +-
 .../validation/SchemaValidationFeature.java        |  45 +-
 .../security/JAASAuthenticationFeature.java        |  52 +-
 .../cxf/transport/common/gzip/GZIPFeature.java     | 131 +--
 .../cxf/validation/BeanValidationFeature.java      |  44 +-
 .../validation/ClientBeanValidationFeature.java    |  47 +-
 .../cxf/tracing/brave/BraveClientFeature.java      |  48 +-
 .../org/apache/cxf/tracing/brave/BraveFeature.java |  77 +-
 .../opentracing/OpenTracingClientFeature.java      |  30 +-
 .../tracing/opentracing/OpenTracingFeature.java    |  42 +-
 .../cxf/binding/coloc/feature/ColocFeature.java    |  31 +-
 .../org/apache/cxf/clustering/FailoverFeature.java | 124 ++-
 .../cxf/clustering/LoadDistributorFeature.java     |  19 +-
 .../CircuitBreakerFailoverFeature.java             |  91 +-
 .../org/apache/cxf/ext/logging/LoggingFeature.java | 220 +++--
 .../org/apache/cxf/metrics/MetricsFeature.java     | 163 ++--
 .../apache/cxf/throttling/ThrottlingFeature.java   |  45 +-
 .../validation/JAXRSBeanValidationFeature.java     |  53 +-
 .../cxf/javascript/JavascriptOptionsFeature.java   |  48 +-
 .../interceptor/ResponseTimeFeature.java           |  35 +-
 .../JAXRSClientBeanValidationFeature.java          |  24 +-
 .../apache/cxf/jaxrs/openapi/OpenApiFeature.java   | 994 +++++++++++++--------
 .../cxf/jaxrs/swagger/AbstractSwaggerFeature.java  | 305 +++++--
 .../apache/cxf/jaxrs/swagger/Swagger2Feature.java  | 779 +++++++++-------
 .../java/org/apache/cxf/jaxrs/sse/SseFeature.java  |  28 +-
 .../cxf/transport/http/HttpConduitFeature.java     |  32 +-
 .../cxf/transport/http/HttpDestinationFeature.java |  33 +-
 .../transport/https/CertConstraintsFeature.java    |  96 +-
 .../transport/jms/ConnectionFactoryFeature.java    |  66 +-
 .../apache/cxf/transport/jms/JMSConfigFeature.java |  79 +-
 .../org/apache/cxf/systest/jaeger/TestSender.java  |  10 +
 .../opentracing/OpenTracingTracingTest.java        |  27 +-
 .../ValidationClientServerTest.java                |  12 +-
 40 files changed, 2851 insertions(+), 1631 deletions(-)

diff --git a/core/src/main/java/org/apache/cxf/databinding/stax/StaxDataBindingFeature.java b/core/src/main/java/org/apache/cxf/databinding/stax/StaxDataBindingFeature.java
index ed4ed86..2eff9f1 100644
--- a/core/src/main/java/org/apache/cxf/databinding/stax/StaxDataBindingFeature.java
+++ b/core/src/main/java/org/apache/cxf/databinding/stax/StaxDataBindingFeature.java
@@ -24,32 +24,38 @@ import java.util.List;
 import org.apache.cxf.Bus;
 import org.apache.cxf.endpoint.Client;
 import org.apache.cxf.endpoint.Server;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.interceptor.AbstractInDatabindingInterceptor;
 import org.apache.cxf.interceptor.Interceptor;
 import org.apache.cxf.message.Message;
 
-public class StaxDataBindingFeature extends AbstractFeature {
+public class StaxDataBindingFeature extends DelegatingFeature<StaxDataBindingFeature.Portable> {
 
-
-    @Override
-    public void initialize(Client client, Bus bus) {
-        removeDatabindingInterceptor(client.getEndpoint().getBinding().getInInterceptors());
+    public StaxDataBindingFeature() {
+        super(new Portable());
     }
 
-    @Override
-    public void initialize(Server server, Bus bus) {
-        removeDatabindingInterceptor(server.getEndpoint().getBinding().getInInterceptors());
-    }
+    public static class Portable implements AbstractPortableFeature {
+        @Override
+        public void initialize(Client client, Bus bus) {
+            removeDatabindingInterceptor(client.getEndpoint().getBinding().getInInterceptors());
+        }
+
+        @Override
+        public void initialize(Server server, Bus bus) {
+            removeDatabindingInterceptor(server.getEndpoint().getBinding().getInInterceptors());
+        }
 
-    private void removeDatabindingInterceptor(List<Interceptor<? extends Message>> inInterceptors) {
-        List<Interceptor<? extends Message>> remove = new LinkedList<>();
-        for (Interceptor<? extends Message> i : inInterceptors) {
-            if (i instanceof AbstractInDatabindingInterceptor) {
-                remove.add(i);
+        private void removeDatabindingInterceptor(List<Interceptor<? extends Message>> inInterceptors) {
+            List<Interceptor<? extends Message>> remove = new LinkedList<>();
+            for (Interceptor<? extends Message> i : inInterceptors) {
+                if (i instanceof AbstractInDatabindingInterceptor) {
+                    remove.add(i);
+                }
             }
+            inInterceptors.removeAll(remove);
+            inInterceptors.add(new StaxDataBindingInterceptor());
         }
-        inInterceptors.removeAll(remove);
-        inInterceptors.add(new StaxDataBindingInterceptor());
     }
 }
diff --git a/core/src/main/java/org/apache/cxf/feature/AbstractFeature.java b/core/src/main/java/org/apache/cxf/feature/AbstractFeature.java
index c6ff44d..39ac97d 100644
--- a/core/src/main/java/org/apache/cxf/feature/AbstractFeature.java
+++ b/core/src/main/java/org/apache/cxf/feature/AbstractFeature.java
@@ -23,8 +23,6 @@ import java.util.List;
 import javax.xml.ws.WebServiceFeature;
 
 import org.apache.cxf.Bus;
-import org.apache.cxf.endpoint.Client;
-import org.apache.cxf.endpoint.Server;
 import org.apache.cxf.interceptor.InterceptorProvider;
 
 /**
@@ -36,49 +34,27 @@ import org.apache.cxf.interceptor.InterceptorProvider;
  * If you're simply adding interceptors to a Server, Client, or Bus, this allows you to add
  * them easily.
  */
-public abstract class AbstractFeature extends WebServiceFeature implements Feature {
+public abstract class AbstractFeature extends WebServiceFeature implements AbstractPortableFeature {
+    @Override
     public String getID() {
         return getClass().getName();
     }
 
-    public void initialize(Server server, Bus bus) {
-        initializeProvider(server.getEndpoint(), bus);
+    @Override
+    public boolean isEnabled() {
+        return enabled;
     }
 
-    public void initialize(Client client, Bus bus) {
-        initializeProvider(client, bus);
-    }
-
-    public void initialize(InterceptorProvider interceptorProvider, Bus bus) {
-        initializeProvider(interceptorProvider, bus);
-    }
-
-    public void initialize(Bus bus) {
-        initializeProvider(bus, bus);
+    @Override
+    public void doInitializeProvider(InterceptorProvider provider, Bus bus) {
+        initializeProvider(provider, bus);
     }
 
     protected void initializeProvider(InterceptorProvider provider, Bus bus) {
-
+        // no-op
     }
 
-    /**
-     * Convenience method to extract a feature by type from an active list.
-     *
-     * @param features the given feature list
-     * @param type the feature type required
-     * @return the feature of the specified type if active
-     */
-    public static <T> T getActive(List<? extends Feature> features,
-                                  Class<T> type) {
-        T active = null;
-        if (features != null) {
-            for (Feature feature : features) {
-                if (type.isInstance(feature)) {
-                    active = type.cast(feature);
-                    break;
-                }
-            }
-        }
-        return active;
+    public static <T> T getActive(List<? extends Feature> features, Class<T> type) {
+        return AbstractPortableFeature.getActive(features, type);
     }
 }
diff --git a/core/src/main/java/org/apache/cxf/feature/AbstractFeature.java b/core/src/main/java/org/apache/cxf/feature/AbstractPortableFeature.java
similarity index 62%
copy from core/src/main/java/org/apache/cxf/feature/AbstractFeature.java
copy to core/src/main/java/org/apache/cxf/feature/AbstractPortableFeature.java
index c6ff44d..2238cc2 100644
--- a/core/src/main/java/org/apache/cxf/feature/AbstractFeature.java
+++ b/core/src/main/java/org/apache/cxf/feature/AbstractPortableFeature.java
@@ -20,45 +20,40 @@ package org.apache.cxf.feature;
 
 import java.util.List;
 
-import javax.xml.ws.WebServiceFeature;
-
 import org.apache.cxf.Bus;
 import org.apache.cxf.endpoint.Client;
 import org.apache.cxf.endpoint.Server;
 import org.apache.cxf.interceptor.InterceptorProvider;
 
 /**
- * A Feature is something that is able to customize a Server, Client, or Bus, typically
- * adding capabilities. For instance, there may be a LoggingFeature which configures
+ * A portable - i.e. for jaxws and jaxrs - Feature is something that is able to customize
+ * a Server, Client, or Bus, typically adding capabilities.
+ * For instance, there may be a LoggingFeature which configures
  * one of the above to log each of their messages.
  * <p>
- * By default the initialize methods all delegate to initializeProvider(InterceptorProvider).
+ * By default the initialize methods all delegate to doInitializeProvider(InterceptorProvider).
  * If you're simply adding interceptors to a Server, Client, or Bus, this allows you to add
  * them easily.
  */
-public abstract class AbstractFeature extends WebServiceFeature implements Feature {
-    public String getID() {
-        return getClass().getName();
-    }
-
-    public void initialize(Server server, Bus bus) {
-        initializeProvider(server.getEndpoint(), bus);
+public interface AbstractPortableFeature extends Feature {
+    default void initialize(Server server, Bus bus) {
+        doInitializeProvider(server.getEndpoint(), bus);
     }
 
-    public void initialize(Client client, Bus bus) {
-        initializeProvider(client, bus);
+    default void initialize(Client client, Bus bus) {
+        doInitializeProvider(client, bus);
     }
 
-    public void initialize(InterceptorProvider interceptorProvider, Bus bus) {
-        initializeProvider(interceptorProvider, bus);
+    default void initialize(InterceptorProvider interceptorProvider, Bus bus) {
+        doInitializeProvider(interceptorProvider, bus);
     }
 
-    public void initialize(Bus bus) {
-        initializeProvider(bus, bus);
+    default void initialize(Bus bus) {
+        doInitializeProvider(bus, bus);
     }
 
-    protected void initializeProvider(InterceptorProvider provider, Bus bus) {
-
+    default void doInitializeProvider(InterceptorProvider provider, Bus bus) {
+        // no-op
     }
 
     /**
@@ -68,8 +63,7 @@ public abstract class AbstractFeature extends WebServiceFeature implements Featu
      * @param type the feature type required
      * @return the feature of the specified type if active
      */
-    public static <T> T getActive(List<? extends Feature> features,
-                                  Class<T> type) {
+    static <T> T getActive(List<? extends Feature> features, Class<T> type) {
         T active = null;
         if (features != null) {
             for (Feature feature : features) {
diff --git a/core/src/main/java/org/apache/cxf/feature/DelegatingFeature.java b/core/src/main/java/org/apache/cxf/feature/DelegatingFeature.java
new file mode 100644
index 0000000..be72118
--- /dev/null
+++ b/core/src/main/java/org/apache/cxf/feature/DelegatingFeature.java
@@ -0,0 +1,70 @@
+/**
+ * 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.cxf.feature;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.endpoint.Client;
+import org.apache.cxf.endpoint.Server;
+import org.apache.cxf.interceptor.InterceptorProvider;
+
+/**
+ * Enable to convert a {@link AbstractPortableFeature} to a {@link AbstractFeature}.
+ *
+ * @param <T> the "portable" feature.
+ */
+public class DelegatingFeature<T extends AbstractPortableFeature> extends AbstractFeature {
+    protected T delegate;
+
+    protected DelegatingFeature(final T d) {
+        delegate = d == null ? getDelegate() : d;
+    }
+
+    protected T getDelegate() { // useful for inheritance
+        return delegate;
+    }
+
+    public void setDelegate(T delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public void initialize(final Server server, final Bus bus) {
+        delegate.initialize(server, bus);
+    }
+
+    @Override
+    public void initialize(final Client client, final Bus bus) {
+        delegate.initialize(client, bus);
+    }
+
+    @Override
+    public void initialize(final InterceptorProvider interceptorProvider, final Bus bus) {
+        delegate.initialize(interceptorProvider, bus);
+    }
+
+    @Override
+    public void initialize(final Bus bus) {
+        delegate.initialize(bus);
+    }
+
+    @Override
+    protected void initializeProvider(final InterceptorProvider interceptorProvider, final Bus bus) {
+        delegate.doInitializeProvider(interceptorProvider, bus);
+    }
+}
diff --git a/core/src/main/java/org/apache/cxf/feature/FastInfosetFeature.java b/core/src/main/java/org/apache/cxf/feature/FastInfosetFeature.java
index a18841d..d6af015 100644
--- a/core/src/main/java/org/apache/cxf/feature/FastInfosetFeature.java
+++ b/core/src/main/java/org/apache/cxf/feature/FastInfosetFeature.java
@@ -37,66 +37,74 @@ import org.apache.cxf.interceptor.InterceptorProvider;
   </pre>
  */
 @NoJSR250Annotations
-public class FastInfosetFeature extends AbstractFeature {
+public class FastInfosetFeature extends DelegatingFeature<FastInfosetFeature.Portable> {
+    public FastInfosetFeature() {
+        super(new Portable());
+    }
 
-    boolean force;
-    private Integer serializerAttributeValueMapMemoryLimit;
-    private Integer serializerMinAttributeValueSize;
-    private Integer serializerMaxAttributeValueSize;
-    private Integer serializerCharacterContentChunkMapMemoryLimit;
-    private Integer serializerMinCharacterContentChunkSize;
-    private Integer serializerMaxCharacterContentChunkSize;
+    public void setForce(boolean b) {
+        delegate.setForce(b);
+    }
 
-    public FastInfosetFeature() {
-        //
+    public boolean getForce() {
+        return delegate.getForce();
     }
 
+    public static class Portable implements AbstractPortableFeature {
+        boolean force;
+        private Integer serializerAttributeValueMapMemoryLimit;
+        private Integer serializerMinAttributeValueSize;
+        private Integer serializerMaxAttributeValueSize;
+        private Integer serializerCharacterContentChunkMapMemoryLimit;
+        private Integer serializerMinCharacterContentChunkSize;
+        private Integer serializerMaxCharacterContentChunkSize;
 
-    @Override
-    protected void initializeProvider(InterceptorProvider provider, Bus bus) {
+        @Override
+        public void doInitializeProvider(InterceptorProvider provider, Bus bus) {
 
-        FIStaxInInterceptor in = new FIStaxInInterceptor();
+            FIStaxInInterceptor in = new FIStaxInInterceptor();
 
-        FIStaxOutInterceptor out = new FIStaxOutInterceptor(force);
-        if (serializerAttributeValueMapMemoryLimit != null && serializerAttributeValueMapMemoryLimit.intValue() > 0) {
-            out.setSerializerAttributeValueMapMemoryLimit(serializerAttributeValueMapMemoryLimit);
-        }
-        if (serializerMinAttributeValueSize != null && serializerMinAttributeValueSize.intValue() > 0) {
-            out.setSerializerMinAttributeValueSize(serializerMinAttributeValueSize);
-        }
-        if (serializerMaxAttributeValueSize != null && serializerMaxAttributeValueSize.intValue() > 0) {
-            out.setSerializerMaxAttributeValueSize(serializerMaxAttributeValueSize);
-        }
-        if (serializerCharacterContentChunkMapMemoryLimit != null
-                && serializerCharacterContentChunkMapMemoryLimit.intValue() > 0) {
-            out.setSerializerCharacterContentChunkMapMemoryLimit(
-                    serializerCharacterContentChunkMapMemoryLimit);
-        }
-        if (serializerMinCharacterContentChunkSize != null && serializerMinCharacterContentChunkSize.intValue() > 0) {
-            out.setSerializerMinCharacterContentChunkSize(serializerMinCharacterContentChunkSize);
-        }
-        if (serializerMaxCharacterContentChunkSize != null && serializerMaxCharacterContentChunkSize.intValue() > 0) {
-            out.setSerializerMaxCharacterContentChunkSize(serializerMaxCharacterContentChunkSize);
-        }
+            FIStaxOutInterceptor out = new FIStaxOutInterceptor(force);
+            if (serializerAttributeValueMapMemoryLimit != null && serializerAttributeValueMapMemoryLimit > 0) {
+                out.setSerializerAttributeValueMapMemoryLimit(serializerAttributeValueMapMemoryLimit);
+            }
+            if (serializerMinAttributeValueSize != null && serializerMinAttributeValueSize > 0) {
+                out.setSerializerMinAttributeValueSize(serializerMinAttributeValueSize);
+            }
+            if (serializerMaxAttributeValueSize != null && serializerMaxAttributeValueSize > 0) {
+                out.setSerializerMaxAttributeValueSize(serializerMaxAttributeValueSize);
+            }
+            if (serializerCharacterContentChunkMapMemoryLimit != null
+                    && serializerCharacterContentChunkMapMemoryLimit > 0) {
+                out.setSerializerCharacterContentChunkMapMemoryLimit(
+                        serializerCharacterContentChunkMapMemoryLimit);
+            }
+            if (serializerMinCharacterContentChunkSize != null && serializerMinCharacterContentChunkSize > 0) {
+                out.setSerializerMinCharacterContentChunkSize(serializerMinCharacterContentChunkSize);
+            }
+            if (serializerMaxCharacterContentChunkSize != null && serializerMaxCharacterContentChunkSize > 0) {
+                out.setSerializerMaxCharacterContentChunkSize(serializerMaxCharacterContentChunkSize);
+            }
 
-        provider.getInInterceptors().add(in);
-        provider.getInFaultInterceptors().add(in);
-        provider.getOutInterceptors().add(out);
-        provider.getOutFaultInterceptors().add(out);
-    }
+            provider.getInInterceptors().add(in);
+            provider.getInFaultInterceptors().add(in);
+            provider.getOutInterceptors().add(out);
+            provider.getOutFaultInterceptors().add(out);
+        }
 
-    /**
-     * Set if FastInfoset is always used without negotiation
-     * @param b
-     */
-    public void setForce(boolean b) {
-        force = b;
-    }
+        /**
+         * Set if FastInfoset is always used without negotiation
+         * @param b
+         */
+        public void setForce(boolean b) {
+            force = b;
+        }
 
-    /**
-     * Retrieve the value set with {@link #setForce(boolean)}.
-     */
-    public boolean getForce() {
-        return force;
+        /**
+         * Retrieve the value set with {@link #setForce(boolean)}.
+         */
+        public boolean getForce() {
+            return force;
+        }
     }
 }
diff --git a/core/src/main/java/org/apache/cxf/feature/LoggingFeature.java b/core/src/main/java/org/apache/cxf/feature/LoggingFeature.java
index f2ccc8c..738ab88 100644
--- a/core/src/main/java/org/apache/cxf/feature/LoggingFeature.java
+++ b/core/src/main/java/org/apache/cxf/feature/LoggingFeature.java
@@ -49,105 +49,149 @@ import org.apache.cxf.interceptor.LoggingOutInterceptor;
 @NoJSR250Annotations
 @Deprecated
 @Provider(value = Type.Feature)
-public class LoggingFeature extends AbstractFeature {
-    private static final int DEFAULT_LIMIT = AbstractLoggingInterceptor.DEFAULT_LIMIT;
-    private static final LoggingInInterceptor IN = new LoggingInInterceptor(DEFAULT_LIMIT);
-    private static final LoggingOutInterceptor OUT = new LoggingOutInterceptor(DEFAULT_LIMIT);
-
-
-    String inLocation;
-    String outLocation;
-    boolean prettyLogging;
-    boolean showBinary;
-
-    int limit = DEFAULT_LIMIT;
-
+public class LoggingFeature extends DelegatingFeature<LoggingFeature.Portable> {
     public LoggingFeature() {
-
+        super(new Portable());
     }
     public LoggingFeature(int lim) {
-        limit = lim;
+        super(new Portable(lim));
     }
     public LoggingFeature(String in, String out) {
-        inLocation = in;
-        outLocation = out;
+        super(new Portable(in, out));
     }
     public LoggingFeature(String in, String out, int lim) {
-        inLocation = in;
-        outLocation = out;
-        limit = lim;
+        super(new Portable(in, out, lim));
     }
 
     public LoggingFeature(String in, String out, int lim, boolean p) {
-        inLocation = in;
-        outLocation = out;
-        limit = lim;
-        prettyLogging = p;
+        super(new Portable(in, out, lim, p));
     }
 
     public LoggingFeature(String in, String out, int lim, boolean p, boolean showBinary) {
-        this(in, out, lim, p);
-        this.showBinary = showBinary;
+        super(new Portable(in, out, lim, p, showBinary));
     }
 
     public LoggingFeature(Logging annotation) {
-        inLocation = annotation.inLocation();
-        outLocation = annotation.outLocation();
-        limit = annotation.limit();
-        prettyLogging = annotation.pretty();
-        showBinary = annotation.showBinary();
+        super(new Portable(annotation));
     }
 
-    @Override
-    protected void initializeProvider(InterceptorProvider provider, Bus bus) {
-        if (limit == DEFAULT_LIMIT && inLocation == null
-            && outLocation == null && !prettyLogging) {
-            provider.getInInterceptors().add(IN);
-            provider.getInFaultInterceptors().add(IN);
-            provider.getOutInterceptors().add(OUT);
-            provider.getOutFaultInterceptors().add(OUT);
-        } else {
-            LoggingInInterceptor in = new LoggingInInterceptor(limit);
-            in.setOutputLocation(inLocation);
-            in.setPrettyLogging(prettyLogging);
-            in.setShowBinaryContent(showBinary);
-            LoggingOutInterceptor out = new LoggingOutInterceptor(limit);
-            out.setOutputLocation(outLocation);
-            out.setPrettyLogging(prettyLogging);
-            out.setShowBinaryContent(showBinary);
-
-            provider.getInInterceptors().add(in);
-            provider.getInFaultInterceptors().add(in);
-            provider.getOutInterceptors().add(out);
-            provider.getOutFaultInterceptors().add(out);
-        }
-    }
-
-    /**
-     * Set a limit on how much content can be logged
-     * @param lim
-     */
     public void setLimit(int lim) {
-        limit = lim;
+        delegate.setLimit(lim);
     }
 
-    /**
-     * Retrieve the value set with {@link #setLimit(int)}.
-     */
     public int getLimit() {
-        return limit;
+        return delegate.getLimit();
     }
 
-    /**
-     */
     public boolean isPrettyLogging() {
-        return prettyLogging;
+        return delegate.isPrettyLogging();
     }
-    /**
-     * Turn pretty logging of XML content on/off
-     * @param prettyLogging
-     */
+
     public void setPrettyLogging(boolean prettyLogging) {
-        this.prettyLogging = prettyLogging;
+        delegate.setPrettyLogging(prettyLogging);
+    }
+
+    @Provider(Type.Feature)
+    public static class Portable implements AbstractPortableFeature {
+        private static final int DEFAULT_LIMIT = AbstractLoggingInterceptor.DEFAULT_LIMIT;
+        private static final LoggingInInterceptor IN = new LoggingInInterceptor(DEFAULT_LIMIT);
+        private static final LoggingOutInterceptor OUT = new LoggingOutInterceptor(DEFAULT_LIMIT);
+
+
+        String inLocation;
+        String outLocation;
+        boolean prettyLogging;
+        boolean showBinary;
+
+        int limit = DEFAULT_LIMIT;
+
+        public Portable() {
+
+        }
+        public Portable(int lim) {
+            limit = lim;
+        }
+        public Portable(String in, String out) {
+            inLocation = in;
+            outLocation = out;
+        }
+        public Portable(String in, String out, int lim) {
+            inLocation = in;
+            outLocation = out;
+            limit = lim;
+        }
+
+        public Portable(String in, String out, int lim, boolean p) {
+            inLocation = in;
+            outLocation = out;
+            limit = lim;
+            prettyLogging = p;
+        }
+
+        public Portable(String in, String out, int lim, boolean p, boolean showBinary) {
+            this(in, out, lim, p);
+            this.showBinary = showBinary;
+        }
+
+        public Portable(Logging annotation) {
+            inLocation = annotation.inLocation();
+            outLocation = annotation.outLocation();
+            limit = annotation.limit();
+            prettyLogging = annotation.pretty();
+            showBinary = annotation.showBinary();
+        }
+
+        @Override
+        public void doInitializeProvider(InterceptorProvider provider, Bus bus) {
+            if (limit == DEFAULT_LIMIT && inLocation == null
+                    && outLocation == null && !prettyLogging) {
+                provider.getInInterceptors().add(IN);
+                provider.getInFaultInterceptors().add(IN);
+                provider.getOutInterceptors().add(OUT);
+                provider.getOutFaultInterceptors().add(OUT);
+            } else {
+                LoggingInInterceptor in = new LoggingInInterceptor(limit);
+                in.setOutputLocation(inLocation);
+                in.setPrettyLogging(prettyLogging);
+                in.setShowBinaryContent(showBinary);
+                LoggingOutInterceptor out = new LoggingOutInterceptor(limit);
+                out.setOutputLocation(outLocation);
+                out.setPrettyLogging(prettyLogging);
+                out.setShowBinaryContent(showBinary);
+
+                provider.getInInterceptors().add(in);
+                provider.getInFaultInterceptors().add(in);
+                provider.getOutInterceptors().add(out);
+                provider.getOutFaultInterceptors().add(out);
+            }
+        }
+
+        /**
+         * Set a limit on how much content can be logged
+         * @param lim
+         */
+        public void setLimit(int lim) {
+            limit = lim;
+        }
+
+        /**
+         * Retrieve the value set with {@link #setLimit(int)}.
+         */
+        public int getLimit() {
+            return limit;
+        }
+
+        /**
+         */
+        public boolean isPrettyLogging() {
+            return prettyLogging;
+        }
+        /**
+         * Turn pretty logging of XML content on/off
+         * @param prettyLogging
+         */
+        public void setPrettyLogging(boolean prettyLogging) {
+            this.prettyLogging = prettyLogging;
+        }
     }
 }
diff --git a/core/src/main/java/org/apache/cxf/feature/StaxTransformFeature.java b/core/src/main/java/org/apache/cxf/feature/StaxTransformFeature.java
index 398b6b8..3064110 100644
--- a/core/src/main/java/org/apache/cxf/feature/StaxTransformFeature.java
+++ b/core/src/main/java/org/apache/cxf/feature/StaxTransformFeature.java
@@ -39,69 +39,123 @@ import org.apache.cxf.interceptor.transform.TransformOutInterceptor;
   </pre>
  */
 @NoJSR250Annotations
-public class StaxTransformFeature extends AbstractFeature {
-
-    private TransformInInterceptor in = new TransformInInterceptor();
-    private TransformOutInterceptor out = new TransformOutInterceptor();
+public class StaxTransformFeature extends DelegatingFeature<StaxTransformFeature.Portable> {
 
     public StaxTransformFeature() {
-        //
-    }
-
-    @Override
-    protected void initializeProvider(InterceptorProvider provider, Bus bus) {
-
-        provider.getInInterceptors().add(in);
-        provider.getOutInterceptors().add(out);
-        provider.getOutFaultInterceptors().add(out);
+        super(new Portable());
     }
 
     public void setOutTransformElements(Map<String, String> outElements) {
-        out.setOutTransformElements(outElements);
+        delegate.setOutTransformElements(outElements);
     }
 
     public void setOutTransformAttributes(Map<String, String> outAttributes) {
-        out.setOutTransformAttributes(outAttributes);
+        delegate.setOutTransformAttributes(outAttributes);
     }
 
     public void setAttributesToElements(boolean value) {
-        out.setAttributesToElements(value);
+        delegate.setAttributesToElements(value);
     }
 
     public void setSkipOnFault(boolean value) {
-        out.setSkipOnFault(value);
+        delegate.setSkipOnFault(value);
     }
 
     public void setOutAppendElements(Map<String, String> map) {
-        out.setOutAppendElements(map);
+        delegate.setOutAppendElements(map);
     }
 
     public void setOutDropElements(List<String> dropElementsSet) {
-        out.setOutDropElements(dropElementsSet);
+        delegate.setOutDropElements(dropElementsSet);
     }
 
     public void setInAppendElements(Map<String, String> inElements) {
-        in.setInAppendElements(inElements);
+        delegate.setInAppendElements(inElements);
     }
 
     public void setInDropElements(List<String> dropElementsSet) {
-        in.setInDropElements(dropElementsSet);
+        delegate.setInDropElements(dropElementsSet);
     }
 
     public void setInTransformElements(Map<String, String> inElements) {
-        in.setInTransformElements(inElements);
+        delegate.setInTransformElements(inElements);
     }
 
     public void setInTransformAttributes(Map<String, String> inAttributes) {
-        in.setInTransformAttributes(inAttributes);
+        delegate.setInTransformAttributes(inAttributes);
     }
 
     public void setOutDefaultNamespace(String ns) {
-        out.setDefaultNamespace(ns);
+        delegate.setOutDefaultNamespace(ns);
     }
 
     public void setContextPropertyName(String propertyName) {
-        in.setContextPropertyName(propertyName);
-        out.setContextPropertyName(propertyName);
+        delegate.setContextPropertyName(propertyName);
+    }
+
+    public static class Portable implements AbstractPortableFeature {
+        private TransformInInterceptor in = new TransformInInterceptor();
+        private TransformOutInterceptor out = new TransformOutInterceptor();
+
+        public Portable() {
+            //
+        }
+
+        @Override
+        public void doInitializeProvider(InterceptorProvider provider, Bus bus) {
+
+            provider.getInInterceptors().add(in);
+            provider.getOutInterceptors().add(out);
+            provider.getOutFaultInterceptors().add(out);
+        }
+
+        public void setOutTransformElements(Map<String, String> outElements) {
+            out.setOutTransformElements(outElements);
+        }
+
+        public void setOutTransformAttributes(Map<String, String> outAttributes) {
+            out.setOutTransformAttributes(outAttributes);
+        }
+
+        public void setAttributesToElements(boolean value) {
+            out.setAttributesToElements(value);
+        }
+
+        public void setSkipOnFault(boolean value) {
+            out.setSkipOnFault(value);
+        }
+
+        public void setOutAppendElements(Map<String, String> map) {
+            out.setOutAppendElements(map);
+        }
+
+        public void setOutDropElements(List<String> dropElementsSet) {
+            out.setOutDropElements(dropElementsSet);
+        }
+
+        public void setInAppendElements(Map<String, String> inElements) {
+            in.setInAppendElements(inElements);
+        }
+
+        public void setInDropElements(List<String> dropElementsSet) {
+            in.setInDropElements(dropElementsSet);
+        }
+
+        public void setInTransformElements(Map<String, String> inElements) {
+            in.setInTransformElements(inElements);
+        }
+
+        public void setInTransformAttributes(Map<String, String> inAttributes) {
+            in.setInTransformAttributes(inAttributes);
+        }
+
+        public void setOutDefaultNamespace(String ns) {
+            out.setDefaultNamespace(ns);
+        }
+
+        public void setContextPropertyName(String propertyName) {
+            in.setContextPropertyName(propertyName);
+            out.setContextPropertyName(propertyName);
+        }
     }
 }
diff --git a/core/src/main/java/org/apache/cxf/feature/transform/XSLTFeature.java b/core/src/main/java/org/apache/cxf/feature/transform/XSLTFeature.java
index f09d47b..1d3b62b 100644
--- a/core/src/main/java/org/apache/cxf/feature/transform/XSLTFeature.java
+++ b/core/src/main/java/org/apache/cxf/feature/transform/XSLTFeature.java
@@ -20,7 +20,8 @@ package org.apache.cxf.feature.transform;
 
 import org.apache.cxf.Bus;
 import org.apache.cxf.common.injection.NoJSR250Annotations;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.interceptor.InterceptorProvider;
 
 /**
@@ -31,30 +32,43 @@ import org.apache.cxf.interceptor.InterceptorProvider;
  * (can be fixed in further versions when XSLT engine supports XML stream).
  */
 @NoJSR250Annotations
-public class XSLTFeature extends AbstractFeature {
-    private String inXSLTPath;
-    private String outXSLTPath;
-
-    @Override
-    protected void initializeProvider(InterceptorProvider provider, Bus bus) {
-        if (inXSLTPath != null) {
-            XSLTInInterceptor in = new XSLTInInterceptor(inXSLTPath);
-            provider.getInInterceptors().add(in);
-        }
-
-        if (outXSLTPath != null) {
-            XSLTOutInterceptor out = new XSLTOutInterceptor(outXSLTPath);
-            provider.getOutInterceptors().add(out);
-            provider.getOutFaultInterceptors().add(out);
-        }
+public class XSLTFeature extends DelegatingFeature<XSLTFeature.Portable> {
+    public XSLTFeature() {
+        super(new Portable());
     }
 
     public void setInXSLTPath(String inXSLTPath) {
-        this.inXSLTPath = inXSLTPath;
+        delegate.setInXSLTPath(inXSLTPath);
     }
 
     public void setOutXSLTPath(String outXSLTPath) {
-        this.outXSLTPath = outXSLTPath;
+        delegate.setOutXSLTPath(outXSLTPath);
     }
 
+    public static class Portable implements AbstractPortableFeature {
+        private String inXSLTPath;
+        private String outXSLTPath;
+
+        @Override
+        public void doInitializeProvider(InterceptorProvider provider, Bus bus) {
+            if (inXSLTPath != null) {
+                XSLTInInterceptor in = new XSLTInInterceptor(inXSLTPath);
+                provider.getInInterceptors().add(in);
+            }
+
+            if (outXSLTPath != null) {
+                XSLTOutInterceptor out = new XSLTOutInterceptor(outXSLTPath);
+                provider.getOutInterceptors().add(out);
+                provider.getOutFaultInterceptors().add(out);
+            }
+        }
+
+        public void setInXSLTPath(String inXSLTPath) {
+            this.inXSLTPath = inXSLTPath;
+        }
+
+        public void setOutXSLTPath(String outXSLTPath) {
+            this.outXSLTPath = outXSLTPath;
+        }
+    }
 }
diff --git a/core/src/main/java/org/apache/cxf/feature/validation/SchemaValidationFeature.java b/core/src/main/java/org/apache/cxf/feature/validation/SchemaValidationFeature.java
index 12ec4d5..e8ca6bb 100644
--- a/core/src/main/java/org/apache/cxf/feature/validation/SchemaValidationFeature.java
+++ b/core/src/main/java/org/apache/cxf/feature/validation/SchemaValidationFeature.java
@@ -24,7 +24,9 @@ import org.apache.cxf.annotations.SchemaValidation.SchemaValidationType;
 import org.apache.cxf.endpoint.Client;
 import org.apache.cxf.endpoint.Endpoint;
 import org.apache.cxf.endpoint.Server;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
+import org.apache.cxf.interceptor.InterceptorProvider;
 import org.apache.cxf.message.Message;
 import org.apache.cxf.service.model.BindingOperationInfo;
 
@@ -32,26 +34,37 @@ import org.apache.cxf.service.model.BindingOperationInfo;
  * A feature to configure schema validation at the operation level, as an alternative to
  * using the @SchemaValidation annotation.
  */
-public class SchemaValidationFeature extends AbstractFeature {
-    private final SchemaValidationTypeProvider provider;
-
+public class SchemaValidationFeature extends DelegatingFeature<SchemaValidationFeature.Portable> {
     public SchemaValidationFeature(final SchemaValidationTypeProvider provider) {
-        this.provider = provider;
+        super(new Portable(provider));
     }
 
-    public void initialize(Server server, Bus bus) {
-        initialise(server.getEndpoint());
-    }
+    public static class Portable implements AbstractPortableFeature {
+        private final SchemaValidationTypeProvider provider;
 
-    public void initialize(Client client, Bus bus) {
-        initialise(client.getEndpoint());
-    }
+        public Portable(final SchemaValidationTypeProvider provider) {
+            this.provider = provider;
+        }
+
+        public void initialize(Server server, Bus bus) {
+            initialise(server.getEndpoint());
+        }
+
+        public void initialize(Client client, Bus bus) {
+            initialise(client.getEndpoint());
+        }
+
+        @Override
+        public void doInitializeProvider(InterceptorProvider interceptorProvider, Bus bus) {
+            // no-op
+        }
 
-    private void initialise(Endpoint endpoint) {
-        for (BindingOperationInfo bop : endpoint.getEndpointInfo().getBinding().getOperations()) {
-            SchemaValidationType type = provider.getSchemaValidationType(bop.getOperationInfo());
-            if (type != null) {
-                bop.getOperationInfo().setProperty(Message.SCHEMA_VALIDATION_TYPE, type);
+        private void initialise(Endpoint endpoint) {
+            for (BindingOperationInfo bop : endpoint.getEndpointInfo().getBinding().getOperations()) {
+                SchemaValidationType type = provider.getSchemaValidationType(bop.getOperationInfo());
+                if (type != null) {
+                    bop.getOperationInfo().setProperty(Message.SCHEMA_VALIDATION_TYPE, type);
+                }
             }
         }
     }
diff --git a/core/src/main/java/org/apache/cxf/interceptor/security/JAASAuthenticationFeature.java b/core/src/main/java/org/apache/cxf/interceptor/security/JAASAuthenticationFeature.java
index 1b8aa91..42c0daf 100644
--- a/core/src/main/java/org/apache/cxf/interceptor/security/JAASAuthenticationFeature.java
+++ b/core/src/main/java/org/apache/cxf/interceptor/security/JAASAuthenticationFeature.java
@@ -19,40 +19,54 @@
 package org.apache.cxf.interceptor.security;
 
 import org.apache.cxf.Bus;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.interceptor.InterceptorProvider;
 
 /**
  * Feature to do JAAS authentication with defaults for karaf integration
  */
-public class JAASAuthenticationFeature extends AbstractFeature {
+public class JAASAuthenticationFeature extends DelegatingFeature<JAASAuthenticationFeature.Portable> {
     public static final String ID = "jaas";
 
-    private String contextName = "karaf";
-    private boolean reportFault;
+    public JAASAuthenticationFeature() {
+        super(new Portable());
+    }
+
+    public void setContextName(String contextName) {
+        delegate.setContextName(contextName);
+    }
+
+    public void setReportFault(boolean reportFault) {
+        delegate.setReportFault(reportFault);
+    }
 
     @Override
     public String getID() {
         return ID;
     }
 
-    @Override
-    protected void initializeProvider(InterceptorProvider provider, Bus bus) {
-        JAASLoginInterceptor jaasLoginInterceptor = new JAASLoginInterceptor();
-        jaasLoginInterceptor.setRoleClassifierType(JAASLoginInterceptor.ROLE_CLASSIFIER_CLASS_NAME);
-        jaasLoginInterceptor.setRoleClassifier("org.apache.karaf.jaas.boot.principal.RolePrincipal");
-        jaasLoginInterceptor.setContextName(contextName);
-        jaasLoginInterceptor.setReportFault(reportFault);
-        provider.getInInterceptors().add(jaasLoginInterceptor);
-        super.initializeProvider(provider, bus);
-    }
+    public static class Portable implements AbstractPortableFeature {
+        private String contextName = "karaf";
+        private boolean reportFault;
 
-    public void setContextName(String contextName) {
-        this.contextName = contextName;
-    }
+        @Override
+        public void doInitializeProvider(InterceptorProvider provider, Bus bus) {
+            JAASLoginInterceptor jaasLoginInterceptor = new JAASLoginInterceptor();
+            jaasLoginInterceptor.setRoleClassifierType(JAASLoginInterceptor.ROLE_CLASSIFIER_CLASS_NAME);
+            jaasLoginInterceptor.setRoleClassifier("org.apache.karaf.jaas.boot.principal.RolePrincipal");
+            jaasLoginInterceptor.setContextName(contextName);
+            jaasLoginInterceptor.setReportFault(reportFault);
+            provider.getInInterceptors().add(jaasLoginInterceptor);
+        }
 
-    public void setReportFault(boolean reportFault) {
-        this.reportFault = reportFault;
+        public void setContextName(String contextName) {
+            this.contextName = contextName;
+        }
+
+        public void setReportFault(boolean reportFault) {
+            this.reportFault = reportFault;
+        }
     }
 
 }
diff --git a/core/src/main/java/org/apache/cxf/transport/common/gzip/GZIPFeature.java b/core/src/main/java/org/apache/cxf/transport/common/gzip/GZIPFeature.java
index 9e56d87..9b7d53c 100644
--- a/core/src/main/java/org/apache/cxf/transport/common/gzip/GZIPFeature.java
+++ b/core/src/main/java/org/apache/cxf/transport/common/gzip/GZIPFeature.java
@@ -23,7 +23,8 @@ import java.util.List;
 import org.apache.cxf.Bus;
 import org.apache.cxf.annotations.Provider;
 import org.apache.cxf.common.injection.NoJSR250Annotations;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.interceptor.Interceptor;
 import org.apache.cxf.interceptor.InterceptorProvider;
 import org.apache.cxf.message.Message;
@@ -50,69 +51,97 @@ import org.apache.cxf.message.Message;
  */
 @NoJSR250Annotations
 @Provider(value = Provider.Type.Feature)
-public class GZIPFeature extends AbstractFeature {
-    private static final GZIPInInterceptor IN = new GZIPInInterceptor();
-    private static final GZIPOutInterceptor OUT = new GZIPOutInterceptor();
-
-    /**
-     * The compression threshold to pass to the outgoing interceptor.
-     */
-    int threshold = -1;
-
-    /**
-     * Force GZIP instead of negotiate
-     */
-    boolean force;
-
-
-    @Override
-    protected void initializeProvider(InterceptorProvider provider, Bus bus) {
-        provider.getInInterceptors().add(IN);
-        if (threshold == -1 && !force) {
-            provider.getOutInterceptors().add(OUT);
-            provider.getOutFaultInterceptors().add(OUT);
-        } else {
-            GZIPOutInterceptor out = new GZIPOutInterceptor();
-            out.setThreshold(threshold);
-            out.setForce(force);
-            remove(provider.getOutInterceptors());
-            remove(provider.getOutFaultInterceptors());
-            provider.getOutInterceptors().add(out);
-            provider.getOutFaultInterceptors().add(out);
-        }
+public class GZIPFeature extends DelegatingFeature<GZIPFeature.Portable> {
+
+    public GZIPFeature() {
+        super(new Portable());
     }
 
-    private void remove(List<Interceptor<? extends Message>> outInterceptors) {
-        int x = outInterceptors.size();
-        while (x > 0) {
-            --x;
-            if (outInterceptors.get(x) instanceof GZIPOutInterceptor) {
-                outInterceptors.remove(x);
-            }
-        }
+    public void remove(List<Interceptor<? extends Message>> outInterceptors) {
+        delegate.remove(outInterceptors);
     }
 
     public void setThreshold(int threshold) {
-        this.threshold = threshold;
+        delegate.setThreshold(threshold);
     }
 
     public int getThreshold() {
-        return threshold;
+        return delegate.getThreshold();
     }
 
-
-    /**
-     * Set if GZIP is always used without negotiation
-     * @param b
-     */
     public void setForce(boolean b) {
-        force = b;
+        delegate.setForce(b);
     }
 
-    /**
-     * Retrieve the value set with {@link #setForce(boolean)}.
-     */
     public boolean getForce() {
-        return force;
+        return delegate.getForce();
+    }
+
+    @Provider(value = Provider.Type.Feature)
+    public static class Portable implements AbstractPortableFeature {
+        private static final GZIPInInterceptor IN = new GZIPInInterceptor();
+        private static final GZIPOutInterceptor OUT = new GZIPOutInterceptor();
+
+        /**
+         * The compression threshold to pass to the outgoing interceptor.
+         */
+        int threshold = -1;
+
+        /**
+         * Force GZIP instead of negotiate
+         */
+        boolean force;
+
+
+        @Override
+        public void doInitializeProvider(InterceptorProvider provider, Bus bus) {
+            provider.getInInterceptors().add(IN);
+            if (threshold == -1 && !force) {
+                provider.getOutInterceptors().add(OUT);
+                provider.getOutFaultInterceptors().add(OUT);
+            } else {
+                GZIPOutInterceptor out = new GZIPOutInterceptor();
+                out.setThreshold(threshold);
+                out.setForce(force);
+                remove(provider.getOutInterceptors());
+                remove(provider.getOutFaultInterceptors());
+                provider.getOutInterceptors().add(out);
+                provider.getOutFaultInterceptors().add(out);
+            }
+        }
+
+        private void remove(List<Interceptor<? extends Message>> outInterceptors) {
+            int x = outInterceptors.size();
+            while (x > 0) {
+                --x;
+                if (outInterceptors.get(x) instanceof GZIPOutInterceptor) {
+                    outInterceptors.remove(x);
+                }
+            }
+        }
+
+        public void setThreshold(int threshold) {
+            this.threshold = threshold;
+        }
+
+        public int getThreshold() {
+            return threshold;
+        }
+
+
+        /**
+         * Set if GZIP is always used without negotiation
+         * @param b
+         */
+        public void setForce(boolean b) {
+            force = b;
+        }
+
+        /**
+         * Retrieve the value set with {@link #setForce(boolean)}.
+         */
+        public boolean getForce() {
+            return force;
+        }
     }
 }
diff --git a/core/src/main/java/org/apache/cxf/validation/BeanValidationFeature.java b/core/src/main/java/org/apache/cxf/validation/BeanValidationFeature.java
index 26fe2a6..d5da2a3 100644
--- a/core/src/main/java/org/apache/cxf/validation/BeanValidationFeature.java
+++ b/core/src/main/java/org/apache/cxf/validation/BeanValidationFeature.java
@@ -22,27 +22,41 @@ import org.apache.cxf.Bus;
 import org.apache.cxf.annotations.Provider;
 import org.apache.cxf.annotations.Provider.Scope;
 import org.apache.cxf.annotations.Provider.Type;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.interceptor.InterceptorProvider;
 
 @Provider(value = Type.Feature, scope = Scope.Server)
-public class BeanValidationFeature extends AbstractFeature {
+public class BeanValidationFeature extends DelegatingFeature<BeanValidationFeature.Portable> {
 
-    private BeanValidationProvider validationProvider;
-
-    @Override
-    protected void initializeProvider(InterceptorProvider interceptorProvider, Bus bus) {
-        BeanValidationInInterceptor in = new BeanValidationInInterceptor();
-        BeanValidationOutInterceptor out = new BeanValidationOutInterceptor();
-        if (validationProvider != null) {
-            in.setProvider(validationProvider);
-            out.setProvider(validationProvider);
-        }
-        interceptorProvider.getInInterceptors().add(in);
-        interceptorProvider.getOutInterceptors().add(out);
+    public BeanValidationFeature() {
+        super(new Portable());
     }
 
     public void setProvider(BeanValidationProvider provider) {
-        this.validationProvider = provider;
+        delegate.setProvider(provider);
+    }
+
+    @Provider(value = Type.Feature, scope = Scope.Server)
+    public static class Portable implements AbstractPortableFeature {
+
+        private BeanValidationProvider validationProvider;
+
+        @Override
+        public void doInitializeProvider(InterceptorProvider interceptorProvider, Bus bus) {
+            BeanValidationInInterceptor in = new BeanValidationInInterceptor();
+            BeanValidationOutInterceptor out = new BeanValidationOutInterceptor();
+            if (validationProvider != null) {
+                in.setProvider(validationProvider);
+                out.setProvider(validationProvider);
+            }
+            interceptorProvider.getInInterceptors().add(in);
+            interceptorProvider.getOutInterceptors().add(out);
+        }
+
+        public void setProvider(BeanValidationProvider provider) {
+            this.validationProvider = provider;
+        }
     }
+
 }
diff --git a/core/src/main/java/org/apache/cxf/validation/ClientBeanValidationFeature.java b/core/src/main/java/org/apache/cxf/validation/ClientBeanValidationFeature.java
index d975a3d..31b01b5 100644
--- a/core/src/main/java/org/apache/cxf/validation/ClientBeanValidationFeature.java
+++ b/core/src/main/java/org/apache/cxf/validation/ClientBeanValidationFeature.java
@@ -22,29 +22,48 @@ import org.apache.cxf.Bus;
 import org.apache.cxf.annotations.Provider;
 import org.apache.cxf.annotations.Provider.Scope;
 import org.apache.cxf.annotations.Provider.Type;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.interceptor.InterceptorProvider;
 
 @Provider(value = Type.Feature, scope = Scope.Client)
-public class ClientBeanValidationFeature extends AbstractFeature {
+public class ClientBeanValidationFeature extends DelegatingFeature<ClientBeanValidationFeature.Portable> {
+    public ClientBeanValidationFeature() {
+        super(new Portable());
+    }
+
+    protected ClientBeanValidationFeature(final Portable d) {
+        super(d);
+    }
 
-    private BeanValidationProvider validationProvider;
+    public void addInterceptor(InterceptorProvider interceptorProvider, ClientBeanValidationOutInterceptor out) {
+        delegate.addInterceptor(interceptorProvider, out);
+    }
 
-    @Override
-    protected void initializeProvider(InterceptorProvider interceptorProvider, Bus bus) {
-        ClientBeanValidationOutInterceptor out = new ClientBeanValidationOutInterceptor();
-        addInterceptor(interceptorProvider, out);
+    public void setProvider(BeanValidationProvider provider) {
+        delegate.setProvider(provider);
     }
 
-    protected void addInterceptor(InterceptorProvider interceptorProvider, ClientBeanValidationOutInterceptor out) {
-        if (validationProvider != null) {
-            out.setProvider(validationProvider);
+    @Provider(value = Type.Feature, scope = Scope.Client)
+    public static class Portable implements AbstractPortableFeature {
+        private BeanValidationProvider validationProvider;
+
+        @Override
+        public void doInitializeProvider(InterceptorProvider interceptorProvider, Bus bus) {
+            ClientBeanValidationOutInterceptor out = new ClientBeanValidationOutInterceptor();
+            addInterceptor(interceptorProvider, out);
         }
-        interceptorProvider.getOutInterceptors().add(out);
 
-    }
+        protected void addInterceptor(InterceptorProvider interceptorProvider, ClientBeanValidationOutInterceptor out) {
+            if (validationProvider != null) {
+                out.setProvider(validationProvider);
+            }
+            interceptorProvider.getOutInterceptors().add(out);
 
-    public void setProvider(BeanValidationProvider provider) {
-        this.validationProvider = provider;
+        }
+
+        public void setProvider(BeanValidationProvider provider) {
+            this.validationProvider = provider;
+        }
     }
 }
diff --git a/integration/tracing/tracing-brave/src/main/java/org/apache/cxf/tracing/brave/BraveClientFeature.java b/integration/tracing/tracing-brave/src/main/java/org/apache/cxf/tracing/brave/BraveClientFeature.java
index 509c7bc..17d8390 100644
--- a/integration/tracing/tracing-brave/src/main/java/org/apache/cxf/tracing/brave/BraveClientFeature.java
+++ b/integration/tracing/tracing-brave/src/main/java/org/apache/cxf/tracing/brave/BraveClientFeature.java
@@ -25,32 +25,44 @@ import org.apache.cxf.annotations.Provider;
 import org.apache.cxf.annotations.Provider.Scope;
 import org.apache.cxf.annotations.Provider.Type;
 import org.apache.cxf.common.injection.NoJSR250Annotations;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.interceptor.InterceptorProvider;
 
 @NoJSR250Annotations
 @Provider(value = Type.Feature, scope = Scope.Client)
-public class BraveClientFeature extends AbstractFeature {
-    private BraveClientStartInterceptor out;
-    private BraveClientStopInterceptor in;
-
+public class BraveClientFeature extends DelegatingFeature<BraveClientFeature.Portable> {
     public BraveClientFeature(final Tracing tracing) {
-        this(
-          HttpTracing
-              .newBuilder(tracing)
-              .clientParser(new HttpClientSpanParser())
-              .build()
-        );
+        super(new Portable(tracing));
     }
-    
+
     public BraveClientFeature(HttpTracing brave) {
-        out = new BraveClientStartInterceptor(brave);
-        in = new BraveClientStopInterceptor(brave);
+        super(new Portable(brave));
     }
 
-    @Override
-    protected void initializeProvider(InterceptorProvider provider, Bus bus) {
-        provider.getInInterceptors().add(in);
-        provider.getOutInterceptors().add(out);
+    @Provider(value = Type.Feature, scope = Scope.Client)
+    public static class Portable implements AbstractPortableFeature {
+        private BraveClientStartInterceptor out;
+        private BraveClientStopInterceptor in;
+
+        public Portable(final Tracing tracing) {
+            this(
+                    HttpTracing
+                            .newBuilder(tracing)
+                            .clientParser(new HttpClientSpanParser())
+                            .build()
+            );
+        }
+
+        public Portable(HttpTracing brave) {
+            out = new BraveClientStartInterceptor(brave);
+            in = new BraveClientStopInterceptor(brave);
+        }
+
+        @Override
+        public void doInitializeProvider(InterceptorProvider provider, Bus bus) {
+            provider.getInInterceptors().add(in);
+            provider.getOutInterceptors().add(out);
+        }
     }
 }
diff --git a/integration/tracing/tracing-brave/src/main/java/org/apache/cxf/tracing/brave/BraveFeature.java b/integration/tracing/tracing-brave/src/main/java/org/apache/cxf/tracing/brave/BraveFeature.java
index faf9c8d..7414739 100644
--- a/integration/tracing/tracing-brave/src/main/java/org/apache/cxf/tracing/brave/BraveFeature.java
+++ b/integration/tracing/tracing-brave/src/main/java/org/apache/cxf/tracing/brave/BraveFeature.java
@@ -27,48 +27,69 @@ import org.apache.cxf.annotations.Provider;
 import org.apache.cxf.annotations.Provider.Scope;
 import org.apache.cxf.annotations.Provider.Type;
 import org.apache.cxf.common.injection.NoJSR250Annotations;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.interceptor.InterceptorProvider;
 
 @NoJSR250Annotations
 @Provider(value = Type.Feature, scope = Scope.Server)
-public class BraveFeature extends AbstractFeature {
-    private BraveStartInterceptor in;
-    private BraveStopInterceptor out;
-
+public class BraveFeature extends DelegatingFeature<BraveFeature.Portable> {
     public BraveFeature() {
         this("cxf-svc-" + UUID.randomUUID().toString());
     }
 
-    public BraveFeature(final String name) {
-        this(
-            HttpTracing
-                .newBuilder(Tracing.newBuilder().localServiceName(name).build())
-                .serverParser(new HttpServerSpanParser())
-                .build()
-        );
-    }
-    
     public BraveFeature(final Tracing tracing) {
-        this(
-          HttpTracing
-              .newBuilder(tracing)
-              .serverParser(new HttpServerSpanParser())
-              .build()
-        );
+        this(Portable.newTracer(tracing));
+    }
+
+    public BraveFeature(final String name) {
+        this(Portable.newTracer(Portable.newTracing(name)));
     }
 
     public BraveFeature(HttpTracing brave) {
-        in = new BraveStartInterceptor(brave);
-        out = new BraveStopInterceptor(brave);
+        super(new Portable(brave));
     }
 
-    @Override
-    protected void initializeProvider(InterceptorProvider provider, Bus bus) {
-        provider.getInInterceptors().add(in);
-        provider.getInFaultInterceptors().add(in);
+    @Provider(value = Type.Feature, scope = Scope.Server)
+    public static class Portable implements AbstractPortableFeature {
+        private BraveStartInterceptor in;
+        private BraveStopInterceptor out;
+
+        public Portable() {
+            this("cxf-svc-" + UUID.randomUUID().toString());
+        }
+
+        public Portable(final String name) {
+            this(newTracer(newTracing(name)));
+        }
+
+        public Portable(final Tracing tracing) {
+            this(newTracer(tracing));
+        }
+
+        public Portable(HttpTracing brave) {
+            in = new BraveStartInterceptor(brave);
+            out = new BraveStopInterceptor(brave);
+        }
+
+        @Override
+        public void doInitializeProvider(InterceptorProvider provider, Bus bus) {
+            provider.getInInterceptors().add(in);
+            provider.getInFaultInterceptors().add(in);
+
+            provider.getOutInterceptors().add(out);
+            provider.getOutFaultInterceptors().add(out);
+        }
+
+        private static HttpTracing newTracer(Tracing tracing) {
+            return HttpTracing
+                    .newBuilder(tracing)
+                    .serverParser(new HttpServerSpanParser())
+                    .build();
+        }
 
-        provider.getOutInterceptors().add(out);
-        provider.getOutFaultInterceptors().add(out);
+        private static Tracing newTracing(String name) {
+            return Tracing.newBuilder().localServiceName(name).build();
+        }
     }
 }
diff --git a/integration/tracing/tracing-opentracing/src/main/java/org/apache/cxf/tracing/opentracing/OpenTracingClientFeature.java b/integration/tracing/tracing-opentracing/src/main/java/org/apache/cxf/tracing/opentracing/OpenTracingClientFeature.java
index d5ab3cf..e2b010e 100644
--- a/integration/tracing/tracing-opentracing/src/main/java/org/apache/cxf/tracing/opentracing/OpenTracingClientFeature.java
+++ b/integration/tracing/tracing-opentracing/src/main/java/org/apache/cxf/tracing/opentracing/OpenTracingClientFeature.java
@@ -23,25 +23,33 @@ import org.apache.cxf.annotations.Provider;
 import org.apache.cxf.annotations.Provider.Scope;
 import org.apache.cxf.annotations.Provider.Type;
 import org.apache.cxf.common.injection.NoJSR250Annotations;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.interceptor.InterceptorProvider;
 
 import io.opentracing.Tracer;
 
 @NoJSR250Annotations
 @Provider(value = Type.Feature, scope = Scope.Client)
-public class OpenTracingClientFeature extends AbstractFeature {
-    private OpenTracingClientStartInterceptor out;
-    private OpenTracingClientStopInterceptor in;
-    
+public class OpenTracingClientFeature extends DelegatingFeature<OpenTracingClientFeature.Portable> {
     public OpenTracingClientFeature(Tracer tracer) {
-        out = new OpenTracingClientStartInterceptor(tracer);
-        in = new OpenTracingClientStopInterceptor(tracer);
+        super(new Portable(tracer));
     }
 
-    @Override
-    protected void initializeProvider(InterceptorProvider provider, Bus bus) {
-        provider.getInInterceptors().add(in);
-        provider.getOutInterceptors().add(out);
+    @Provider(value = Type.Feature, scope = Scope.Client)
+    public static class Portable implements AbstractPortableFeature {
+        private OpenTracingClientStartInterceptor out;
+        private OpenTracingClientStopInterceptor in;
+
+        public Portable(Tracer tracer) {
+            out = new OpenTracingClientStartInterceptor(tracer);
+            in = new OpenTracingClientStopInterceptor(tracer);
+        }
+
+        @Override
+        public void doInitializeProvider(InterceptorProvider provider, Bus bus) {
+            provider.getInInterceptors().add(in);
+            provider.getOutInterceptors().add(out);
+        }
     }
 }
diff --git a/integration/tracing/tracing-opentracing/src/main/java/org/apache/cxf/tracing/opentracing/OpenTracingFeature.java b/integration/tracing/tracing-opentracing/src/main/java/org/apache/cxf/tracing/opentracing/OpenTracingFeature.java
index 5ed3223..9b91918 100644
--- a/integration/tracing/tracing-opentracing/src/main/java/org/apache/cxf/tracing/opentracing/OpenTracingFeature.java
+++ b/integration/tracing/tracing-opentracing/src/main/java/org/apache/cxf/tracing/opentracing/OpenTracingFeature.java
@@ -23,7 +23,8 @@ import org.apache.cxf.annotations.Provider;
 import org.apache.cxf.annotations.Provider.Scope;
 import org.apache.cxf.annotations.Provider.Type;
 import org.apache.cxf.common.injection.NoJSR250Annotations;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.interceptor.InterceptorProvider;
 
 import io.opentracing.Tracer;
@@ -31,25 +32,36 @@ import io.opentracing.util.GlobalTracer;
 
 @NoJSR250Annotations
 @Provider(value = Type.Feature, scope = Scope.Server)
-public class OpenTracingFeature extends AbstractFeature {
-    private OpenTracingStartInterceptor in;
-    private OpenTracingStopInterceptor out;
-
+public class OpenTracingFeature extends DelegatingFeature<OpenTracingFeature.Portable> {
     public OpenTracingFeature() {
-        this(GlobalTracer.get());
+        super(new Portable());
     }
-    
+
     public OpenTracingFeature(final Tracer tracer) {
-        in = new OpenTracingStartInterceptor(tracer);
-        out = new OpenTracingStopInterceptor(tracer);
+        super(new Portable(tracer));
     }
 
-    @Override
-    protected void initializeProvider(InterceptorProvider provider, Bus bus) {
-        provider.getInInterceptors().add(in);
-        provider.getInFaultInterceptors().add(in);
+    @Provider(value = Type.Feature, scope = Scope.Server)
+    public static class Portable implements AbstractPortableFeature {
+        private OpenTracingStartInterceptor in;
+        private OpenTracingStopInterceptor out;
+
+        public Portable() {
+            this(GlobalTracer.get());
+        }
+
+        public Portable(final Tracer tracer) {
+            in = new OpenTracingStartInterceptor(tracer);
+            out = new OpenTracingStopInterceptor(tracer);
+        }
+
+        @Override
+        public void doInitializeProvider(InterceptorProvider provider, Bus bus) {
+            provider.getInInterceptors().add(in);
+            provider.getInFaultInterceptors().add(in);
 
-        provider.getOutInterceptors().add(out);
-        provider.getOutFaultInterceptors().add(out);
+            provider.getOutInterceptors().add(out);
+            provider.getOutFaultInterceptors().add(out);
+        }
     }
 }
diff --git a/rt/bindings/coloc/src/main/java/org/apache/cxf/binding/coloc/feature/ColocFeature.java b/rt/bindings/coloc/src/main/java/org/apache/cxf/binding/coloc/feature/ColocFeature.java
index c943f4f..9f98832 100644
--- a/rt/bindings/coloc/src/main/java/org/apache/cxf/binding/coloc/feature/ColocFeature.java
+++ b/rt/bindings/coloc/src/main/java/org/apache/cxf/binding/coloc/feature/ColocFeature.java
@@ -25,23 +25,30 @@ import org.apache.cxf.common.injection.NoJSR250Annotations;
 import org.apache.cxf.endpoint.Client;
 import org.apache.cxf.endpoint.ConduitSelector;
 import org.apache.cxf.endpoint.DeferredConduitSelector;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.interceptor.InterceptorProvider;
 
 @NoJSR250Annotations
-public class ColocFeature extends AbstractFeature {
+public class ColocFeature extends DelegatingFeature<ColocFeature.Portable> {
 
-    @Override
-    public void initialize(Client client, Bus bus) {
-        ConduitSelector selector = new DeferredConduitSelector();
-        selector.setEndpoint(client.getEndpoint());
-        client.setConduitSelector(selector);
-        initializeProvider(client, bus);
+    public ColocFeature() {
+        super(new Portable());
     }
 
-    @Override
-    protected void initializeProvider(InterceptorProvider provider, Bus bus) {
-        provider.getInInterceptors().add(new ColocInInterceptor());
-        provider.getOutInterceptors().add(new ColocOutInterceptor(bus));
+    public static class Portable implements AbstractPortableFeature {
+        @Override
+        public void initialize(Client client, Bus bus) {
+            ConduitSelector selector = new DeferredConduitSelector();
+            selector.setEndpoint(client.getEndpoint());
+            client.setConduitSelector(selector);
+            doInitializeProvider(client, bus);
+        }
+
+        @Override
+        public void doInitializeProvider(InterceptorProvider provider, Bus bus) {
+            provider.getInInterceptors().add(new ColocInInterceptor());
+            provider.getOutInterceptors().add(new ColocOutInterceptor(bus));
+        }
     }
 }
diff --git a/rt/features/clustering/src/main/java/org/apache/cxf/clustering/FailoverFeature.java b/rt/features/clustering/src/main/java/org/apache/cxf/clustering/FailoverFeature.java
index 8d95b0f..76f4816 100644
--- a/rt/features/clustering/src/main/java/org/apache/cxf/clustering/FailoverFeature.java
+++ b/rt/features/clustering/src/main/java/org/apache/cxf/clustering/FailoverFeature.java
@@ -28,7 +28,8 @@ import org.apache.cxf.endpoint.Client;
 import org.apache.cxf.endpoint.ConduitSelector;
 import org.apache.cxf.endpoint.ConduitSelectorHolder;
 import org.apache.cxf.endpoint.Endpoint;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.interceptor.InterceptorProvider;
 
 /**
@@ -39,68 +40,107 @@ import org.apache.cxf.interceptor.InterceptorProvider;
 @NoJSR250Annotations
 @EvaluateAllEndpoints
 @Provider(value = Type.Feature, scope = Scope.Client)
-public class FailoverFeature extends AbstractFeature {
-
-    private FailoverStrategy failoverStrategy;
-    private FailoverTargetSelector targetSelector;
-    private String clientBootstrapAddress;
-
+public class FailoverFeature extends DelegatingFeature<FailoverFeature.Portable> {
+    protected FailoverFeature(Portable portable) {
+        super(portable);
+    }
     public FailoverFeature() {
-
+        super(new Portable());
     }
     public FailoverFeature(String clientBootstrapAddress) {
-        this.clientBootstrapAddress = clientBootstrapAddress;
-    }
-
-    @Override
-    protected void initializeProvider(InterceptorProvider provider, Bus bus) {
-        if (provider instanceof ConduitSelectorHolder) {
-            ConduitSelectorHolder csHolder = (ConduitSelectorHolder) provider;
-            Endpoint endpoint = csHolder.getConduitSelector().getEndpoint();
-            ConduitSelector conduitSelector = initTargetSelector(endpoint);
-            csHolder.setConduitSelector(conduitSelector);
-        }
-    }
-
-    @Override
-    public void initialize(Client client, Bus bus) {
-        ConduitSelector selector = initTargetSelector(client.getConduitSelector().getEndpoint());
-        client.setConduitSelector(selector);
+        super(new Portable(clientBootstrapAddress));
     }
 
-    protected ConduitSelector initTargetSelector(Endpoint endpoint) {
-        FailoverTargetSelector selector = getTargetSelector();
-        selector.setEndpoint(endpoint);
-        if (getStrategy() != null) {
-            selector.setStrategy(getStrategy());
-        }
-        return selector;
+    public ConduitSelector initTargetSelector(Endpoint endpoint) {
+        return delegate.initTargetSelector(endpoint);
     }
 
     public FailoverTargetSelector getTargetSelector() {
-        if (this.targetSelector == null) {
-            this.targetSelector = new FailoverTargetSelector(clientBootstrapAddress);
-        }
-        return this.targetSelector;
+        return delegate.getTargetSelector();
     }
 
     public void setTargetSelector(FailoverTargetSelector selector) {
-        this.targetSelector = selector;
+        delegate.setTargetSelector(selector);
     }
 
     public void setStrategy(FailoverStrategy strategy) {
-        failoverStrategy = strategy;
+        delegate.setStrategy(strategy);
     }
 
-    public FailoverStrategy getStrategy()  {
-        return failoverStrategy;
+    public FailoverStrategy getStrategy() {
+        return delegate.getStrategy();
     }
 
     public String getClientBootstrapAddress() {
-        return clientBootstrapAddress;
+        return delegate.getClientBootstrapAddress();
     }
 
     public void setClientBootstrapAddress(String clientBootstrapAddress) {
-        this.clientBootstrapAddress = clientBootstrapAddress;
+        delegate.setClientBootstrapAddress(clientBootstrapAddress);
+    }
+
+    public static class Portable implements AbstractPortableFeature {
+        private FailoverStrategy failoverStrategy;
+        private FailoverTargetSelector targetSelector;
+        private String clientBootstrapAddress;
+
+        public Portable() {
+
+        }
+        public Portable(String clientBootstrapAddress) {
+            this.clientBootstrapAddress = clientBootstrapAddress;
+        }
+
+        @Override
+        public void doInitializeProvider(InterceptorProvider provider, Bus bus) {
+            if (provider instanceof ConduitSelectorHolder) {
+                ConduitSelectorHolder csHolder = (ConduitSelectorHolder) provider;
+                Endpoint endpoint = csHolder.getConduitSelector().getEndpoint();
+                ConduitSelector conduitSelector = initTargetSelector(endpoint);
+                csHolder.setConduitSelector(conduitSelector);
+            }
+        }
+
+        @Override
+        public void initialize(Client client, Bus bus) {
+            ConduitSelector selector = initTargetSelector(client.getConduitSelector().getEndpoint());
+            client.setConduitSelector(selector);
+        }
+
+        protected ConduitSelector initTargetSelector(Endpoint endpoint) {
+            FailoverTargetSelector selector = getTargetSelector();
+            selector.setEndpoint(endpoint);
+            if (getStrategy() != null) {
+                selector.setStrategy(getStrategy());
+            }
+            return selector;
+        }
+
+        public FailoverTargetSelector getTargetSelector() {
+            if (this.targetSelector == null) {
+                this.targetSelector = new FailoverTargetSelector(clientBootstrapAddress);
+            }
+            return this.targetSelector;
+        }
+
+        public void setTargetSelector(FailoverTargetSelector selector) {
+            this.targetSelector = selector;
+        }
+
+        public void setStrategy(FailoverStrategy strategy) {
+            failoverStrategy = strategy;
+        }
+
+        public FailoverStrategy getStrategy()  {
+            return failoverStrategy;
+        }
+
+        public String getClientBootstrapAddress() {
+            return clientBootstrapAddress;
+        }
+
+        public void setClientBootstrapAddress(String clientBootstrapAddress) {
+            this.clientBootstrapAddress = clientBootstrapAddress;
+        }
     }
 }
diff --git a/rt/features/clustering/src/main/java/org/apache/cxf/clustering/LoadDistributorFeature.java b/rt/features/clustering/src/main/java/org/apache/cxf/clustering/LoadDistributorFeature.java
index ade09ac..598f1fb 100644
--- a/rt/features/clustering/src/main/java/org/apache/cxf/clustering/LoadDistributorFeature.java
+++ b/rt/features/clustering/src/main/java/org/apache/cxf/clustering/LoadDistributorFeature.java
@@ -29,16 +29,29 @@ import org.apache.cxf.common.injection.NoJSR250Annotations;
 @NoJSR250Annotations
 public class LoadDistributorFeature extends FailoverFeature {
 
-
     public LoadDistributorFeature() {
-
+        super(new Portable());
     }
     public LoadDistributorFeature(String clientBootstrapAddress) {
-        super(clientBootstrapAddress);
+        super(new FailoverFeature.Portable(clientBootstrapAddress));
     }
 
     @Override
     public FailoverTargetSelector getTargetSelector() {
         return new LoadDistributorTargetSelector(getClientBootstrapAddress());
     }
+
+    public static class Portable extends FailoverFeature.Portable {
+        public Portable() {
+
+        }
+        public Portable(String clientBootstrapAddress) {
+            super(clientBootstrapAddress);
+        }
+
+        @Override
+        public FailoverTargetSelector getTargetSelector() {
+            return new LoadDistributorTargetSelector(getClientBootstrapAddress());
+        }
+    }
 }
diff --git a/rt/features/clustering/src/main/java/org/apache/cxf/clustering/circuitbreaker/CircuitBreakerFailoverFeature.java b/rt/features/clustering/src/main/java/org/apache/cxf/clustering/circuitbreaker/CircuitBreakerFailoverFeature.java
index aedf44c..4c41821 100644
--- a/rt/features/clustering/src/main/java/org/apache/cxf/clustering/circuitbreaker/CircuitBreakerFailoverFeature.java
+++ b/rt/features/clustering/src/main/java/org/apache/cxf/clustering/circuitbreaker/CircuitBreakerFailoverFeature.java
@@ -25,59 +25,106 @@ import org.apache.cxf.clustering.FailoverFeature;
 import org.apache.cxf.clustering.FailoverTargetSelector;
 
 public class CircuitBreakerFailoverFeature extends FailoverFeature {
-    private int threshold;
-    private long timeout;
-    private FailoverTargetSelector targetSelector;
-
     public CircuitBreakerFailoverFeature() {
         this(CircuitBreakerTargetSelector.DEFAULT_THESHOLD,
-             CircuitBreakerTargetSelector.DEFAULT_TIMEOUT);
+                CircuitBreakerTargetSelector.DEFAULT_TIMEOUT);
     }
 
     public CircuitBreakerFailoverFeature(String clientBootstrapAddress) {
         this(CircuitBreakerTargetSelector.DEFAULT_THESHOLD,
-             CircuitBreakerTargetSelector.DEFAULT_TIMEOUT,
-             clientBootstrapAddress);
+                CircuitBreakerTargetSelector.DEFAULT_TIMEOUT,
+                clientBootstrapAddress);
     }
 
     public CircuitBreakerFailoverFeature(int threshold, long timeout) {
-        this.threshold = threshold;
-        this.timeout = timeout;
+        super(new Portable(threshold, timeout));
     }
 
     public CircuitBreakerFailoverFeature(int threshold, long timeout, String clientBootstrapAddress) {
-        super(clientBootstrapAddress);
-        this.threshold = threshold;
-        this.timeout = timeout;
+        super(new Portable(threshold, timeout, clientBootstrapAddress));
     }
 
     @Override
     public FailoverTargetSelector getTargetSelector() {
-        if (this.targetSelector == null) {
-            this.targetSelector = new CircuitBreakerTargetSelector(threshold, timeout,
-                                                                   super.getClientBootstrapAddress());
-        }
-        return this.targetSelector;
+        return delegate.getTargetSelector();
     }
 
     @Override
     public void setTargetSelector(FailoverTargetSelector targetSelector) {
-        this.targetSelector = targetSelector;
+        delegate.setTargetSelector(targetSelector);
     }
 
     public int getThreshold() {
-        return threshold;
+        return Portable.class.cast(delegate).getThreshold();
     }
 
     public long getTimeout() {
-        return timeout;
+        return Portable.class.cast(delegate).getTimeout();
     }
 
     public void setThreshold(int threshold) {
-        this.threshold = threshold;
+        Portable.class.cast(delegate).setThreshold(threshold);
     }
 
     public void setTimeout(long timeout) {
-        this.timeout = timeout;
+        Portable.class.cast(delegate).setTimeout(timeout);
+    }
+
+    public static class Portable extends FailoverFeature.Portable {
+        private int threshold;
+        private long timeout;
+        private FailoverTargetSelector targetSelector;
+
+        public Portable() {
+            this(CircuitBreakerTargetSelector.DEFAULT_THESHOLD,
+                    CircuitBreakerTargetSelector.DEFAULT_TIMEOUT);
+        }
+
+        public Portable(String clientBootstrapAddress) {
+            this(CircuitBreakerTargetSelector.DEFAULT_THESHOLD,
+                    CircuitBreakerTargetSelector.DEFAULT_TIMEOUT,
+                    clientBootstrapAddress);
+        }
+
+        public Portable(int threshold, long timeout) {
+            this.threshold = threshold;
+            this.timeout = timeout;
+        }
+
+        public Portable(int threshold, long timeout, String clientBootstrapAddress) {
+            super(clientBootstrapAddress);
+            this.threshold = threshold;
+            this.timeout = timeout;
+        }
+
+        @Override
+        public FailoverTargetSelector getTargetSelector() {
+            if (this.targetSelector == null) {
+                this.targetSelector = new CircuitBreakerTargetSelector(threshold, timeout,
+                        super.getClientBootstrapAddress());
+            }
+            return this.targetSelector;
+        }
+
+        @Override
+        public void setTargetSelector(FailoverTargetSelector targetSelector) {
+            this.targetSelector = targetSelector;
+        }
+
+        public int getThreshold() {
+            return threshold;
+        }
+
+        public long getTimeout() {
+            return timeout;
+        }
+
+        public void setThreshold(int threshold) {
+            this.threshold = threshold;
+        }
+
+        public void setTimeout(long timeout) {
+            this.timeout = timeout;
+        }
     }
 }
diff --git a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingFeature.java b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingFeature.java
index c0eb36a..25851cc 100644
--- a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingFeature.java
+++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingFeature.java
@@ -26,7 +26,8 @@ import org.apache.cxf.ext.logging.event.LogEventSender;
 import org.apache.cxf.ext.logging.event.PrettyLoggingFilter;
 import org.apache.cxf.ext.logging.slf4j.Slf4jEventSender;
 import org.apache.cxf.ext.logging.slf4j.Slf4jVerboseEventSender;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.interceptor.InterceptorProvider;
 
 /**
@@ -46,121 +47,176 @@ import org.apache.cxf.interceptor.InterceptorProvider;
  */
 @NoJSR250Annotations
 @Provider(value = Type.Feature)
-public class LoggingFeature extends AbstractFeature {
-    private LoggingInInterceptor in;
-    private LoggingOutInterceptor out;
-    private PrettyLoggingFilter inPrettyFilter;
-    private PrettyLoggingFilter outPrettyFilter;
-
+public class LoggingFeature extends DelegatingFeature<LoggingFeature.Portable> {
     public LoggingFeature() {
-        LogEventSender sender = new Slf4jVerboseEventSender();
-        inPrettyFilter = new PrettyLoggingFilter(sender);
-        outPrettyFilter = new PrettyLoggingFilter(sender);
-        in = new LoggingInInterceptor(inPrettyFilter);
-        out = new LoggingOutInterceptor(outPrettyFilter);
-    }
-
-    @Override
-    protected void initializeProvider(InterceptorProvider provider, Bus bus) {
-
-        provider.getInInterceptors().add(in);
-        provider.getInFaultInterceptors().add(in);
-
-        provider.getOutInterceptors().add(out);
-        provider.getOutFaultInterceptors().add(out);
+        super(new Portable());
     }
 
     public void setLimit(int limit) {
-        in.setLimit(limit);
-        out.setLimit(limit);
+        delegate.setLimit(limit);
     }
 
     public void setInMemThreshold(long inMemThreshold) {
-        in.setInMemThreshold(inMemThreshold);
-        out.setInMemThreshold(inMemThreshold);
+        delegate.setInMemThreshold(inMemThreshold);
     }
 
     public void setSender(LogEventSender sender) {
-        this.inPrettyFilter.setNext(sender);
-        this.outPrettyFilter.setNext(sender);
+        delegate.setSender(sender);
     }
+
     public void setInSender(LogEventSender s) {
-        this.inPrettyFilter.setNext(s);
+        delegate.setInSender(s);
     }
+
     public void setOutSender(LogEventSender s) {
-        this.outPrettyFilter.setNext(s);
+        delegate.setOutSender(s);
     }
 
     public void setPrettyLogging(boolean prettyLogging) {
-        this.inPrettyFilter.setPrettyLogging(prettyLogging);
-        this.outPrettyFilter.setPrettyLogging(prettyLogging);
+        delegate.setPrettyLogging(prettyLogging);
     }
 
-    /**
-     * Log binary content?
-     * @param logBinary defaults to false
-     */
     public void setLogBinary(boolean logBinary) {
-        in.setLogBinary(logBinary);
-        out.setLogBinary(logBinary);
+        delegate.setLogBinary(logBinary);
     }
 
-    /**
-     * Log multipart content?
-     * @param logMultipart defaults to true
-     */
     public void setLogMultipart(boolean logMultipart) {
-        in.setLogMultipart(logMultipart);
-        out.setLogMultipart(logMultipart);
+        delegate.setLogMultipart(logMultipart);
     }
 
     public void setVerbose(boolean verbose) {
-        setSender(verbose ? new Slf4jVerboseEventSender() : new Slf4jEventSender());
+        delegate.setVerbose(verbose);
     }
 
-    /**
-     * Add additional binary media types to the default values in the LoggingInInterceptor.
-     * Content for these types will not be logged.
-     * For example:
-     * <pre>
-     * &lt;bean id="loggingFeature" class="org.apache.cxf.ext.logging.LoggingFeature"&gt;
-     *   &lt;property name="addInBinaryContentMediaTypes" value="audio/mpeg;application/zip"/&gt;
-     * &lt;/bean&gt;
-     * </pre>
-     * @param mediaTypes list of mediaTypes. symbol ; - delimeter
-     */
     public void addInBinaryContentMediaTypes(String mediaTypes) {
-        in.addBinaryContentMediaTypes(mediaTypes);
+        delegate.addInBinaryContentMediaTypes(mediaTypes);
     }
 
-    /**
-     * Add additional binary media types to the default values in the LoggingOutInterceptor.
-     * Content for these types will not be logged.
-     * For example:
-     * <pre>
-     * &lt;bean id="loggingFeature" class="org.apache.cxf.ext.logging.LoggingFeature"&gt;
-     *   &lt;property name="addOutBinaryContentMediaTypes" value="audio/mpeg;application/zip"/&gt;
-     * &lt;/bean&gt;
-     * </pre>
-     * @param mediaTypes list of mediaTypes. symbol ; - delimeter
-     */
     public void addOutBinaryContentMediaTypes(String mediaTypes) {
-        out.addBinaryContentMediaTypes(mediaTypes);
+        delegate.addOutBinaryContentMediaTypes(mediaTypes);
     }
 
-    /**
-     * Add additional binary media types to the default values for both logging interceptors
-     * Content for these types will not be logged.
-     * For example:
-     * <pre>
-     * &lt;bean id="loggingFeature" class="org.apache.cxf.ext.logging.LoggingFeature"&gt;
-     *   &lt;property name="addBinaryContentMediaTypes" value="audio/mpeg;application/zip"/&gt;
-     * &lt;/bean&gt;
-     * </pre>
-     * @param mediaTypes list of mediaTypes. symbol ; - delimeter
-     */
     public void addBinaryContentMediaTypes(String mediaTypes) {
-        addInBinaryContentMediaTypes(mediaTypes);
-        addOutBinaryContentMediaTypes(mediaTypes);
+        delegate.addBinaryContentMediaTypes(mediaTypes);
+    }
+
+    @Provider(value = Type.Feature)
+    public static class Portable implements AbstractPortableFeature {
+        private LoggingInInterceptor in;
+        private LoggingOutInterceptor out;
+        private PrettyLoggingFilter inPrettyFilter;
+        private PrettyLoggingFilter outPrettyFilter;
+
+        public Portable() {
+            LogEventSender sender = new Slf4jVerboseEventSender();
+            inPrettyFilter = new PrettyLoggingFilter(sender);
+            outPrettyFilter = new PrettyLoggingFilter(sender);
+            in = new LoggingInInterceptor(inPrettyFilter);
+            out = new LoggingOutInterceptor(outPrettyFilter);
+        }
+
+        @Override
+        public void doInitializeProvider(InterceptorProvider provider, Bus bus) {
+
+            provider.getInInterceptors().add(in);
+            provider.getInFaultInterceptors().add(in);
+
+            provider.getOutInterceptors().add(out);
+            provider.getOutFaultInterceptors().add(out);
+        }
+
+        public void setLimit(int limit) {
+            in.setLimit(limit);
+            out.setLimit(limit);
+        }
+
+        public void setInMemThreshold(long inMemThreshold) {
+            in.setInMemThreshold(inMemThreshold);
+            out.setInMemThreshold(inMemThreshold);
+        }
+
+        public void setSender(LogEventSender sender) {
+            this.inPrettyFilter.setNext(sender);
+            this.outPrettyFilter.setNext(sender);
+        }
+        public void setInSender(LogEventSender s) {
+            this.inPrettyFilter.setNext(s);
+        }
+        public void setOutSender(LogEventSender s) {
+            this.outPrettyFilter.setNext(s);
+        }
+
+        public void setPrettyLogging(boolean prettyLogging) {
+            this.inPrettyFilter.setPrettyLogging(prettyLogging);
+            this.outPrettyFilter.setPrettyLogging(prettyLogging);
+        }
+
+        /**
+         * Log binary content?
+         * @param logBinary defaults to false
+         */
+        public void setLogBinary(boolean logBinary) {
+            in.setLogBinary(logBinary);
+            out.setLogBinary(logBinary);
+        }
+
+        /**
+         * Log multipart content?
+         * @param logMultipart defaults to true
+         */
+        public void setLogMultipart(boolean logMultipart) {
+            in.setLogMultipart(logMultipart);
+            out.setLogMultipart(logMultipart);
+        }
+
+        public void setVerbose(boolean verbose) {
+            setSender(verbose ? new Slf4jVerboseEventSender() : new Slf4jEventSender());
+        }
+
+        /**
+         * Add additional binary media types to the default values in the LoggingInInterceptor.
+         * Content for these types will not be logged.
+         * For example:
+         * <pre>
+         * &lt;bean id="loggingFeature" class="org.apache.cxf.ext.logging.LoggingFeature"&gt;
+         *   &lt;property name="addInBinaryContentMediaTypes" value="audio/mpeg;application/zip"/&gt;
+         * &lt;/bean&gt;
+         * </pre>
+         * @param mediaTypes list of mediaTypes. symbol ; - delimeter
+         */
+        public void addInBinaryContentMediaTypes(String mediaTypes) {
+            in.addBinaryContentMediaTypes(mediaTypes);
+        }
+
+        /**
+         * Add additional binary media types to the default values in the LoggingOutInterceptor.
+         * Content for these types will not be logged.
+         * For example:
+         * <pre>
+         * &lt;bean id="loggingFeature" class="org.apache.cxf.ext.logging.LoggingFeature"&gt;
+         *   &lt;property name="addOutBinaryContentMediaTypes" value="audio/mpeg;application/zip"/&gt;
+         * &lt;/bean&gt;
+         * </pre>
+         * @param mediaTypes list of mediaTypes. symbol ; - delimeter
+         */
+        public void addOutBinaryContentMediaTypes(String mediaTypes) {
+            out.addBinaryContentMediaTypes(mediaTypes);
+        }
+
+        /**
+         * Add additional binary media types to the default values for both logging interceptors
+         * Content for these types will not be logged.
+         * For example:
+         * <pre>
+         * &lt;bean id="loggingFeature" class="org.apache.cxf.ext.logging.LoggingFeature"&gt;
+         *   &lt;property name="addBinaryContentMediaTypes" value="audio/mpeg;application/zip"/&gt;
+         * &lt;/bean&gt;
+         * </pre>
+         * @param mediaTypes list of mediaTypes. symbol ; - delimeter
+         */
+        public void addBinaryContentMediaTypes(String mediaTypes) {
+            addInBinaryContentMediaTypes(mediaTypes);
+            addOutBinaryContentMediaTypes(mediaTypes);
+        }
     }
 }
diff --git a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/MetricsFeature.java b/rt/features/metrics/src/main/java/org/apache/cxf/metrics/MetricsFeature.java
index 958644f..15ab372 100644
--- a/rt/features/metrics/src/main/java/org/apache/cxf/metrics/MetricsFeature.java
+++ b/rt/features/metrics/src/main/java/org/apache/cxf/metrics/MetricsFeature.java
@@ -31,7 +31,8 @@ import org.apache.cxf.configuration.ConfiguredBeanLocator;
 import org.apache.cxf.endpoint.Client;
 import org.apache.cxf.endpoint.Endpoint;
 import org.apache.cxf.endpoint.Server;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.interceptor.InterceptorProvider;
 import org.apache.cxf.metrics.interceptors.CountingOutInterceptor;
 import org.apache.cxf.metrics.interceptors.MetricsMessageClientOutInterceptor;
@@ -46,92 +47,106 @@ import org.apache.cxf.metrics.interceptors.MetricsMessageOutInterceptor;
  */
 @NoJSR250Annotations
 @Provider(Type.Feature)
-public class MetricsFeature extends AbstractFeature {
-    MetricsProvider[] providers;
-
+public class MetricsFeature extends DelegatingFeature<MetricsFeature.Portable> {
     public MetricsFeature() {
-        this.providers = null;
+        super(new Portable());
     }
     public MetricsFeature(MetricsProvider provider) {
-        this.providers = new MetricsProvider[] {provider};
+        super(new Portable(provider));
     }
     public MetricsFeature(MetricsProvider ... providers) {
-        this.providers = providers.length > 0 ? providers : null;
+        super(new Portable(providers));
     }
 
-    @Override
-    public void initialize(Server server, Bus bus) {
-        createDefaultProvidersIfNeeded(bus);
-        //can optimize for server case and just put interceptors it needs
-        Endpoint provider = server.getEndpoint();
-        MetricsMessageOutInterceptor out = new MetricsMessageOutInterceptor(providers);
-        CountingOutInterceptor countingOut = new CountingOutInterceptor();
-
-        provider.getInInterceptors().add(new MetricsMessageInInterceptor(providers));
-        provider.getInInterceptors().add(new MetricsMessageInOneWayInterceptor(providers));
-        provider.getInInterceptors().add(new MetricsMessageInPreInvokeInterceptor(providers));
-
-        provider.getOutInterceptors().add(countingOut);
-        provider.getOutInterceptors().add(out);
-        provider.getOutFaultInterceptors().add(countingOut);
-        provider.getOutFaultInterceptors().add(out);
-    }
+    @Provider(Type.Feature)
+    public static class Portable implements AbstractPortableFeature {
+        MetricsProvider[] providers;
 
-    @Override
-    public void initialize(Client client, Bus bus) {
-        createDefaultProvidersIfNeeded(bus);
-        //can optimize for client case and just put interceptors it needs
-        MetricsMessageOutInterceptor out = new MetricsMessageOutInterceptor(providers);
-        CountingOutInterceptor countingOut = new CountingOutInterceptor();
-
-        client.getInInterceptors().add(new MetricsMessageInInterceptor(providers));
-        client.getInInterceptors().add(new MetricsMessageInPostInvokeInterceptor(providers));
-        client.getInFaultInterceptors().add(new MetricsMessageInPostInvokeInterceptor(providers));
-        client.getOutInterceptors().add(countingOut);
-        client.getOutInterceptors().add(out);
-        client.getOutInterceptors().add(new MetricsMessageClientOutInterceptor(providers));
-    }
+        public Portable() {
+            this.providers = null;
+        }
+        public Portable(MetricsProvider provider) {
+            this.providers = new MetricsProvider[] {provider};
+        }
+        public Portable(MetricsProvider ... providers) {
+            this.providers = providers.length > 0 ? providers : null;
+        }
 
+        @Override
+        public void initialize(Server server, Bus bus) {
+            createDefaultProvidersIfNeeded(bus);
+            //can optimize for server case and just put interceptors it needs
+            Endpoint provider = server.getEndpoint();
+            MetricsMessageOutInterceptor out = new MetricsMessageOutInterceptor(providers);
+            CountingOutInterceptor countingOut = new CountingOutInterceptor();
 
-    @Override
-    protected void initializeProvider(InterceptorProvider provider, Bus bus) {
-        createDefaultProvidersIfNeeded(bus);
-        //if feature is added to the bus, we need to add all the interceptors
-        MetricsMessageOutInterceptor out = new MetricsMessageOutInterceptor(providers);
-        CountingOutInterceptor countingOut = new CountingOutInterceptor();
-
-        provider.getInInterceptors().add(new MetricsMessageInInterceptor(providers));
-        provider.getInInterceptors().add(new MetricsMessageInOneWayInterceptor(providers));
-        provider.getInInterceptors().add(new MetricsMessageInPreInvokeInterceptor(providers));
-        provider.getInInterceptors().add(new MetricsMessageInPostInvokeInterceptor(providers));
-        provider.getInFaultInterceptors().add(new MetricsMessageInPreInvokeInterceptor(providers));
-        provider.getInFaultInterceptors().add(new MetricsMessageInPostInvokeInterceptor(providers));
-
-        provider.getOutInterceptors().add(countingOut);
-        provider.getOutInterceptors().add(out);
-        provider.getOutInterceptors().add(new MetricsMessageClientOutInterceptor(providers));
-        provider.getOutFaultInterceptors().add(countingOut);
-        provider.getOutFaultInterceptors().add(out);
-    }
-    private void createDefaultProvidersIfNeeded(Bus bus) {
-        if (providers == null) {
-            ConfiguredBeanLocator b = bus.getExtension(ConfiguredBeanLocator.class);
-            if (b != null) {
-                Collection<?> coll = b.getBeansOfType(MetricsProvider.class);
-                if (coll != null) {
-                    providers = coll.toArray(new MetricsProvider[]{});
+            provider.getInInterceptors().add(new MetricsMessageInInterceptor(providers));
+            provider.getInInterceptors().add(new MetricsMessageInOneWayInterceptor(providers));
+            provider.getInInterceptors().add(new MetricsMessageInPreInvokeInterceptor(providers));
+
+            provider.getOutInterceptors().add(countingOut);
+            provider.getOutInterceptors().add(out);
+            provider.getOutFaultInterceptors().add(countingOut);
+            provider.getOutFaultInterceptors().add(out);
+        }
+
+        @Override
+        public void initialize(Client client, Bus bus) {
+            createDefaultProvidersIfNeeded(bus);
+            //can optimize for client case and just put interceptors it needs
+            MetricsMessageOutInterceptor out = new MetricsMessageOutInterceptor(providers);
+            CountingOutInterceptor countingOut = new CountingOutInterceptor();
+
+            client.getInInterceptors().add(new MetricsMessageInInterceptor(providers));
+            client.getInInterceptors().add(new MetricsMessageInPostInvokeInterceptor(providers));
+            client.getInFaultInterceptors().add(new MetricsMessageInPostInvokeInterceptor(providers));
+            client.getOutInterceptors().add(countingOut);
+            client.getOutInterceptors().add(out);
+            client.getOutInterceptors().add(new MetricsMessageClientOutInterceptor(providers));
+        }
+
+
+        @Override
+        public void doInitializeProvider(InterceptorProvider provider, Bus bus) {
+            createDefaultProvidersIfNeeded(bus);
+            //if feature is added to the bus, we need to add all the interceptors
+            MetricsMessageOutInterceptor out = new MetricsMessageOutInterceptor(providers);
+            CountingOutInterceptor countingOut = new CountingOutInterceptor();
+
+            provider.getInInterceptors().add(new MetricsMessageInInterceptor(providers));
+            provider.getInInterceptors().add(new MetricsMessageInOneWayInterceptor(providers));
+            provider.getInInterceptors().add(new MetricsMessageInPreInvokeInterceptor(providers));
+            provider.getInInterceptors().add(new MetricsMessageInPostInvokeInterceptor(providers));
+            provider.getInFaultInterceptors().add(new MetricsMessageInPreInvokeInterceptor(providers));
+            provider.getInFaultInterceptors().add(new MetricsMessageInPostInvokeInterceptor(providers));
+
+            provider.getOutInterceptors().add(countingOut);
+            provider.getOutInterceptors().add(out);
+            provider.getOutInterceptors().add(new MetricsMessageClientOutInterceptor(providers));
+            provider.getOutFaultInterceptors().add(countingOut);
+            provider.getOutFaultInterceptors().add(out);
+        }
+        private void createDefaultProvidersIfNeeded(Bus bus) {
+            if (providers == null) {
+                ConfiguredBeanLocator b = bus.getExtension(ConfiguredBeanLocator.class);
+                if (b != null) {
+                    Collection<?> coll = b.getBeansOfType(MetricsProvider.class);
+                    if (coll != null) {
+                        providers = coll.toArray(new MetricsProvider[]{});
+                    }
                 }
             }
-        }
-        if (providers == null) {
-            try {
-                Class<?> cls = ClassLoaderUtils.loadClass("org.apache.cxf.metrics.codahale.CodahaleMetricsProvider",
-                                                        MetricsFeature.class);
-                Constructor<?> c = cls.getConstructor(Bus.class);
-                providers = new MetricsProvider[] {(MetricsProvider)c.newInstance(bus)};
-            } catch (Throwable t) {
-                // ignore;
+            if (providers == null) {
+                try {
+                    Class<?> cls = ClassLoaderUtils.loadClass("org.apache.cxf.metrics.codahale.CodahaleMetricsProvider",
+                            org.apache.cxf.metrics.MetricsFeature.class);
+                    Constructor<?> c = cls.getConstructor(Bus.class);
+                    providers = new MetricsProvider[] {(MetricsProvider)c.newInstance(bus)};
+                } catch (Throwable t) {
+                    // ignore;
+                }
             }
         }
     }
+
 }
diff --git a/rt/features/throttling/src/main/java/org/apache/cxf/throttling/ThrottlingFeature.java b/rt/features/throttling/src/main/java/org/apache/cxf/throttling/ThrottlingFeature.java
index f171b94..7f773f9 100644
--- a/rt/features/throttling/src/main/java/org/apache/cxf/throttling/ThrottlingFeature.java
+++ b/rt/features/throttling/src/main/java/org/apache/cxf/throttling/ThrottlingFeature.java
@@ -20,36 +20,47 @@
 package org.apache.cxf.throttling;
 
 import org.apache.cxf.Bus;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.interceptor.InterceptorProvider;
 
 /**
  *
  */
-public class ThrottlingFeature extends AbstractFeature {
-    final ThrottlingManager manager;
-
+public class ThrottlingFeature extends DelegatingFeature<ThrottlingFeature.Portable> {
     public ThrottlingFeature() {
-        manager = null;
+        super(new Portable());
     }
 
     public ThrottlingFeature(ThrottlingManager manager) {
-        this.manager = manager;
+        super(new Portable(manager));
     }
 
-    @Override
-    protected void initializeProvider(InterceptorProvider provider, Bus bus) {
-        ThrottlingManager m = manager;
-        if (m == null) {
-            m = bus.getExtension(ThrottlingManager.class);
+    public static class Portable implements AbstractPortableFeature {
+        final ThrottlingManager manager;
+
+        public Portable() {
+            manager = null;
         }
-        if (m == null) {
-            throw new IllegalArgumentException("ThrottlingManager must not be null");
+
+        public Portable(ThrottlingManager manager) {
+            this.manager = manager;
         }
-        for (String p : m.getDecisionPhases()) {
-            provider.getInInterceptors().add(new ThrottlingInterceptor(p, m));
+
+        @Override
+        public void doInitializeProvider(InterceptorProvider provider, Bus bus) {
+            ThrottlingManager m = manager;
+            if (m == null) {
+                m = bus.getExtension(ThrottlingManager.class);
+            }
+            if (m == null) {
+                throw new IllegalArgumentException("ThrottlingManager must not be null");
+            }
+            for (String p : m.getDecisionPhases()) {
+                provider.getInInterceptors().add(new ThrottlingInterceptor(p, m));
+            }
+            provider.getOutInterceptors().add(new ThrottlingResponseInterceptor());
+            provider.getOutFaultInterceptors().add(new ThrottlingResponseInterceptor());
         }
-        provider.getOutInterceptors().add(new ThrottlingResponseInterceptor());
-        provider.getOutFaultInterceptors().add(new ThrottlingResponseInterceptor());
     }
 }
diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/validation/JAXRSBeanValidationFeature.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/validation/JAXRSBeanValidationFeature.java
index b939904..49ef1a8 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/validation/JAXRSBeanValidationFeature.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/validation/JAXRSBeanValidationFeature.java
@@ -22,33 +22,50 @@ import org.apache.cxf.Bus;
 import org.apache.cxf.annotations.Provider;
 import org.apache.cxf.annotations.Provider.Scope;
 import org.apache.cxf.annotations.Provider.Type;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.interceptor.InterceptorProvider;
 import org.apache.cxf.validation.BeanValidationInInterceptor;
 import org.apache.cxf.validation.BeanValidationProvider;
 
 @Provider(value = Type.Feature, scope = Scope.Server)
-public class JAXRSBeanValidationFeature extends AbstractFeature {
-
-    private BeanValidationProvider validationProvider;
-    private boolean supportMultipleValidations;
-    @Override
-    protected void initializeProvider(InterceptorProvider interceptorProvider, Bus bus) {
-        BeanValidationInInterceptor in = new JAXRSBeanValidationInInterceptor();
-        JAXRSBeanValidationOutInterceptor out = new JAXRSBeanValidationOutInterceptor();
-        out.setSupportMultipleValidations(supportMultipleValidations);
-        if (validationProvider != null) {
-            in.setProvider(validationProvider);
-            out.setProvider(validationProvider);
-        }
-        interceptorProvider.getInInterceptors().add(in);
-        interceptorProvider.getOutInterceptors().add(out);
+public class JAXRSBeanValidationFeature extends DelegatingFeature<JAXRSBeanValidationFeature.Portable> {
+
+    public JAXRSBeanValidationFeature() {
+        super(new Portable());
     }
 
     public void setProvider(BeanValidationProvider provider) {
-        this.validationProvider = provider;
+        delegate.setProvider(provider);
     }
+
     public void setSupportMultipleValidations(boolean supportMultipleValidations) {
-        this.supportMultipleValidations = supportMultipleValidations;
+        delegate.setSupportMultipleValidations(supportMultipleValidations);
+    }
+
+    @Provider(value = Type.Feature, scope = Scope.Server)
+    public static class Portable implements AbstractPortableFeature {
+
+        private BeanValidationProvider validationProvider;
+        private boolean supportMultipleValidations;
+        @Override
+        public void doInitializeProvider(InterceptorProvider interceptorProvider, Bus bus) {
+            BeanValidationInInterceptor in = new JAXRSBeanValidationInInterceptor();
+            JAXRSBeanValidationOutInterceptor out = new JAXRSBeanValidationOutInterceptor();
+            out.setSupportMultipleValidations(supportMultipleValidations);
+            if (validationProvider != null) {
+                in.setProvider(validationProvider);
+                out.setProvider(validationProvider);
+            }
+            interceptorProvider.getInInterceptors().add(in);
+            interceptorProvider.getOutInterceptors().add(out);
+        }
+
+        public void setProvider(BeanValidationProvider provider) {
+            this.validationProvider = provider;
+        }
+        public void setSupportMultipleValidations(boolean supportMultipleValidations) {
+            this.supportMultipleValidations = supportMultipleValidations;
+        }
     }
 }
diff --git a/rt/javascript/javascript-rt/src/main/java/org/apache/cxf/javascript/JavascriptOptionsFeature.java b/rt/javascript/javascript-rt/src/main/java/org/apache/cxf/javascript/JavascriptOptionsFeature.java
index 9f087fe..f433b21 100644
--- a/rt/javascript/javascript-rt/src/main/java/org/apache/cxf/javascript/JavascriptOptionsFeature.java
+++ b/rt/javascript/javascript-rt/src/main/java/org/apache/cxf/javascript/JavascriptOptionsFeature.java
@@ -24,7 +24,8 @@ import java.util.Map;
 import org.apache.cxf.Bus;
 import org.apache.cxf.common.injection.NoJSR250Annotations;
 import org.apache.cxf.endpoint.Server;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 
 /**
  * This class provides configuration options to the JavaScript client generator.
@@ -42,27 +43,42 @@ import org.apache.cxf.feature.AbstractFeature;
   * At this time, there is no corresponding WSDL extension for this information.
  */
 @NoJSR250Annotations
-public class JavascriptOptionsFeature extends AbstractFeature {
-    private Map<String, String> namespacePrefixMap;
+public class JavascriptOptionsFeature extends DelegatingFeature<JavascriptOptionsFeature.Portable> {
+
+    public JavascriptOptionsFeature() {
+        super(new Portable());
+    }
 
-    /**
-     * Retrieve the map from namespace URI strings to JavaScript function prefixes.
-     * @return the map
-     */
     public Map<String, String> getNamespacePrefixMap() {
-        return namespacePrefixMap;
+        return delegate.getNamespacePrefixMap();
     }
 
-    /**
-     * Set the map from namespace URI strings to Javascript function prefixes.
-     * @param namespacePrefixMap the map from namespace URI strings to JavaScript function prefixes.
-     */
     public void setNamespacePrefixMap(Map<String, String> namespacePrefixMap) {
-        this.namespacePrefixMap = namespacePrefixMap;
+        delegate.setNamespacePrefixMap(namespacePrefixMap);
     }
 
-    @Override
-    public void initialize(Server server, Bus bus) {
-      //  server.getEndpoint().getActiveFeatures().add(this);
+    public static class Portable implements AbstractPortableFeature {
+        private Map<String, String> namespacePrefixMap;
+
+        /**
+         * Retrieve the map from namespace URI strings to JavaScript function prefixes.
+         * @return the map
+         */
+        public Map<String, String> getNamespacePrefixMap() {
+            return namespacePrefixMap;
+        }
+
+        /**
+         * Set the map from namespace URI strings to Javascript function prefixes.
+         * @param namespacePrefixMap the map from namespace URI strings to JavaScript function prefixes.
+         */
+        public void setNamespacePrefixMap(Map<String, String> namespacePrefixMap) {
+            this.namespacePrefixMap = namespacePrefixMap;
+        }
+
+        @Override
+        public void initialize(Server server, Bus bus) {
+            //  server.getEndpoint().getActiveFeatures().add(this);
+        }
     }
 }
diff --git a/rt/management/src/main/java/org/apache/cxf/management/interceptor/ResponseTimeFeature.java b/rt/management/src/main/java/org/apache/cxf/management/interceptor/ResponseTimeFeature.java
index 1e0d262..fc67a0d 100644
--- a/rt/management/src/main/java/org/apache/cxf/management/interceptor/ResponseTimeFeature.java
+++ b/rt/management/src/main/java/org/apache/cxf/management/interceptor/ResponseTimeFeature.java
@@ -20,25 +20,30 @@ package org.apache.cxf.management.interceptor;
 
 import org.apache.cxf.Bus;
 import org.apache.cxf.common.injection.NoJSR250Annotations;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.interceptor.InterceptorProvider;
 
 @NoJSR250Annotations
-public class ResponseTimeFeature extends AbstractFeature {
-    private static final ResponseTimeMessageInInterceptor IN =
-        new ResponseTimeMessageInInterceptor();
-    private static final ResponseTimeMessageInvokerInterceptor INVOKER =
-        new ResponseTimeMessageInvokerInterceptor();
-    private static final ResponseTimeMessageOutInterceptor OUT =
-        new ResponseTimeMessageOutInterceptor();
+public class ResponseTimeFeature extends DelegatingFeature<ResponseTimeFeature.Portable> {
+    public ResponseTimeFeature() {
+        super(new Portable());
+    }
 
-    @Override
-    protected void initializeProvider(InterceptorProvider provider, Bus bus) {
-        provider.getInInterceptors().add(IN);
-        provider.getInFaultInterceptors().add(IN);
-        provider.getInInterceptors().add(INVOKER);
-        provider.getOutInterceptors().add(OUT);
+    public static class Portable implements AbstractPortableFeature {
+        private static final ResponseTimeMessageInInterceptor IN =
+                new ResponseTimeMessageInInterceptor();
+        private static final ResponseTimeMessageInvokerInterceptor INVOKER =
+                new ResponseTimeMessageInvokerInterceptor();
+        private static final ResponseTimeMessageOutInterceptor OUT =
+                new ResponseTimeMessageOutInterceptor();
 
+        @Override
+        public void doInitializeProvider(InterceptorProvider provider, Bus bus) {
+            provider.getInInterceptors().add(IN);
+            provider.getInFaultInterceptors().add(IN);
+            provider.getInInterceptors().add(INVOKER);
+            provider.getOutInterceptors().add(OUT);
+        }
     }
-
 }
diff --git a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/validation/JAXRSClientBeanValidationFeature.java b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/validation/JAXRSClientBeanValidationFeature.java
index 9e45eab..320e88b 100644
--- a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/validation/JAXRSClientBeanValidationFeature.java
+++ b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/validation/JAXRSClientBeanValidationFeature.java
@@ -27,14 +27,24 @@ import org.apache.cxf.validation.ClientBeanValidationFeature;
 
 @Provider(value = Type.Feature, scope = Scope.Client)
 public class JAXRSClientBeanValidationFeature extends ClientBeanValidationFeature {
-    private boolean wrapInProcessingException;
-    @Override
-    protected void initializeProvider(InterceptorProvider interceptorProvider, Bus bus) {
-        JAXRSClientBeanValidationOutInterceptor out = new JAXRSClientBeanValidationOutInterceptor();
-        out.setWrapInProcessingException(wrapInProcessingException);
-        super.addInterceptor(interceptorProvider, out);
+    public JAXRSClientBeanValidationFeature() {
+        super(new Portable());
     }
+
     public void setWrapInProcessingException(boolean wrapInProcessingException) {
-        this.wrapInProcessingException = wrapInProcessingException;
+        Portable.class.cast(getDelegate()).setWrapInProcessingException(wrapInProcessingException);
+    }
+
+    public static class Portable extends ClientBeanValidationFeature.Portable {
+        private boolean wrapInProcessingException;
+        @Override
+        public void doInitializeProvider(InterceptorProvider interceptorProvider, Bus bus) {
+            JAXRSClientBeanValidationOutInterceptor out = new JAXRSClientBeanValidationOutInterceptor();
+            out.setWrapInProcessingException(wrapInProcessingException);
+            super.addInterceptor(interceptorProvider, out);
+        }
+        public void setWrapInProcessingException(boolean wrapInProcessingException) {
+            this.wrapInProcessingException = wrapInProcessingException;
+        }
     }
 }
diff --git a/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/OpenApiFeature.java b/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/OpenApiFeature.java
index 2d8f606..dae1bdb 100644
--- a/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/OpenApiFeature.java
+++ b/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/OpenApiFeature.java
@@ -38,7 +38,8 @@ import org.apache.cxf.annotations.Provider.Type;
 import org.apache.cxf.common.util.PropertyUtils;
 import org.apache.cxf.common.util.StringUtils;
 import org.apache.cxf.endpoint.Server;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.jaxrs.JAXRSServiceFactoryBean;
 import org.apache.cxf.jaxrs.common.openapi.DefaultApplicationFactory;
 import org.apache.cxf.jaxrs.common.openapi.SwaggerProperties;
@@ -61,515 +62,810 @@ import io.swagger.v3.oas.models.info.License;
 import io.swagger.v3.oas.models.security.SecurityScheme;
 
 @Provider(value = Type.Feature, scope = Scope.Server)
-public class OpenApiFeature extends AbstractFeature implements SwaggerUiSupport, SwaggerProperties {
-    private String version;
-    private String title;
-    private String description;
-    private String contactName;
-    private String contactEmail;
-    private String contactUrl;
-    private String license;
-    private String licenseUrl;
-    private String termsOfServiceUrl;
-    // Read all operations also with no @Operation
-    private boolean readAllResources = true; 
-    // Scan all JAX-RS resources automatically
-    private boolean scan = true;
-    private boolean prettyPrint = true;
-    private boolean runAsFilter;
-    private Collection<String> ignoredRoutes;
-    private Set<String> resourcePackages;
-    private Set<String> resourceClasses;
-    private String filterClass;
-
-    private Boolean supportSwaggerUi;
-    private String swaggerUiVersion;
-    private String swaggerUiMavenGroupAndArtifact;
-    private Map<String, String> swaggerUiMediaTypes;
-    
-    // Additional components
-    private Map<String, SecurityScheme> securityDefinitions;
-    private OpenApiCustomizer customizer;
-    
-    // Allows to pass the configuration location, usually openapi-configuration.json
-    // or openapi-configuration.yml file.
-    private String configLocation;
-    // Allows to pass the properties location, by default swagger.properties
-    private String propertiesLocation = DEFAULT_PROPS_LOCATION;
-    // Allows to disable automatic scan of known configuration locations (enabled by default)
-    private boolean scanKnownConfigLocations = true;
-    // Swagger UI configuration parameters (to be passed as query string).
-    private SwaggerUiConfig swaggerUiConfig;
-    // Generates the Swagger Context ID (instead of using the default one). It is
-    // necessary when more than one JAXRS Server Factory Bean or OpenApiFeature instance
-    // are co-located in the same application.
-    private boolean useContextBasedConfig;
-    private String ctxId;
+public class OpenApiFeature extends DelegatingFeature<OpenApiFeature.Portable>
+        implements SwaggerUiSupport, SwaggerProperties {
 
-    @Override
-    public void initialize(Server server, Bus bus) {
-        final JAXRSServiceFactoryBean sfb = (JAXRSServiceFactoryBean)server
-            .getEndpoint()
-            .get(JAXRSServiceFactoryBean.class.getName());
-
-        final ServerProviderFactory factory = (ServerProviderFactory)server
-            .getEndpoint()
-            .get(ServerProviderFactory.class.getName());
-
-        final Set<String> packages = new HashSet<>();
-        if (resourcePackages != null) {
-            packages.addAll(resourcePackages);
-        }
-        
-        // Generate random Context ID for Swagger
-        if (useContextBasedConfig) {
-            ctxId = UUID.randomUUID().toString();
-        }
-
-        Properties swaggerProps = null;
-        GenericOpenApiContextBuilder<?> openApiConfiguration; 
-        final Application application = DefaultApplicationFactory.createApplicationOrDefault(server, factory, 
-            sfb, bus, resourcePackages, isScan());
-        
-        String defaultConfigLocation = getConfigLocation();
-        if (scanKnownConfigLocations && StringUtils.isEmpty(defaultConfigLocation)) {
-            defaultConfigLocation = OpenApiDefaultConfigurationScanner.locateDefaultConfiguration().orElse(null);
-        }
-        
-        if (StringUtils.isEmpty(defaultConfigLocation)) {
-            swaggerProps = getSwaggerProperties(propertiesLocation, bus);
-            
-            if (isScan()) {
-                packages.addAll(scanResourcePackages(sfb));
-            }
-        
-            final OpenAPI oas = new OpenAPI().info(getInfo(swaggerProps));
-            registerComponents(securityDefinitions).ifPresent(oas::setComponents);
-            
-            final SwaggerConfiguration config = new SwaggerConfiguration()
-                .openAPI(oas)
-                .prettyPrint(getOrFallback(isPrettyPrint(), swaggerProps, PRETTY_PRINT_PROPERTY))
-                .readAllResources(isReadAllResources())
-                .ignoredRoutes(getIgnoredRoutes())
-                .filterClass(getOrFallback(getFilterClass(), swaggerProps, FILTER_CLASS_PROPERTY))
-                .resourceClasses(getResourceClasses())
-                .resourcePackages(getOrFallback(packages, swaggerProps, RESOURCE_PACKAGE_PROPERTY));
-
-            openApiConfiguration = new JaxrsOpenApiContextBuilder<>()
-                .application(application)
-                .openApiConfiguration(config)
-                .ctxId(ctxId); /* will be null if not used */
-        } else {
-            openApiConfiguration = new JaxrsOpenApiContextBuilder<>()
-                .application(application)
-                .configLocation(defaultConfigLocation)
-                .ctxId(ctxId); /* will be null if not used */
-        }
-
-        try {
-            final OpenApiContext context = openApiConfiguration.buildContext(true);
-            final Properties userProperties = getUserProperties(
-                context
-                    .getOpenApiConfiguration()
-                    .getUserDefinedOptions());
-            registerOpenApiResources(sfb, packages, context.getOpenApiConfiguration());
-            registerSwaggerUiResources(sfb, combine(swaggerProps, userProperties), factory, bus);
-            
-            if (useContextBasedConfig) {
-                registerServletConfigProvider(factory);
-            }
-            
-            if (customizer != null) {
-                customizer.setApplicationInfo(factory.getApplicationProvider());
-            }
-            
-            bus.setProperty("openapi.service.description.available", "true");
-        } catch (OpenApiConfigurationException ex) {
-            throw new RuntimeException("Unable to initialize OpenAPI context", ex);
-        }
+    public OpenApiFeature() {
+        super(new Portable());
     }
 
     public boolean isScan() {
-        return scan;
+        return delegate.isScan();
     }
 
     public void setScan(boolean scan) {
-        this.scan = scan;
+        delegate.setScan(scan);
     }
 
     public String getFilterClass() {
-        return filterClass;
+        return delegate.getFilterClass();
     }
 
     public void setFilterClass(String filterClass) {
-        this.filterClass = filterClass;
+        delegate.setFilterClass(filterClass);
     }
-    
+
     public Set<String> getResourcePackages() {
-        return resourcePackages;
+        return delegate.getResourcePackages();
     }
-    
+
     public void setResourcePackages(Set<String> resourcePackages) {
-        this.resourcePackages = (resourcePackages == null) ? null : new HashSet<>(resourcePackages);
+        delegate.setResourcePackages(resourcePackages);
     }
 
     public String getVersion() {
-        return version;
+        return delegate.getVersion();
     }
 
     public void setVersion(String version) {
-        this.version = version;
+        delegate.setVersion(version);
     }
 
     public String getTitle() {
-        return title;
+        return delegate.getTitle();
     }
 
     public void setTitle(String title) {
-        this.title = title;
+        delegate.setTitle(title);
     }
 
     public String getDescription() {
-        return description;
+        return delegate.getDescription();
     }
 
     public void setDescription(String description) {
-        this.description = description;
+        delegate.setDescription(description);
     }
 
     public String getContactName() {
-        return contactName;
+        return delegate.getContactName();
     }
 
     public void setContactName(String contactName) {
-        this.contactName = contactName;
+        delegate.setContactName(contactName);
     }
 
     public String getContactEmail() {
-        return contactEmail;
+        return delegate.getContactEmail();
     }
 
     public void setContactEmail(String contactEmail) {
-        this.contactEmail = contactEmail;
+        delegate.setContactEmail(contactEmail);
     }
 
     public String getContactUrl() {
-        return contactUrl;
+        return delegate.getContactUrl();
     }
 
     public void setContactUrl(String contactUrl) {
-        this.contactUrl = contactUrl;
+        delegate.setContactUrl(contactUrl);
     }
 
     public String getLicense() {
-        return license;
+        return delegate.getLicense();
     }
 
     public void setLicense(String license) {
-        this.license = license;
+        delegate.setLicense(license);
     }
 
     public String getLicenseUrl() {
-        return licenseUrl;
+        return delegate.getLicenseUrl();
     }
 
     public void setLicenseUrl(String licenseUrl) {
-        this.licenseUrl = licenseUrl;
+        delegate.setLicenseUrl(licenseUrl);
     }
 
     public String getTermsOfServiceUrl() {
-        return termsOfServiceUrl;
+        return delegate.getTermsOfServiceUrl();
     }
 
     public void setTermsOfServiceUrl(String termsOfServiceUrl) {
-        this.termsOfServiceUrl = termsOfServiceUrl;
+        delegate.setTermsOfServiceUrl(termsOfServiceUrl);
     }
 
     public boolean isReadAllResources() {
-        return readAllResources;
+        return delegate.isReadAllResources();
     }
 
     public void setReadAllResources(boolean readAllResources) {
-        this.readAllResources = readAllResources;
+        delegate.setReadAllResources(readAllResources);
     }
 
     public Set<String> getResourceClasses() {
-        return resourceClasses;
+        return delegate.getResourceClasses();
     }
 
     public void setResourceClasses(Set<String> resourceClasses) {
-        this.resourceClasses = (resourceClasses == null) ? null : new HashSet<>(resourceClasses);
+        delegate.setResourceClasses(resourceClasses);
     }
 
     public Collection<String> getIgnoredRoutes() {
-        return ignoredRoutes;
+        return delegate.getIgnoredRoutes();
     }
 
     public void setIgnoredRoutes(Collection<String> ignoredRoutes) {
-        this.ignoredRoutes = (ignoredRoutes == null) ? null : new HashSet<>(ignoredRoutes);
+        delegate.setIgnoredRoutes(ignoredRoutes);
     }
 
     public boolean isPrettyPrint() {
-        return prettyPrint;
+        return delegate.isPrettyPrint();
     }
 
     public void setPrettyPrint(boolean prettyPrint) {
-        this.prettyPrint = prettyPrint;
+        delegate.setPrettyPrint(prettyPrint);
     }
-    
+
     public boolean isRunAsFilter() {
-        return runAsFilter;
+        return delegate.isRunAsFilter();
     }
-    
+
     @Override
     public Boolean isSupportSwaggerUi() {
-        return supportSwaggerUi;
+        return delegate.isSupportSwaggerUi();
     }
 
     public void setSupportSwaggerUi(Boolean supportSwaggerUi) {
-        this.supportSwaggerUi = supportSwaggerUi;
+        delegate.setSupportSwaggerUi(supportSwaggerUi);
     }
 
     public String getSwaggerUiVersion() {
-        return swaggerUiVersion;
+        return delegate.getSwaggerUiVersion();
     }
 
     public void setSwaggerUiVersion(String swaggerUiVersion) {
-        this.swaggerUiVersion = swaggerUiVersion;
+        delegate.setSwaggerUiVersion(swaggerUiVersion);
     }
 
     public String getSwaggerUiMavenGroupAndArtifact() {
-        return swaggerUiMavenGroupAndArtifact;
+        return delegate.getSwaggerUiMavenGroupAndArtifact();
     }
 
-    public void setSwaggerUiMavenGroupAndArtifact(
-            String swaggerUiMavenGroupAndArtifact) {
-        this.swaggerUiMavenGroupAndArtifact = swaggerUiMavenGroupAndArtifact;
+    public void setSwaggerUiMavenGroupAndArtifact(String swaggerUiMavenGroupAndArtifact) {
+        delegate.setSwaggerUiMavenGroupAndArtifact(swaggerUiMavenGroupAndArtifact);
     }
 
     @Override
     public Map<String, String> getSwaggerUiMediaTypes() {
-        return swaggerUiMediaTypes;
+        return delegate.getSwaggerUiMediaTypes();
     }
 
     public void setSwaggerUiMediaTypes(Map<String, String> swaggerUiMediaTypes) {
-        this.swaggerUiMediaTypes = swaggerUiMediaTypes;
+        delegate.setSwaggerUiMediaTypes(swaggerUiMediaTypes);
     }
 
     public String getConfigLocation() {
-        return configLocation;
+        return delegate.getConfigLocation();
     }
 
     public void setConfigLocation(String configLocation) {
-        this.configLocation = configLocation;
+        delegate.setConfigLocation(configLocation);
     }
 
     public String getPropertiesLocation() {
-        return propertiesLocation;
+        return delegate.getPropertiesLocation();
     }
 
     public void setPropertiesLocation(String propertiesLocation) {
-        this.propertiesLocation = propertiesLocation;
+        delegate.setPropertiesLocation(propertiesLocation);
     }
 
     public void setRunAsFilter(boolean runAsFilter) {
-        this.runAsFilter = runAsFilter;
+        delegate.setRunAsFilter(runAsFilter);
     }
 
     public Map<String, SecurityScheme> getSecurityDefinitions() {
-        return securityDefinitions;
+        return delegate.getSecurityDefinitions();
     }
 
     public void setSecurityDefinitions(Map<String, SecurityScheme> securityDefinitions) {
-        this.securityDefinitions = securityDefinitions;
+        delegate.setSecurityDefinitions(securityDefinitions);
     }
-    
+
     public OpenApiCustomizer getCustomizer() {
-        return customizer;
+        return delegate.getCustomizer();
     }
-    
+
     public void setCustomizer(OpenApiCustomizer customizer) {
-        this.customizer = customizer;
+        delegate.setCustomizer(customizer);
     }
-    
+
     public void setScanKnownConfigLocations(boolean scanKnownConfigLocations) {
-        this.scanKnownConfigLocations = scanKnownConfigLocations;
+        delegate.setScanKnownConfigLocations(scanKnownConfigLocations);
     }
-    
+
     public boolean isScanKnownConfigLocations() {
-        return scanKnownConfigLocations;
+        return delegate.isScanKnownConfigLocations();
     }
-    
-    public void setSwaggerUiConfig(final SwaggerUiConfig swaggerUiConfig) {
-        this.swaggerUiConfig = swaggerUiConfig;
+
+    public void setSwaggerUiConfig(SwaggerUiConfig swaggerUiConfig) {
+        delegate.setSwaggerUiConfig(swaggerUiConfig);
     }
-    
-    public void setUseContextBasedConfig(final boolean useContextBasedConfig) {
-        this.useContextBasedConfig = useContextBasedConfig;
+
+    public void setUseContextBasedConfig(boolean useContextBasedConfig) {
+        delegate.setUseContextBasedConfig(useContextBasedConfig);
     }
-    
+
     public boolean isUseContextBasedConfig() {
-        return useContextBasedConfig;
+        return delegate.isUseContextBasedConfig();
     }
-    
+
     @Override
     public SwaggerUiConfig getSwaggerUiConfig() {
-        return swaggerUiConfig;
+        return delegate.getSwaggerUiConfig();
     }
 
     @Override
     public String findSwaggerUiRoot() {
-        return SwaggerUi.findSwaggerUiRoot(swaggerUiMavenGroupAndArtifact, swaggerUiVersion);
-    }
-    
-    protected Properties getUserProperties(final Map<String, Object> userDefinedOptions) {
-        final Properties properties = new Properties();
-        
-        if (userDefinedOptions != null) {
-            userDefinedOptions
-                .entrySet()
-                .stream()
-                .filter(entry -> entry.getValue() != null)
-                .forEach(entry -> properties.setProperty(entry.getKey(), entry.getValue().toString()));
-        }
-        
-        return properties;
-    }
-
-    protected void registerOpenApiResources(
-            final JAXRSServiceFactoryBean sfb, 
-            final Set<String> packages, 
-            final OpenAPIConfiguration config) {
-
-        if (customizer != null) {
-            customizer.setClassResourceInfos(sfb.getClassResourceInfo());
-        }
-
-        sfb.setResourceClassesFromBeans(Arrays.asList(
-            createOpenApiResource()
-                .openApiConfiguration(config)
-                .configLocation(configLocation)
-                .resourcePackages(packages)));
-    }
-
-    protected void registerServletConfigProvider(ServerProviderFactory factory) {
-        factory.setUserProviders(Arrays.asList(new ServletConfigProvider(ctxId)));
-    }
-
-    protected void registerSwaggerUiResources(JAXRSServiceFactoryBean sfb, Properties properties, 
-            ServerProviderFactory factory, Bus bus) {
-        
-        final Registration swaggerUiRegistration = getSwaggerUi(bus, properties, isRunAsFilter());
-        
-        if (!isRunAsFilter()) {
-            sfb.setResourceClassesFromBeans(swaggerUiRegistration.getResources());
-        } 
-
-        factory.setUserProviders(swaggerUiRegistration.getProviders());
-    }
-
-    /**
-     * The info will be used only if there is no @OpenAPIDefinition annotation is present. 
-     */
-    private Info getInfo(final Properties properties) {
-        final Info info = new Info()
-            .title(getOrFallback(getTitle(), properties, TITLE_PROPERTY))
-            .version(getOrFallback(getVersion(), properties, VERSION_PROPERTY))
-            .description(getOrFallback(getDescription(), properties, DESCRIPTION_PROPERTY))
-            .termsOfService(getOrFallback(getTermsOfServiceUrl(), properties, TERMS_URL_PROPERTY))
-            .contact(new Contact()
-                .name(getOrFallback(getContactName(), properties, CONTACT_PROPERTY))
-                .email(getContactEmail())
-                .url(getContactUrl()))
-            .license(new License()
-                .name(getOrFallback(getLicense(), properties, LICENSE_PROPERTY))
-                .url(getOrFallback(getLicenseUrl(), properties, LICENSE_URL_PROPERTY)));
-        
-        if (info.getLicense().getName() == null) {
-            info.getLicense().setName(DEFAULT_LICENSE_VALUE);
-        }
-        
-        if (info.getLicense().getUrl() == null && DEFAULT_LICENSE_VALUE.equals(info.getLicense().getName())) {
-            info.getLicense().setUrl(DEFAULT_LICENSE_URL);
-        }
-        
-        return info;
-    }
-
-    private String getOrFallback(String value, Properties properties, String property) {
-        if (value == null && properties != null) {
-            return properties.getProperty(property);
-        } else {
-            return value;
-        }
-    }
-
-    private Boolean getOrFallback(Boolean value, Properties properties, String property) {
-        Boolean fallback = value;
-        if (value == null && properties != null) {
-            fallback = PropertyUtils.isTrue(properties.get(PRETTY_PRINT_PROPERTY));
-        }
-        
-        if (fallback == null) {
-            return false;
-        }
-        
-        return fallback;
-    }
-    
-    private Set<String> getOrFallback(Set<String> collection, Properties properties, String property) {
-        if (collection.isEmpty() && properties != null) {
-            final String value = properties.getProperty(property);
-            if (!StringUtils.isEmpty(value)) {
-                collection.add(value);
+        return delegate.findSwaggerUiRoot();
+    }
+
+    public Properties getUserProperties(Map<String, Object> userDefinedOptions) {
+        return delegate.getUserProperties(userDefinedOptions);
+    }
+
+    public void registerOpenApiResources(JAXRSServiceFactoryBean sfb, Set<String> packages,
+                                         OpenAPIConfiguration config) {
+        delegate.registerOpenApiResources(sfb, packages, config);
+    }
+
+    public void registerServletConfigProvider(ServerProviderFactory factory) {
+        delegate.registerServletConfigProvider(factory);
+    }
+
+    public void registerSwaggerUiResources(JAXRSServiceFactoryBean sfb, Properties properties,
+                                           ServerProviderFactory factory, Bus bus) {
+        delegate.registerSwaggerUiResources(sfb, properties, factory, bus);
+    }
+
+    public Info getInfo(Properties properties) {
+        return delegate.getInfo(properties);
+    }
+
+    public String getOrFallback(String value, Properties properties, String property) {
+        return delegate.getOrFallback(value, properties, property);
+    }
+
+    public Boolean getOrFallback(Boolean value, Properties properties, String property) {
+        return delegate.getOrFallback(value, properties, property);
+    }
+
+    public Set<String> getOrFallback(Set<String> collection, Properties properties, String property) {
+        return delegate.getOrFallback(collection, properties, property);
+    }
+
+    public Collection<String> scanResourcePackages(JAXRSServiceFactoryBean sfb) {
+        return delegate.scanResourcePackages(sfb);
+    }
+
+    public static Properties combine(Properties primary, Properties secondary) {
+        return Portable.combine(primary, secondary);
+    }
+
+    public static void setOrReplace(Properties source, Properties destination) {
+        Portable.setOrReplace(source, destination);
+    }
+
+    public static Optional<Components> registerComponents(Map<String, SecurityScheme> securityDefinitions) {
+        return Portable.registerComponents(securityDefinitions);
+    }
+
+    public OpenApiResource createOpenApiResource() {
+        return delegate.createOpenApiResource();
+    }
+
+    @Provider(value = Type.Feature, scope = Scope.Server)
+    public static class Portable implements AbstractPortableFeature, SwaggerUiSupport, SwaggerProperties {
+        private String version;
+        private String title;
+        private String description;
+        private String contactName;
+        private String contactEmail;
+        private String contactUrl;
+        private String license;
+        private String licenseUrl;
+        private String termsOfServiceUrl;
+        // Read all operations also with no @Operation
+        private boolean readAllResources = true;
+        // Scan all JAX-RS resources automatically
+        private boolean scan = true;
+        private boolean prettyPrint = true;
+        private boolean runAsFilter;
+        private Collection<String> ignoredRoutes;
+        private Set<String> resourcePackages;
+        private Set<String> resourceClasses;
+        private String filterClass;
+
+        private Boolean supportSwaggerUi;
+        private String swaggerUiVersion;
+        private String swaggerUiMavenGroupAndArtifact;
+        private Map<String, String> swaggerUiMediaTypes;
+
+        // Additional components
+        private Map<String, SecurityScheme> securityDefinitions;
+        private OpenApiCustomizer customizer;
+
+        // Allows to pass the configuration location, usually openapi-configuration.json
+        // or openapi-configuration.yml file.
+        private String configLocation;
+        // Allows to pass the properties location, by default swagger.properties
+        private String propertiesLocation = DEFAULT_PROPS_LOCATION;
+        // Allows to disable automatic scan of known configuration locations (enabled by default)
+        private boolean scanKnownConfigLocations = true;
+        // Swagger UI configuration parameters (to be passed as query string).
+        private SwaggerUiConfig swaggerUiConfig;
+        // Generates the Swagger Context ID (instead of using the default one). It is
+        // necessary when more than one JAXRS Server Factory Bean or OpenApiFeature instance
+        // are co-located in the same application.
+        private boolean useContextBasedConfig;
+        private String ctxId;
+
+        @Override
+        public void initialize(Server server, Bus bus) {
+            final JAXRSServiceFactoryBean sfb = (JAXRSServiceFactoryBean)server
+                    .getEndpoint()
+                    .get(JAXRSServiceFactoryBean.class.getName());
+
+            final ServerProviderFactory factory = (ServerProviderFactory)server
+                    .getEndpoint()
+                    .get(ServerProviderFactory.class.getName());
+
+            final Set<String> packages = new HashSet<>();
+            if (resourcePackages != null) {
+                packages.addAll(resourcePackages);
+            }
+
+            // Generate random Context ID for Swagger
+            if (useContextBasedConfig) {
+                ctxId = UUID.randomUUID().toString();
+            }
+
+            Properties swaggerProps = null;
+            GenericOpenApiContextBuilder<?> openApiConfiguration;
+            final Application application = DefaultApplicationFactory.createApplicationOrDefault(server, factory,
+                    sfb, bus, resourcePackages, isScan());
+
+            String defaultConfigLocation = getConfigLocation();
+            if (scanKnownConfigLocations && StringUtils.isEmpty(defaultConfigLocation)) {
+                defaultConfigLocation = OpenApiDefaultConfigurationScanner.locateDefaultConfiguration().orElse(null);
+            }
+
+            if (StringUtils.isEmpty(defaultConfigLocation)) {
+                swaggerProps = getSwaggerProperties(propertiesLocation, bus);
+
+                if (isScan()) {
+                    packages.addAll(scanResourcePackages(sfb));
+                }
+
+                final OpenAPI oas = new OpenAPI().info(getInfo(swaggerProps));
+                registerComponents(securityDefinitions).ifPresent(oas::setComponents);
+
+                final SwaggerConfiguration config = new SwaggerConfiguration()
+                        .openAPI(oas)
+                        .prettyPrint(getOrFallback(isPrettyPrint(), swaggerProps, PRETTY_PRINT_PROPERTY))
+                        .readAllResources(isReadAllResources())
+                        .ignoredRoutes(getIgnoredRoutes())
+                        .filterClass(getOrFallback(getFilterClass(), swaggerProps, FILTER_CLASS_PROPERTY))
+                        .resourceClasses(getResourceClasses())
+                        .resourcePackages(getOrFallback(packages, swaggerProps, RESOURCE_PACKAGE_PROPERTY));
+
+                openApiConfiguration = new JaxrsOpenApiContextBuilder<>()
+                        .application(application)
+                        .openApiConfiguration(config)
+                        .ctxId(ctxId); /* will be null if not used */
+            } else {
+                openApiConfiguration = new JaxrsOpenApiContextBuilder<>()
+                        .application(application)
+                        .configLocation(defaultConfigLocation)
+                        .ctxId(ctxId); /* will be null if not used */
+            }
+
+            try {
+                final OpenApiContext context = openApiConfiguration.buildContext(true);
+                final Properties userProperties = getUserProperties(
+                        context
+                                .getOpenApiConfiguration()
+                                .getUserDefinedOptions());
+                registerOpenApiResources(sfb, packages, context.getOpenApiConfiguration());
+                registerSwaggerUiResources(sfb, combine(swaggerProps, userProperties), factory, bus);
+
+                if (useContextBasedConfig) {
+                    registerServletConfigProvider(factory);
+                }
+
+                if (customizer != null) {
+                    customizer.setApplicationInfo(factory.getApplicationProvider());
+                }
+
+                bus.setProperty("openapi.service.description.available", "true");
+            } catch (OpenApiConfigurationException ex) {
+                throw new RuntimeException("Unable to initialize OpenAPI context", ex);
             }
-        } 
-
-        return collection;
-    }
-
-    private Collection<String> scanResourcePackages(JAXRSServiceFactoryBean sfb) {
-        return sfb
-            .getClassResourceInfo()
-            .stream()
-            .map(cri -> cri.getServiceClass().getPackage().getName())
-            .collect(Collectors.toSet());
-    }
-    
-    private static Properties combine(final Properties primary, final Properties secondary) {
-        if (primary == null) {
-            return secondary;
-        } else if (secondary == null) {
-            return primary;
-        } else {
-            final Properties combined = new Properties();
-            setOrReplace(secondary, combined);
-            setOrReplace(primary, combined);
-            return combined;
-        }
-    }
-
-    private static void setOrReplace(final Properties source, final Properties destination) {
-        final Enumeration<?> enumeration = source.propertyNames();
-        while (enumeration.hasMoreElements()) {
-            final String name = (String)enumeration.nextElement(); 
-            destination.setProperty(name, source.getProperty(name));
-        }
-    }
-    
-    private static Optional<Components> registerComponents(Map<String, SecurityScheme> securityDefinitions) {
-        final Components components = new Components();
-    
-        boolean hasComponents = false;
-        if (securityDefinitions != null && !securityDefinitions.isEmpty()) {
-            securityDefinitions.forEach(components::addSecuritySchemes);
-            hasComponents |= true;
-        }
-        
-        return hasComponents ? Optional.of(components) : Optional.empty();
-    }
-    
-    private OpenApiResource createOpenApiResource() {
-        return (customizer == null) ? new OpenApiResource() : new OpenApiCustomizedResource(customizer);
+        }
+
+        public boolean isScan() {
+            return scan;
+        }
+
+        public void setScan(boolean scan) {
+            this.scan = scan;
+        }
+
+        public String getFilterClass() {
+            return filterClass;
+        }
+
+        public void setFilterClass(String filterClass) {
+            this.filterClass = filterClass;
+        }
+
+        public Set<String> getResourcePackages() {
+            return resourcePackages;
+        }
+
+        public void setResourcePackages(Set<String> resourcePackages) {
+            this.resourcePackages = (resourcePackages == null) ? null : new HashSet<>(resourcePackages);
+        }
+
+        public String getVersion() {
+            return version;
+        }
+
+        public void setVersion(String version) {
+            this.version = version;
+        }
+
+        public String getTitle() {
+            return title;
+        }
+
+        public void setTitle(String title) {
+            this.title = title;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public void setDescription(String description) {
+            this.description = description;
+        }
+
+        public String getContactName() {
+            return contactName;
+        }
+
+        public void setContactName(String contactName) {
+            this.contactName = contactName;
+        }
+
+        public String getContactEmail() {
+            return contactEmail;
+        }
+
+        public void setContactEmail(String contactEmail) {
+            this.contactEmail = contactEmail;
+        }
+
+        public String getContactUrl() {
+            return contactUrl;
+        }
+
+        public void setContactUrl(String contactUrl) {
+            this.contactUrl = contactUrl;
+        }
+
+        public String getLicense() {
+            return license;
+        }
+
+        public void setLicense(String license) {
+            this.license = license;
+        }
+
+        public String getLicenseUrl() {
+            return licenseUrl;
+        }
+
+        public void setLicenseUrl(String licenseUrl) {
+            this.licenseUrl = licenseUrl;
+        }
+
+        public String getTermsOfServiceUrl() {
+            return termsOfServiceUrl;
+        }
+
+        public void setTermsOfServiceUrl(String termsOfServiceUrl) {
+            this.termsOfServiceUrl = termsOfServiceUrl;
+        }
+
+        public boolean isReadAllResources() {
+            return readAllResources;
+        }
+
+        public void setReadAllResources(boolean readAllResources) {
+            this.readAllResources = readAllResources;
+        }
+
+        public Set<String> getResourceClasses() {
+            return resourceClasses;
+        }
+
+        public void setResourceClasses(Set<String> resourceClasses) {
+            this.resourceClasses = (resourceClasses == null) ? null : new HashSet<>(resourceClasses);
+        }
+
+        public Collection<String> getIgnoredRoutes() {
+            return ignoredRoutes;
+        }
+
+        public void setIgnoredRoutes(Collection<String> ignoredRoutes) {
+            this.ignoredRoutes = (ignoredRoutes == null) ? null : new HashSet<>(ignoredRoutes);
+        }
+
+        public boolean isPrettyPrint() {
+            return prettyPrint;
+        }
+
+        public void setPrettyPrint(boolean prettyPrint) {
+            this.prettyPrint = prettyPrint;
+        }
+
+        public boolean isRunAsFilter() {
+            return runAsFilter;
+        }
+
+        @Override
+        public Boolean isSupportSwaggerUi() {
+            return supportSwaggerUi;
+        }
+
+        public void setSupportSwaggerUi(Boolean supportSwaggerUi) {
+            this.supportSwaggerUi = supportSwaggerUi;
+        }
+
+        public String getSwaggerUiVersion() {
+            return swaggerUiVersion;
+        }
+
+        public void setSwaggerUiVersion(String swaggerUiVersion) {
+            this.swaggerUiVersion = swaggerUiVersion;
+        }
+
+        public String getSwaggerUiMavenGroupAndArtifact() {
+            return swaggerUiMavenGroupAndArtifact;
+        }
+
+        public void setSwaggerUiMavenGroupAndArtifact(
+                String swaggerUiMavenGroupAndArtifact) {
+            this.swaggerUiMavenGroupAndArtifact = swaggerUiMavenGroupAndArtifact;
+        }
+
+        @Override
+        public Map<String, String> getSwaggerUiMediaTypes() {
+            return swaggerUiMediaTypes;
+        }
+
+        public void setSwaggerUiMediaTypes(Map<String, String> swaggerUiMediaTypes) {
+            this.swaggerUiMediaTypes = swaggerUiMediaTypes;
+        }
+
+        public String getConfigLocation() {
+            return configLocation;
+        }
+
+        public void setConfigLocation(String configLocation) {
+            this.configLocation = configLocation;
+        }
+
+        public String getPropertiesLocation() {
+            return propertiesLocation;
+        }
+
+        public void setPropertiesLocation(String propertiesLocation) {
+            this.propertiesLocation = propertiesLocation;
+        }
+
+        public void setRunAsFilter(boolean runAsFilter) {
+            this.runAsFilter = runAsFilter;
+        }
+
+        public Map<String, SecurityScheme> getSecurityDefinitions() {
+            return securityDefinitions;
+        }
+
+        public void setSecurityDefinitions(Map<String, SecurityScheme> securityDefinitions) {
+            this.securityDefinitions = securityDefinitions;
+        }
+
+        public OpenApiCustomizer getCustomizer() {
+            return customizer;
+        }
+
+        public void setCustomizer(OpenApiCustomizer customizer) {
+            this.customizer = customizer;
+        }
+
+        public void setScanKnownConfigLocations(boolean scanKnownConfigLocations) {
+            this.scanKnownConfigLocations = scanKnownConfigLocations;
+        }
+
+        public boolean isScanKnownConfigLocations() {
+            return scanKnownConfigLocations;
+        }
+
+        public void setSwaggerUiConfig(final SwaggerUiConfig swaggerUiConfig) {
+            this.swaggerUiConfig = swaggerUiConfig;
+        }
+
+        public void setUseContextBasedConfig(final boolean useContextBasedConfig) {
+            this.useContextBasedConfig = useContextBasedConfig;
+        }
+
+        public boolean isUseContextBasedConfig() {
+            return useContextBasedConfig;
+        }
+
+        @Override
+        public SwaggerUiConfig getSwaggerUiConfig() {
+            return swaggerUiConfig;
+        }
+
+        @Override
+        public String findSwaggerUiRoot() {
+            return SwaggerUi.findSwaggerUiRoot(swaggerUiMavenGroupAndArtifact, swaggerUiVersion);
+        }
+
+        protected Properties getUserProperties(final Map<String, Object> userDefinedOptions) {
+            final Properties properties = new Properties();
+
+            if (userDefinedOptions != null) {
+                userDefinedOptions
+                        .entrySet()
+                        .stream()
+                        .filter(entry -> entry.getValue() != null)
+                        .forEach(entry -> properties.setProperty(entry.getKey(), entry.getValue().toString()));
+            }
+
+            return properties;
+        }
+
+        protected void registerOpenApiResources(
+                final JAXRSServiceFactoryBean sfb,
+                final Set<String> packages,
+                final OpenAPIConfiguration config) {
+
+            if (customizer != null) {
+                customizer.setClassResourceInfos(sfb.getClassResourceInfo());
+            }
+
+            sfb.setResourceClassesFromBeans(Arrays.asList(
+                    createOpenApiResource()
+                            .openApiConfiguration(config)
+                            .configLocation(configLocation)
+                            .resourcePackages(packages)));
+        }
+
+        protected void registerServletConfigProvider(ServerProviderFactory factory) {
+            factory.setUserProviders(Arrays.asList(new ServletConfigProvider(ctxId)));
+        }
+
+        protected void registerSwaggerUiResources(JAXRSServiceFactoryBean sfb, Properties properties,
+                                                  ServerProviderFactory factory, Bus bus) {
+
+            final Registration swaggerUiRegistration = getSwaggerUi(bus, properties, isRunAsFilter());
+
+            if (!isRunAsFilter()) {
+                sfb.setResourceClassesFromBeans(swaggerUiRegistration.getResources());
+            }
+
+            factory.setUserProviders(swaggerUiRegistration.getProviders());
+        }
+
+        /**
+         * The info will be used only if there is no @OpenAPIDefinition annotation is present.
+         */
+        private Info getInfo(final Properties properties) {
+            final Info info = new Info()
+                    .title(getOrFallback(getTitle(), properties, TITLE_PROPERTY))
+                    .version(getOrFallback(getVersion(), properties, VERSION_PROPERTY))
+                    .description(getOrFallback(getDescription(), properties, DESCRIPTION_PROPERTY))
+                    .termsOfService(getOrFallback(getTermsOfServiceUrl(), properties, TERMS_URL_PROPERTY))
+                    .contact(new Contact()
+                            .name(getOrFallback(getContactName(), properties, CONTACT_PROPERTY))
+                            .email(getContactEmail())
+                            .url(getContactUrl()))
+                    .license(new License()
+                            .name(getOrFallback(getLicense(), properties, LICENSE_PROPERTY))
+                            .url(getOrFallback(getLicenseUrl(), properties, LICENSE_URL_PROPERTY)));
+
+            if (info.getLicense().getName() == null) {
+                info.getLicense().setName(DEFAULT_LICENSE_VALUE);
+            }
+
+            if (info.getLicense().getUrl() == null && DEFAULT_LICENSE_VALUE.equals(info.getLicense().getName())) {
+                info.getLicense().setUrl(DEFAULT_LICENSE_URL);
+            }
+
+            return info;
+        }
+
+        private String getOrFallback(String value, Properties properties, String property) {
+            if (value == null && properties != null) {
+                return properties.getProperty(property);
+            } else {
+                return value;
+            }
+        }
+
+        private Boolean getOrFallback(Boolean value, Properties properties, String property) {
+            Boolean fallback = value;
+            if (value == null && properties != null) {
+                fallback = PropertyUtils.isTrue(properties.get(PRETTY_PRINT_PROPERTY));
+            }
+
+            if (fallback == null) {
+                return false;
+            }
+
+            return fallback;
+        }
+
+        private Set<String> getOrFallback(Set<String> collection, Properties properties, String property) {
+            if (collection.isEmpty() && properties != null) {
+                final String value = properties.getProperty(property);
+                if (!StringUtils.isEmpty(value)) {
+                    collection.add(value);
+                }
+            }
+
+            return collection;
+        }
+
+        private Collection<String> scanResourcePackages(JAXRSServiceFactoryBean sfb) {
+            return sfb
+                    .getClassResourceInfo()
+                    .stream()
+                    .map(cri -> cri.getServiceClass().getPackage().getName())
+                    .collect(Collectors.toSet());
+        }
+
+        private static Properties combine(final Properties primary, final Properties secondary) {
+            if (primary == null) {
+                return secondary;
+            } else if (secondary == null) {
+                return primary;
+            } else {
+                final Properties combined = new Properties();
+                setOrReplace(secondary, combined);
+                setOrReplace(primary, combined);
+                return combined;
+            }
+        }
+
+        private static void setOrReplace(final Properties source, final Properties destination) {
+            final Enumeration<?> enumeration = source.propertyNames();
+            while (enumeration.hasMoreElements()) {
+                final String name = (String)enumeration.nextElement();
+                destination.setProperty(name, source.getProperty(name));
+            }
+        }
+
+        private static Optional<Components> registerComponents(Map<String, SecurityScheme> securityDefinitions) {
+            final Components components = new Components();
+
+            boolean hasComponents = false;
+            if (securityDefinitions != null && !securityDefinitions.isEmpty()) {
+                securityDefinitions.forEach(components::addSecuritySchemes);
+                hasComponents |= true;
+            }
+
+            return hasComponents ? Optional.of(components) : Optional.empty();
+        }
+
+        private OpenApiResource createOpenApiResource() {
+            return (customizer == null) ? new OpenApiResource() : new OpenApiCustomizedResource(customizer);
+        }
     }
 }
diff --git a/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/AbstractSwaggerFeature.java b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/AbstractSwaggerFeature.java
index 8294a56..c403242 100644
--- a/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/AbstractSwaggerFeature.java
+++ b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/AbstractSwaggerFeature.java
@@ -25,161 +25,284 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.cxf.Bus;
 import org.apache.cxf.common.util.PackageUtils;
 import org.apache.cxf.endpoint.Server;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.jaxrs.JAXRSServiceFactoryBean;
 import org.apache.cxf.jaxrs.model.ClassResourceInfo;
 
-public abstract class AbstractSwaggerFeature extends AbstractFeature {
-
-    private static final boolean SWAGGER_JAXRS_AVAILABLE;
-
-    static {
-        SWAGGER_JAXRS_AVAILABLE = isSwaggerJaxRsAvailable();
+public abstract class AbstractSwaggerFeature<T extends AbstractSwaggerFeature.Portable>
+        extends DelegatingFeature<T> {
+    protected AbstractSwaggerFeature(final T d) {
+        super(d);
     }
 
-    protected boolean licenseWasSet;
-    private boolean runAsFilter;
-    private boolean activateOnlyIfJaxrsSupported;
-    private String resourcePackage;
-    private String version;
-    private String basePath;
-    private String title;
-    private String description;
-    private String contact;
-    private String license;
-    private String licenseUrl;
-    private String termsOfServiceUrl;
-    private String filterClass;
-
-    private static boolean isSwaggerJaxRsAvailable() {
-        try {
-            Class.forName("io.swagger.jaxrs.DefaultParameterExtension");
-            return true;
-        } catch (Throwable ex) {
-            return false;
-        }
+    public static boolean isSwaggerJaxRsAvailable() {
+        return Portable.isSwaggerJaxRsAvailable();
     }
 
-    @Override
-    public void initialize(Server server, Bus bus) {
-        if (!activateOnlyIfJaxrsSupported || SWAGGER_JAXRS_AVAILABLE) {
-            calculateDefaultResourcePackage(server);
-            calculateDefaultBasePath(server);
-            addSwaggerResource(server, bus);
-
-            initializeProvider(server.getEndpoint(), bus);
-            bus.setProperty("swagger.service.description.available", "true");
-        }
+    public void addSwaggerResource(Server server, Bus bus) {
+        getDelegate().addSwaggerResource(server, bus);
     }
 
-    protected abstract void addSwaggerResource(Server server, Bus bus);
-
-    protected abstract void setBasePathByAddress(String address);
-
-    private void calculateDefaultResourcePackage(Server server) {
-        if (!StringUtils.isEmpty(getResourcePackage())) {
-            return;
-        }
-        JAXRSServiceFactoryBean serviceFactoryBean =
-            (JAXRSServiceFactoryBean)server.getEndpoint().get(JAXRSServiceFactoryBean.class.getName());
-        List<ClassResourceInfo> resourceInfos = serviceFactoryBean.getClassResourceInfo();
+    public void setBasePathByAddress(String address) {
+        getDelegate().setBasePathByAddress(address);
+    }
 
-        if (resourceInfos.size() == 1) {
-            setResourcePackage(resourceInfos.get(0).getServiceClass().getPackage().getName());
-        } else {
-            List<Class<?>> serviceClasses = new ArrayList<>(resourceInfos.size());
-            for (ClassResourceInfo cri : resourceInfos) {
-                serviceClasses.add(cri.getServiceClass());
-            }
-            String sharedPackage = PackageUtils.getSharedPackageName(serviceClasses);
-            if (!StringUtils.isEmpty(sharedPackage)) {
-                setResourcePackage(sharedPackage);
-            }
-        }
+    public void calculateDefaultResourcePackage(Server server) {
+        getDelegate().calculateDefaultResourcePackage(server);
     }
 
-    protected void calculateDefaultBasePath(Server server) {
-        if (getBasePath() == null || getBasePath().length() == 0) {
-            String address = server.getEndpoint().getEndpointInfo().getAddress();
-            setBasePathByAddress(address);
-        }
+    public void calculateDefaultBasePath(Server server) {
+        getDelegate().calculateDefaultBasePath(server);
     }
 
     public String getResourcePackage() {
-        return resourcePackage;
+        return getDelegate().getResourcePackage();
     }
+
     public void setResourcePackage(String resourcePackage) {
-        this.resourcePackage = resourcePackage;
+        getDelegate().setResourcePackage(resourcePackage);
     }
+
     public String getVersion() {
-        return version;
+        return getDelegate().getVersion();
     }
+
     public void setVersion(String version) {
-        this.version = version;
+        getDelegate().setVersion(version);
     }
+
     public String getBasePath() {
-        return basePath;
+        return getDelegate().getBasePath();
     }
+
     public void setBasePath(String basePath) {
-        this.basePath = basePath;
+        getDelegate().setBasePath(basePath);
     }
+
     public String getTitle() {
-        return title;
+        return getDelegate().getTitle();
     }
+
     public void setTitle(String title) {
-        this.title = title;
+        getDelegate().setTitle(title);
     }
+
     public String getDescription() {
-        return description;
+        return getDelegate().getDescription();
     }
+
     public void setDescription(String description) {
-        this.description = description;
+        getDelegate().setDescription(description);
     }
+
     public String getContact() {
-        return contact;
+        return getDelegate().getContact();
     }
+
     public void setContact(String contact) {
-        this.contact = contact;
+        getDelegate().setContact(contact);
     }
+
     public String getLicense() {
-        return license;
+        return getDelegate().getLicense();
     }
+
     public void setLicense(String license) {
-        this.licenseWasSet = true;
-        this.license = license;
+        getDelegate().setLicense(license);
     }
+
     public String getLicenseUrl() {
-        return licenseUrl;
+        return getDelegate().getLicenseUrl();
     }
+
     public void setLicenseUrl(String licenseUrl) {
-        this.licenseUrl = licenseUrl;
+        getDelegate().setLicenseUrl(licenseUrl);
     }
+
     public String getTermsOfServiceUrl() {
-        return termsOfServiceUrl;
+        return getDelegate().getTermsOfServiceUrl();
     }
+
     public void setTermsOfServiceUrl(String termsOfServiceUrl) {
-        this.termsOfServiceUrl = termsOfServiceUrl;
+        getDelegate().setTermsOfServiceUrl(termsOfServiceUrl);
     }
+
     public String getFilterClass() {
-        return filterClass;
+        return getDelegate().getFilterClass();
     }
+
     public void setFilterClass(String filterClass) {
-        this.filterClass = filterClass;
+        getDelegate().setFilterClass(filterClass);
     }
 
     public boolean isRunAsFilter() {
-        return runAsFilter;
+        return getDelegate().isRunAsFilter();
     }
+
     public void setRunAsFilter(boolean runAsFilter) {
-        this.runAsFilter = runAsFilter;
+        getDelegate().setRunAsFilter(runAsFilter);
     }
 
     public boolean isActivateOnlyIfJaxrsSupported() {
-        return activateOnlyIfJaxrsSupported;
+        return getDelegate().isActivateOnlyIfJaxrsSupported();
     }
 
     public void setActivateOnlyIfJaxrsSupported(boolean activateOnlyIfJaxrsSupported) {
-        this.activateOnlyIfJaxrsSupported = activateOnlyIfJaxrsSupported;
+        getDelegate().setActivateOnlyIfJaxrsSupported(activateOnlyIfJaxrsSupported);
+    }
+
+    public abstract static class Portable implements AbstractPortableFeature {
+        private static final boolean SWAGGER_JAXRS_AVAILABLE;
+
+        static {
+            SWAGGER_JAXRS_AVAILABLE = isSwaggerJaxRsAvailable();
+        }
+
+        protected boolean licenseWasSet;
+        private boolean runAsFilter;
+        private boolean activateOnlyIfJaxrsSupported;
+        private String resourcePackage;
+        private String version;
+        private String basePath;
+        private String title;
+        private String description;
+        private String contact;
+        private String license;
+        private String licenseUrl;
+        private String termsOfServiceUrl;
+        private String filterClass;
+
+        private static boolean isSwaggerJaxRsAvailable() {
+            try {
+                Class.forName("io.swagger.jaxrs.DefaultParameterExtension");
+                return true;
+            } catch (Throwable ex) {
+                return false;
+            }
+        }
+
+        @Override
+        public void initialize(Server server, Bus bus) {
+            if (!activateOnlyIfJaxrsSupported || SWAGGER_JAXRS_AVAILABLE) {
+                calculateDefaultResourcePackage(server);
+                calculateDefaultBasePath(server);
+                addSwaggerResource(server, bus);
+
+                doInitializeProvider(server.getEndpoint(), bus);
+                bus.setProperty("swagger.service.description.available", "true");
+            }
+        }
+
+        protected abstract void addSwaggerResource(Server server, Bus bus);
+
+        protected abstract void setBasePathByAddress(String address);
+
+        void calculateDefaultResourcePackage(Server server) {
+            if (!StringUtils.isEmpty(getResourcePackage())) {
+                return;
+            }
+            JAXRSServiceFactoryBean serviceFactoryBean =
+                    (JAXRSServiceFactoryBean)server.getEndpoint().get(JAXRSServiceFactoryBean.class.getName());
+            List<ClassResourceInfo> resourceInfos = serviceFactoryBean.getClassResourceInfo();
+
+            if (resourceInfos.size() == 1) {
+                setResourcePackage(resourceInfos.get(0).getServiceClass().getPackage().getName());
+            } else {
+                List<Class<?>> serviceClasses = new ArrayList<>(resourceInfos.size());
+                for (ClassResourceInfo cri : resourceInfos) {
+                    serviceClasses.add(cri.getServiceClass());
+                }
+                String sharedPackage = PackageUtils.getSharedPackageName(serviceClasses);
+                if (!StringUtils.isEmpty(sharedPackage)) {
+                    setResourcePackage(sharedPackage);
+                }
+            }
+        }
+
+        protected void calculateDefaultBasePath(Server server) {
+            if (getBasePath() == null || getBasePath().length() == 0) {
+                String address = server.getEndpoint().getEndpointInfo().getAddress();
+                setBasePathByAddress(address);
+            }
+        }
+
+        public String getResourcePackage() {
+            return resourcePackage;
+        }
+        public void setResourcePackage(String resourcePackage) {
+            this.resourcePackage = resourcePackage;
+        }
+        public String getVersion() {
+            return version;
+        }
+        public void setVersion(String version) {
+            this.version = version;
+        }
+        public String getBasePath() {
+            return basePath;
+        }
+        public void setBasePath(String basePath) {
+            this.basePath = basePath;
+        }
+        public String getTitle() {
+            return title;
+        }
+        public void setTitle(String title) {
+            this.title = title;
+        }
+        public String getDescription() {
+            return description;
+        }
+        public void setDescription(String description) {
+            this.description = description;
+        }
+        public String getContact() {
+            return contact;
+        }
+        public void setContact(String contact) {
+            this.contact = contact;
+        }
+        public String getLicense() {
+            return license;
+        }
+        public void setLicense(String license) {
+            this.licenseWasSet = true;
+            this.license = license;
+        }
+        public String getLicenseUrl() {
+            return licenseUrl;
+        }
+        public void setLicenseUrl(String licenseUrl) {
+            this.licenseUrl = licenseUrl;
+        }
+        public String getTermsOfServiceUrl() {
+            return termsOfServiceUrl;
+        }
+        public void setTermsOfServiceUrl(String termsOfServiceUrl) {
+            this.termsOfServiceUrl = termsOfServiceUrl;
+        }
+        public String getFilterClass() {
+            return filterClass;
+        }
+        public void setFilterClass(String filterClass) {
+            this.filterClass = filterClass;
+        }
+
+        public boolean isRunAsFilter() {
+            return runAsFilter;
+        }
+        public void setRunAsFilter(boolean runAsFilter) {
+            this.runAsFilter = runAsFilter;
+        }
+
+        public boolean isActivateOnlyIfJaxrsSupported() {
+            return activateOnlyIfJaxrsSupported;
+        }
+
+        public void setActivateOnlyIfJaxrsSupported(boolean activateOnlyIfJaxrsSupported) {
+            this.activateOnlyIfJaxrsSupported = activateOnlyIfJaxrsSupported;
+        }
     }
 
 }
diff --git a/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Feature.java b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Feature.java
index 8e9855d..46b5ead 100644
--- a/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Feature.java
+++ b/rt/rs/description-swagger/src/main/java/org/apache/cxf/jaxrs/swagger/Swagger2Feature.java
@@ -70,377 +70,537 @@ import io.swagger.models.Swagger;
 import io.swagger.models.auth.SecuritySchemeDefinition;
 
 @Provider(value = Type.Feature, scope = Scope.Server)
-public class Swagger2Feature extends AbstractSwaggerFeature implements SwaggerUiSupport, SwaggerProperties {
-    private static final String SCHEMES_PROPERTY = "schemes";
-    private static final String HOST_PROPERTY = "host";
-    private static final String USE_PATH_CFG_PROPERTY = "use.path.based.config";
-
-    private boolean scan;
-    private boolean scanAllResources;
-
-    private String ignoreRoutes;
-
-    private Boolean supportSwaggerUi;
-
-    private String swaggerUiVersion;
-
-    private String swaggerUiMavenGroupAndArtifact;
-
-    private Map<String, String> swaggerUiMediaTypes;
-
-    private boolean dynamicBasePath;
-
-    private Map<String, SecuritySchemeDefinition> securityDefinitions;
-
-    private Swagger2Customizer customizer;
-
-    private String host;
-    private String[] schemes;
-    private Boolean prettyPrint;
-    private Boolean usePathBasedConfig;
-
-    private String propertiesLocation = DEFAULT_PROPS_LOCATION;
-    // Swagger UI configuration parameters (to be passed as query string).
-    private SwaggerUiConfig swaggerUiConfig;
+public class Swagger2Feature extends AbstractSwaggerFeature<Swagger2Feature.Portable>
+        implements SwaggerUiSupport, SwaggerProperties {
+    public Swagger2Feature() {
+        super(new Portable());
+    }
 
     @Override
-    protected void calculateDefaultBasePath(Server server) {
-        dynamicBasePath = true;
-        super.calculateDefaultBasePath(server);
+    protected Portable getDelegate() {
+        return delegate;
     }
 
     @Override
-    protected void addSwaggerResource(Server server, Bus bus) {
-        JAXRSServiceFactoryBean sfb =
-            (JAXRSServiceFactoryBean) server.getEndpoint().get(JAXRSServiceFactoryBean.class.getName());
-
-        ServerProviderFactory factory =
-            (ServerProviderFactory)server.getEndpoint().get(ServerProviderFactory.class.getName());
-        final ApplicationInfo appInfo = DefaultApplicationFactory.createApplicationInfoOrDefault(server, 
-            factory, sfb, bus, isScan());
-
-        List<Object> swaggerResources = new LinkedList<>();
-
-        if (customizer == null) {
-            customizer = new Swagger2Customizer();
-        }
-        ApiListingResource apiListingResource = new Swagger2ApiListingResource(customizer);
-        swaggerResources.add(apiListingResource);
-
-        List<Object> providers = new ArrayList<>();
-        providers.add(new SwaggerSerializers());
-
-        if (isRunAsFilter()) {
-            providers.add(new SwaggerContainerRequestFilter(appInfo == null ? null : appInfo.getProvider(),
-                                                            customizer));
-        }
-
-        final Properties swaggerProps = getSwaggerProperties(propertiesLocation, bus);
-        final Registration swaggerUiRegistration = getSwaggerUi(bus, swaggerProps, isRunAsFilter());
-
-        if (!isRunAsFilter()) {
-            swaggerResources.addAll(swaggerUiRegistration.getResources());
-        }
-
-        providers.addAll(swaggerUiRegistration.getProviders());
-        sfb.setResourceClassesFromBeans(swaggerResources);
-
-        List<ClassResourceInfo> cris = sfb.getClassResourceInfo();
-        if (!isRunAsFilter()) {
-            for (ClassResourceInfo cri : cris) {
-                if (ApiListingResource.class.isAssignableFrom(cri.getResourceClass())) {
-                    InjectionUtils.injectContextProxies(cri, apiListingResource);
-                }
-            }
-        }
-        customizer.setClassResourceInfos(cris);
-        customizer.setDynamicBasePath(dynamicBasePath);
-
-        BeanConfig beanConfig = appInfo == null
-            ? new BeanConfig()
-            : new ApplicationBeanConfig(appInfo.getProvider());
-        initBeanConfig(beanConfig, swaggerProps);
-
-        Swagger swagger = beanConfig.getSwagger();
-        if (swagger != null && securityDefinitions != null) {
-            swagger.setSecurityDefinitions(securityDefinitions);
-        }
-        customizer.setBeanConfig(beanConfig);
-
-        providers.add(new ReaderConfigFilter());
-
-        if (beanConfig.isUsePathBasedConfig()) {
-            providers.add(new ServletConfigProvider());
-        }
-
-        factory.setUserProviders(providers);
+    public void calculateDefaultBasePath(Server server) {
+        delegate.calculateDefaultBasePath(server);
     }
 
-    protected void initBeanConfig(BeanConfig beanConfig, Properties props) {
-
-        // resource package
-        String theResourcePackage = getResourcePackage();
-        if (theResourcePackage == null && props != null) {
-            theResourcePackage = props.getProperty(RESOURCE_PACKAGE_PROPERTY);
-        }
-        beanConfig.setResourcePackage(theResourcePackage);
-
-        // use path based configuration
-        Boolean theUsePathBasedConfig = isUsePathBasedConfig();
-        if (theUsePathBasedConfig == null && props != null) {
-            theUsePathBasedConfig = PropertyUtils.isTrue(props.get(USE_PATH_CFG_PROPERTY));
-        }
-        if (theUsePathBasedConfig == null) {
-            theUsePathBasedConfig = false;
-        }
-        beanConfig.setUsePathBasedConfig(theUsePathBasedConfig);
-
-        // version
-        String theVersion = getVersion();
-        if (theVersion == null && props != null) {
-            theVersion = props.getProperty(VERSION_PROPERTY);
-        }
-        beanConfig.setVersion(theVersion);
-
-        // host
-        String theHost = getHost();
-        if (theHost == null && props != null) {
-            theHost = props.getProperty(HOST_PROPERTY);
-        }
-        beanConfig.setHost(theHost);
-
-        // schemes
-        String[] theSchemes = getSchemes();
-        if (theSchemes == null && props != null && props.containsKey(SCHEMES_PROPERTY)) {
-            theSchemes = props.getProperty(SCHEMES_PROPERTY).split(",");
-        }
-        beanConfig.setSchemes(theSchemes);
-
-        // title
-        String theTitle = getTitle();
-        if (theTitle == null && props != null) {
-            theTitle = props.getProperty(TITLE_PROPERTY);
-        }
-        beanConfig.setTitle(theTitle);
-
-        // description
-        String theDescription = getDescription();
-        if (theDescription == null && props != null) {
-            theDescription = props.getProperty(DESCRIPTION_PROPERTY);
-        }
-        beanConfig.setDescription(theDescription);
-
-        // contact
-        String theContact = getContact();
-        if (theContact == null && props != null) {
-            theContact = props.getProperty(CONTACT_PROPERTY);
-        }
-        beanConfig.setContact(theContact);
-
-        // license
-        String theLicense = getLicense();
-        if (theLicense == null && !licenseWasSet) {
-            if (props != null) {
-                theLicense = props.getProperty(LICENSE_PROPERTY);
-                if (theLicense != null && theLicense.isEmpty()) {
-                    theLicense = null;
-                }
-            } else {
-                theLicense = DEFAULT_LICENSE_VALUE;
-            }
-        }
-        beanConfig.setLicense(theLicense);
-
-        // license url
-        String theLicenseUrl = getLicenseUrl();
-        if (theLicenseUrl == null && props != null) {
-            theLicenseUrl = props.getProperty(LICENSE_URL_PROPERTY);
-        }
-        if (theLicenseUrl == null && DEFAULT_LICENSE_VALUE.equals(theLicense)) {
-            theLicenseUrl = DEFAULT_LICENSE_URL;
-        }
-        beanConfig.setLicenseUrl(theLicenseUrl);
-
-        // terms of service url
-        String theTermsUrl = getTermsOfServiceUrl();
-        if (theTermsUrl == null && props != null) {
-            theTermsUrl = props.getProperty(TERMS_URL_PROPERTY);
-        }
-        beanConfig.setTermsOfServiceUrl(theTermsUrl);
-
-        // pretty print
-        Boolean thePrettyPrint = isPrettyPrint();
-        if (thePrettyPrint == null && props != null) {
-            thePrettyPrint = PropertyUtils.isTrue(props.get(PRETTY_PRINT_PROPERTY));
-        }
-        if (thePrettyPrint == null) {
-            thePrettyPrint = false;
-        }
-        beanConfig.setPrettyPrint(thePrettyPrint);
-
-        // filter class
-        String theFilterClass = getFilterClass();
-        if (theFilterClass == null && props != null) {
-            theFilterClass = props.getProperty(FILTER_CLASS_PROPERTY);
-        }
-        beanConfig.setFilterClass(theFilterClass);
-
-        // scan
-        beanConfig.setScan(isScan());
-
-        // base path is calculated dynamically
-        beanConfig.setBasePath(getBasePath());
+    @Override
+    public void addSwaggerResource(Server server, Bus bus) {
+        delegate.addSwaggerResource(server, bus);
+    }
 
+    public void initBeanConfig(BeanConfig beanConfig, Properties props) {
+        delegate.initBeanConfig(beanConfig, props);
     }
 
     public Boolean isUsePathBasedConfig() {
-        return usePathBasedConfig;
+        return delegate.isUsePathBasedConfig();
     }
 
     public void setUsePathBasedConfig(Boolean usePathBasedConfig) {
-        this.usePathBasedConfig = usePathBasedConfig;
+        delegate.setUsePathBasedConfig(usePathBasedConfig);
     }
 
     public String getHost() {
-        return host;
+        return delegate.getHost();
     }
 
     public void setHost(String host) {
-        this.host = host;
+        delegate.setHost(host);
     }
 
     public String[] getSchemes() {
-        return schemes;
+        return delegate.getSchemes();
     }
 
     public void setSchemes(String[] schemes) {
-        this.schemes = schemes;
+        delegate.setSchemes(schemes);
     }
 
     public Boolean isPrettyPrint() {
-        return prettyPrint;
+        return delegate.isPrettyPrint();
     }
 
     public void setPrettyPrint(Boolean prettyPrint) {
-        this.prettyPrint = prettyPrint;
+        delegate.setPrettyPrint(prettyPrint);
     }
 
     public Swagger2Customizer getCustomizer() {
-        return customizer;
+        return delegate.getCustomizer();
     }
 
     public void setCustomizer(Swagger2Customizer customizer) {
-        this.customizer = customizer;
+        delegate.setCustomizer(customizer);
     }
 
     public boolean isScanAllResources() {
-        return scanAllResources;
+        return delegate.isScanAllResources();
     }
 
     public void setScanAllResources(boolean scanAllResources) {
-        this.scanAllResources = scanAllResources;
+        delegate.setScanAllResources(scanAllResources);
     }
 
     public String getIgnoreRoutes() {
-        return ignoreRoutes;
+        return delegate.getIgnoreRoutes();
     }
 
     public void setIgnoreRoutes(String ignoreRoutes) {
-        this.ignoreRoutes = ignoreRoutes;
+        delegate.setIgnoreRoutes(ignoreRoutes);
     }
 
     @Override
-    protected void setBasePathByAddress(String address) {
-        if (!address.startsWith("/")) {
-            // get the path part
-            URI u = URI.create(address);
-            setBasePath(u.getPath());
-            if (getHost() == null) {
-                setHost(u.getPort() < 0 ? u.getHost() : u.getHost() + ":" + u.getPort());
-            }
-        } else {
-            setBasePath(address);
-        }
+    public void setBasePathByAddress(String address) {
+        delegate.setBasePathByAddress(address);
     }
 
-    /**
-     * Set SwaggerUI Maven group and artifact using the "groupId/artifactId" format.
-     * @param swaggerUiMavenGroupAndArtifact
-     */
     public void setSwaggerUiMavenGroupAndArtifact(String swaggerUiMavenGroupAndArtifact) {
-        this.swaggerUiMavenGroupAndArtifact = swaggerUiMavenGroupAndArtifact;
+        delegate.setSwaggerUiMavenGroupAndArtifact(swaggerUiMavenGroupAndArtifact);
     }
 
     public void setSwaggerUiVersion(String swaggerUiVersion) {
-        this.swaggerUiVersion = swaggerUiVersion;
+        delegate.setSwaggerUiVersion(swaggerUiVersion);
     }
 
     public void setSupportSwaggerUi(boolean supportSwaggerUi) {
-        this.supportSwaggerUi = supportSwaggerUi;
+        delegate.setSupportSwaggerUi(supportSwaggerUi);
     }
 
     @Override
     public Boolean isSupportSwaggerUi() {
-        return supportSwaggerUi;
+        return delegate.isSupportSwaggerUi();
     }
 
     public void setSwaggerUiMediaTypes(Map<String, String> swaggerUiMediaTypes) {
-        this.swaggerUiMediaTypes = swaggerUiMediaTypes;
+        delegate.setSwaggerUiMediaTypes(swaggerUiMediaTypes);
     }
 
     @Override
     public Map<String, String> getSwaggerUiMediaTypes() {
-        return swaggerUiMediaTypes;
+        return delegate.getSwaggerUiMediaTypes();
     }
 
     public void setSecurityDefinitions(Map<String, SecuritySchemeDefinition> securityDefinitions) {
-        this.securityDefinitions = securityDefinitions;
+        delegate.setSecurityDefinitions(securityDefinitions);
     }
 
     public String getPropertiesLocation() {
-        return propertiesLocation;
+        return delegate.getPropertiesLocation();
     }
 
     public void setPropertiesLocation(String propertiesLocation) {
-        this.propertiesLocation = propertiesLocation;
+        delegate.setPropertiesLocation(propertiesLocation);
     }
 
     public boolean isScan() {
-        return scan;
+        return delegate.isScan();
     }
 
     public void setScan(boolean scan) {
-        this.scan = scan;
+        delegate.setScan(scan);
     }
-    
-    public void setSwaggerUiConfig(final SwaggerUiConfig swaggerUiConfig) {
-        this.swaggerUiConfig = swaggerUiConfig;
+
+    public void setSwaggerUiConfig(SwaggerUiConfig swaggerUiConfig) {
+        delegate.setSwaggerUiConfig(swaggerUiConfig);
     }
-    
+
     @Override
     public SwaggerUiConfig getSwaggerUiConfig() {
-        return swaggerUiConfig;
+        return delegate.getSwaggerUiConfig();
     }
 
     @Override
     public String findSwaggerUiRoot() {
-        return SwaggerUi.findSwaggerUiRoot(swaggerUiMavenGroupAndArtifact, swaggerUiVersion);
+        return delegate.findSwaggerUiRoot();
     }
 
-    private class ServletConfigProvider implements ContextProvider<ServletConfig> {
+    @Provider(value = Type.Feature, scope = Scope.Server)
+    public static class Portable extends AbstractSwaggerFeature.Portable
+            implements SwaggerUiSupport, SwaggerProperties {
+        private static final String SCHEMES_PROPERTY = "schemes";
+        private static final String HOST_PROPERTY = "host";
+        private static final String USE_PATH_CFG_PROPERTY = "use.path.based.config";
+
+        private boolean scan;
+        private boolean scanAllResources;
+
+        private String ignoreRoutes;
+
+        private Boolean supportSwaggerUi;
+
+        private String swaggerUiVersion;
+
+        private String swaggerUiMavenGroupAndArtifact;
+
+        private Map<String, String> swaggerUiMediaTypes;
+
+        private boolean dynamicBasePath;
+
+        private Map<String, SecuritySchemeDefinition> securityDefinitions;
+
+        private Swagger2Customizer customizer;
+
+        private String host;
+        private String[] schemes;
+        private Boolean prettyPrint;
+        private Boolean usePathBasedConfig;
+
+        private String propertiesLocation = DEFAULT_PROPS_LOCATION;
+        // Swagger UI configuration parameters (to be passed as query string).
+        private SwaggerUiConfig swaggerUiConfig;
+
+        @Override
+        protected void calculateDefaultBasePath(Server server) {
+            dynamicBasePath = true;
+            super.calculateDefaultBasePath(server);
+        }
 
         @Override
-        public ServletConfig createContext(Message message) {
-            final ServletConfig sc = (ServletConfig)message.get("HTTP.CONFIG");
-
-            // When deploying into OSGi container, it is possible to use embedded Jetty
-            // transport. In this case, the ServletConfig is not available and Swagger
-            // does not take into account certain configuration parameters. To overcome
-            // that, the ServletConfig is synthesized from ServletContext instance.
-            if (sc == null) {
-                final ServletContext context = (ServletContext)message.get("HTTP.CONTEXT");
-                if (context != null) {
-                    return new SyntheticServletConfig(context) {
+        protected void addSwaggerResource(Server server, Bus bus) {
+            JAXRSServiceFactoryBean sfb =
+                    (JAXRSServiceFactoryBean) server.getEndpoint().get(JAXRSServiceFactoryBean.class.getName());
+
+            ServerProviderFactory factory =
+                    (ServerProviderFactory)server.getEndpoint().get(ServerProviderFactory.class.getName());
+            final ApplicationInfo appInfo = DefaultApplicationFactory.createApplicationInfoOrDefault(server,
+                    factory, sfb, bus, isScan());
+
+            List<Object> swaggerResources = new LinkedList<>();
+
+            if (customizer == null) {
+                customizer = new Swagger2Customizer();
+            }
+            ApiListingResource apiListingResource = new Swagger2ApiListingResource(customizer);
+            swaggerResources.add(apiListingResource);
+
+            List<Object> providers = new ArrayList<>();
+            providers.add(new SwaggerSerializers());
+
+            if (isRunAsFilter()) {
+                providers.add(new SwaggerContainerRequestFilter(appInfo == null ? null : appInfo.getProvider(),
+                        customizer));
+            }
+
+            final Properties swaggerProps = getSwaggerProperties(propertiesLocation, bus);
+            final Registration swaggerUiRegistration = getSwaggerUi(bus, swaggerProps, isRunAsFilter());
+
+            if (!isRunAsFilter()) {
+                swaggerResources.addAll(swaggerUiRegistration.getResources());
+            }
+
+            providers.addAll(swaggerUiRegistration.getProviders());
+            sfb.setResourceClassesFromBeans(swaggerResources);
+
+            List<ClassResourceInfo> cris = sfb.getClassResourceInfo();
+            if (!isRunAsFilter()) {
+                for (ClassResourceInfo cri : cris) {
+                    if (ApiListingResource.class.isAssignableFrom(cri.getResourceClass())) {
+                        InjectionUtils.injectContextProxies(cri, apiListingResource);
+                    }
+                }
+            }
+            customizer.setClassResourceInfos(cris);
+            customizer.setDynamicBasePath(dynamicBasePath);
+
+            BeanConfig beanConfig = appInfo == null
+                    ? new BeanConfig()
+                    : new ApplicationBeanConfig(appInfo.getProvider());
+            initBeanConfig(beanConfig, swaggerProps);
+
+            Swagger swagger = beanConfig.getSwagger();
+            if (swagger != null && securityDefinitions != null) {
+                swagger.setSecurityDefinitions(securityDefinitions);
+            }
+            customizer.setBeanConfig(beanConfig);
+
+            providers.add(new ReaderConfigFilter());
+
+            if (beanConfig.isUsePathBasedConfig()) {
+                providers.add(new ServletConfigProvider());
+            }
+
+            factory.setUserProviders(providers);
+        }
+
+        protected void initBeanConfig(BeanConfig beanConfig, Properties props) {
+
+            // resource package
+            String theResourcePackage = getResourcePackage();
+            if (theResourcePackage == null && props != null) {
+                theResourcePackage = props.getProperty(RESOURCE_PACKAGE_PROPERTY);
+            }
+            beanConfig.setResourcePackage(theResourcePackage);
+
+            // use path based configuration
+            Boolean theUsePathBasedConfig = isUsePathBasedConfig();
+            if (theUsePathBasedConfig == null && props != null) {
+                theUsePathBasedConfig = PropertyUtils.isTrue(props.get(USE_PATH_CFG_PROPERTY));
+            }
+            if (theUsePathBasedConfig == null) {
+                theUsePathBasedConfig = false;
+            }
+            beanConfig.setUsePathBasedConfig(theUsePathBasedConfig);
+
+            // version
+            String theVersion = getVersion();
+            if (theVersion == null && props != null) {
+                theVersion = props.getProperty(VERSION_PROPERTY);
+            }
+            beanConfig.setVersion(theVersion);
+
+            // host
+            String theHost = getHost();
+            if (theHost == null && props != null) {
+                theHost = props.getProperty(HOST_PROPERTY);
+            }
+            beanConfig.setHost(theHost);
+
+            // schemes
+            String[] theSchemes = getSchemes();
+            if (theSchemes == null && props != null && props.containsKey(SCHEMES_PROPERTY)) {
+                theSchemes = props.getProperty(SCHEMES_PROPERTY).split(",");
+            }
+            beanConfig.setSchemes(theSchemes);
+
+            // title
+            String theTitle = getTitle();
+            if (theTitle == null && props != null) {
+                theTitle = props.getProperty(TITLE_PROPERTY);
+            }
+            beanConfig.setTitle(theTitle);
+
+            // description
+            String theDescription = getDescription();
+            if (theDescription == null && props != null) {
+                theDescription = props.getProperty(DESCRIPTION_PROPERTY);
+            }
+            beanConfig.setDescription(theDescription);
+
+            // contact
+            String theContact = getContact();
+            if (theContact == null && props != null) {
+                theContact = props.getProperty(CONTACT_PROPERTY);
+            }
+            beanConfig.setContact(theContact);
+
+            // license
+            String theLicense = getLicense();
+            if (theLicense == null && !licenseWasSet) {
+                if (props != null) {
+                    theLicense = props.getProperty(LICENSE_PROPERTY);
+                    if (theLicense != null && theLicense.isEmpty()) {
+                        theLicense = null;
+                    }
+                } else {
+                    theLicense = DEFAULT_LICENSE_VALUE;
+                }
+            }
+            beanConfig.setLicense(theLicense);
+
+            // license url
+            String theLicenseUrl = getLicenseUrl();
+            if (theLicenseUrl == null && props != null) {
+                theLicenseUrl = props.getProperty(LICENSE_URL_PROPERTY);
+            }
+            if (theLicenseUrl == null && DEFAULT_LICENSE_VALUE.equals(theLicense)) {
+                theLicenseUrl = DEFAULT_LICENSE_URL;
+            }
+            beanConfig.setLicenseUrl(theLicenseUrl);
+
+            // terms of service url
+            String theTermsUrl = getTermsOfServiceUrl();
+            if (theTermsUrl == null && props != null) {
+                theTermsUrl = props.getProperty(TERMS_URL_PROPERTY);
+            }
+            beanConfig.setTermsOfServiceUrl(theTermsUrl);
+
+            // pretty print
+            Boolean thePrettyPrint = isPrettyPrint();
+            if (thePrettyPrint == null && props != null) {
+                thePrettyPrint = PropertyUtils.isTrue(props.get(PRETTY_PRINT_PROPERTY));
+            }
+            if (thePrettyPrint == null) {
+                thePrettyPrint = false;
+            }
+            beanConfig.setPrettyPrint(thePrettyPrint);
+
+            // filter class
+            String theFilterClass = getFilterClass();
+            if (theFilterClass == null && props != null) {
+                theFilterClass = props.getProperty(FILTER_CLASS_PROPERTY);
+            }
+            beanConfig.setFilterClass(theFilterClass);
+
+            // scan
+            beanConfig.setScan(isScan());
+
+            // base path is calculated dynamically
+            beanConfig.setBasePath(getBasePath());
+
+        }
+
+        public Boolean isUsePathBasedConfig() {
+            return usePathBasedConfig;
+        }
+
+        public void setUsePathBasedConfig(Boolean usePathBasedConfig) {
+            this.usePathBasedConfig = usePathBasedConfig;
+        }
+
+        public String getHost() {
+            return host;
+        }
+
+        public void setHost(String host) {
+            this.host = host;
+        }
+
+        public String[] getSchemes() {
+            return schemes;
+        }
+
+        public void setSchemes(String[] schemes) {
+            this.schemes = schemes;
+        }
+
+        public Boolean isPrettyPrint() {
+            return prettyPrint;
+        }
+
+        public void setPrettyPrint(Boolean prettyPrint) {
+            this.prettyPrint = prettyPrint;
+        }
+
+        public Swagger2Customizer getCustomizer() {
+            return customizer;
+        }
+
+        public void setCustomizer(Swagger2Customizer customizer) {
+            this.customizer = customizer;
+        }
+
+        public boolean isScanAllResources() {
+            return scanAllResources;
+        }
+
+        public void setScanAllResources(boolean scanAllResources) {
+            this.scanAllResources = scanAllResources;
+        }
+
+        public String getIgnoreRoutes() {
+            return ignoreRoutes;
+        }
+
+        public void setIgnoreRoutes(String ignoreRoutes) {
+            this.ignoreRoutes = ignoreRoutes;
+        }
+
+        @Override
+        protected void setBasePathByAddress(String address) {
+            if (!address.startsWith("/")) {
+                // get the path part
+                URI u = URI.create(address);
+                setBasePath(u.getPath());
+                if (getHost() == null) {
+                    setHost(u.getPort() < 0 ? u.getHost() : u.getHost() + ":" + u.getPort());
+                }
+            } else {
+                setBasePath(address);
+            }
+        }
+
+        /**
+         * Set SwaggerUI Maven group and artifact using the "groupId/artifactId" format.
+         * @param swaggerUiMavenGroupAndArtifact
+         */
+        public void setSwaggerUiMavenGroupAndArtifact(String swaggerUiMavenGroupAndArtifact) {
+            this.swaggerUiMavenGroupAndArtifact = swaggerUiMavenGroupAndArtifact;
+        }
+
+        public void setSwaggerUiVersion(String swaggerUiVersion) {
+            this.swaggerUiVersion = swaggerUiVersion;
+        }
+
+        public void setSupportSwaggerUi(boolean supportSwaggerUi) {
+            this.supportSwaggerUi = supportSwaggerUi;
+        }
+
+        @Override
+        public Boolean isSupportSwaggerUi() {
+            return supportSwaggerUi;
+        }
+
+        public void setSwaggerUiMediaTypes(Map<String, String> swaggerUiMediaTypes) {
+            this.swaggerUiMediaTypes = swaggerUiMediaTypes;
+        }
+
+        @Override
+        public Map<String, String> getSwaggerUiMediaTypes() {
+            return swaggerUiMediaTypes;
+        }
+
+        public void setSecurityDefinitions(Map<String, SecuritySchemeDefinition> securityDefinitions) {
+            this.securityDefinitions = securityDefinitions;
+        }
+
+        public String getPropertiesLocation() {
+            return propertiesLocation;
+        }
+
+        public void setPropertiesLocation(String propertiesLocation) {
+            this.propertiesLocation = propertiesLocation;
+        }
+
+        public boolean isScan() {
+            return scan;
+        }
+
+        public void setScan(boolean scan) {
+            this.scan = scan;
+        }
+
+        public void setSwaggerUiConfig(final SwaggerUiConfig swaggerUiConfig) {
+            this.swaggerUiConfig = swaggerUiConfig;
+        }
+
+        @Override
+        public SwaggerUiConfig getSwaggerUiConfig() {
+            return swaggerUiConfig;
+        }
+
+        @Override
+        public String findSwaggerUiRoot() {
+            return SwaggerUi.findSwaggerUiRoot(swaggerUiMavenGroupAndArtifact, swaggerUiVersion);
+        }
+
+        private class ServletConfigProvider implements ContextProvider<ServletConfig> {
+
+            @Override
+            public ServletConfig createContext(Message message) {
+                final ServletConfig sc = (ServletConfig)message.get("HTTP.CONFIG");
+
+                // When deploying into OSGi container, it is possible to use embedded Jetty
+                // transport. In this case, the ServletConfig is not available and Swagger
+                // does not take into account certain configuration parameters. To overcome
+                // that, the ServletConfig is synthesized from ServletContext instance.
+                if (sc == null) {
+                    final ServletContext context = (ServletContext)message.get("HTTP.CONTEXT");
+                    if (context != null) {
+                        return new SyntheticServletConfig(context) {
+                            @Override
+                            public String getInitParameter(String name) {
+                                if (Objects.equals(SwaggerContextService.USE_PATH_BASED_CONFIG, name)) {
+                                    return "true";
+                                } else {
+                                    return super.getInitParameter(name);
+                                }
+                            }
+                        };
+                    }
+                } else if (sc.getInitParameter(SwaggerContextService.USE_PATH_BASED_CONFIG) == null) {
+                    return new DelegatingServletConfig(sc) {
                         @Override
                         public String getInitParameter(String name) {
                             if (Objects.equals(SwaggerContextService.USE_PATH_BASED_CONFIG, name)) {
@@ -451,26 +611,47 @@ public class Swagger2Feature extends AbstractSwaggerFeature implements SwaggerUi
                         }
                     };
                 }
-            } else if (sc.getInitParameter(SwaggerContextService.USE_PATH_BASED_CONFIG) == null) {
-                return new DelegatingServletConfig(sc) {
-                    @Override
-                    public String getInitParameter(String name) {
-                        if (Objects.equals(SwaggerContextService.USE_PATH_BASED_CONFIG, name)) {
-                            return "true";
-                        } else {
-                            return super.getInitParameter(name);
-                        }
+
+                return sc;
+            }
+        }
+
+        protected class ReaderConfigFilter implements ContainerRequestFilter {
+
+            @Context
+            protected MessageContext mc;
+
+            @Override
+            public void filter(ContainerRequestContext requestContext) throws IOException {
+                ServletContext servletContext = mc.getServletContext();
+                if (servletContext != null && servletContext.getAttribute(ReaderConfig.class.getName()) == null) {
+                    if (mc.getServletConfig() != null
+                            && Boolean.valueOf(mc.getServletConfig().getInitParameter("scan.all.resources"))) {
+                        addReaderConfig(mc.getServletConfig().getInitParameter("ignore.routes"));
+                    } else if (isScanAllResources()) {
+                        addReaderConfig(getIgnoreRoutes());
                     }
-                };
+                }
             }
 
-            return sc;
+            protected void addReaderConfig(String ignoreRoutesParam) {
+                DefaultReaderConfig rc = new DefaultReaderConfig();
+                rc.setScanAllResources(true);
+                if (ignoreRoutesParam != null) {
+                    Set<String> routes = new LinkedHashSet<>();
+                    for (String route : ignoreRoutesParam.split(",")) {
+                        routes.add(route.trim());
+                    }
+                    rc.setIgnoredRoutes(routes);
+                }
+                mc.getServletContext().setAttribute(ReaderConfig.class.getName(), rc);
+            }
         }
     }
 
     @PreMatching
     protected static class SwaggerContainerRequestFilter extends Swagger2ApiListingResource
-        implements ContainerRequestFilter {
+            implements ContainerRequestFilter {
 
         protected static final String APIDOCS_LISTING_PATH_JSON = "swagger.json";
         protected static final String APIDOCS_LISTING_PATH_YAML = "swagger.yaml";
@@ -504,36 +685,4 @@ public class Swagger2Feature extends AbstractSwaggerFeature implements SwaggerUi
             }
         }
     }
-
-    protected class ReaderConfigFilter implements ContainerRequestFilter {
-
-        @Context
-        protected MessageContext mc;
-
-        @Override
-        public void filter(ContainerRequestContext requestContext) throws IOException {
-            ServletContext servletContext = mc.getServletContext();
-            if (servletContext != null && servletContext.getAttribute(ReaderConfig.class.getName()) == null) {
-                if (mc.getServletConfig() != null
-                        && Boolean.valueOf(mc.getServletConfig().getInitParameter("scan.all.resources"))) {
-                    addReaderConfig(mc.getServletConfig().getInitParameter("ignore.routes"));
-                } else if (isScanAllResources()) {
-                    addReaderConfig(getIgnoreRoutes());
-                }
-            }
-        }
-
-        protected void addReaderConfig(String ignoreRoutesParam) {
-            DefaultReaderConfig rc = new DefaultReaderConfig();
-            rc.setScanAllResources(true);
-            if (ignoreRoutesParam != null) {
-                Set<String> routes = new LinkedHashSet<>();
-                for (String route : ignoreRoutesParam.split(",")) {
-                    routes.add(route.trim());
-                }
-                rc.setIgnoredRoutes(routes);
-            }
-            mc.getServletContext().setAttribute(ReaderConfig.class.getName(), rc);
-        }
-    }
 }
diff --git a/rt/rs/sse/src/main/java/org/apache/cxf/jaxrs/sse/SseFeature.java b/rt/rs/sse/src/main/java/org/apache/cxf/jaxrs/sse/SseFeature.java
index 9ecbe27..92c85fe 100644
--- a/rt/rs/sse/src/main/java/org/apache/cxf/jaxrs/sse/SseFeature.java
+++ b/rt/rs/sse/src/main/java/org/apache/cxf/jaxrs/sse/SseFeature.java
@@ -26,19 +26,29 @@ import org.apache.cxf.annotations.Provider;
 import org.apache.cxf.annotations.Provider.Scope;
 import org.apache.cxf.annotations.Provider.Type;
 import org.apache.cxf.endpoint.Server;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.jaxrs.provider.ServerProviderFactory;
 
 @Provider(value = Type.Feature, scope = Scope.Server)
-public class SseFeature extends AbstractFeature {
-    @Override
-    public void initialize(Server server, Bus bus) {
-        final List<Object> providers = new ArrayList<>();
+public class SseFeature extends DelegatingFeature<SseFeature.Portable> {
 
-        providers.add(new SseContextProvider());
-        providers.add(new SseEventSinkContextProvider());
+    public SseFeature() {
+        super(new Portable());
+    }
+
+    @Provider(value = Type.Feature, scope = Scope.Server)
+    public static class Portable implements AbstractPortableFeature {
+
+        @Override
+        public void initialize(Server server, Bus bus) {
+            final List<Object> providers = new ArrayList<>();
+
+            providers.add(new SseContextProvider());
+            providers.add(new SseEventSinkContextProvider());
 
-        ((ServerProviderFactory) server.getEndpoint().get(
-            ServerProviderFactory.class.getName())).setUserProviders(providers);
+            ((ServerProviderFactory) server.getEndpoint().get(
+                    ServerProviderFactory.class.getName())).setUserProviders(providers);
+        }
     }
 }
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpConduitFeature.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpConduitFeature.java
index c39d351..e58fe5c 100644
--- a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpConduitFeature.java
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpConduitFeature.java
@@ -20,26 +20,36 @@ package org.apache.cxf.transport.http;
 
 import org.apache.cxf.Bus;
 import org.apache.cxf.endpoint.Client;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.transport.Conduit;
 
 /**
  * Programmatically configure a http conduit. This can also be used as a DOSGi
  * intent.
  */
-public class HttpConduitFeature extends AbstractFeature {
-    private HttpConduitConfig conduitConfig;
-
-    @Override
-    public void initialize(Client client, Bus bus) {
-        Conduit conduit = client.getConduit();
-        if (conduitConfig != null && conduit instanceof HTTPConduit) {
-            conduitConfig.apply((HTTPConduit)conduit);
-        }
+public class HttpConduitFeature extends DelegatingFeature<HttpConduitFeature.Portable> {
+    public HttpConduitFeature() {
+        super(new Portable());
     }
 
     public void setConduitConfig(HttpConduitConfig conduitConfig) {
-        this.conduitConfig = conduitConfig;
+        delegate.setConduitConfig(conduitConfig);
     }
 
+    public static class Portable implements AbstractPortableFeature {
+        private HttpConduitConfig conduitConfig;
+
+        @Override
+        public void initialize(Client client, Bus bus) {
+            Conduit conduit = client.getConduit();
+            if (conduitConfig != null && conduit instanceof HTTPConduit) {
+                conduitConfig.apply((HTTPConduit)conduit);
+            }
+        }
+
+        public void setConduitConfig(HttpConduitConfig conduitConfig) {
+            this.conduitConfig = conduitConfig;
+        }
+    }
 }
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpDestinationFeature.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpDestinationFeature.java
index 02dcac2..91268f9 100644
--- a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpDestinationFeature.java
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpDestinationFeature.java
@@ -20,25 +20,36 @@ package org.apache.cxf.transport.http;
 
 import org.apache.cxf.Bus;
 import org.apache.cxf.endpoint.Server;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.transport.Destination;
 
 /**
  * Programmatically configure a http destination. This can also be used as a DOSGi
  * intent.
  */
-public class HttpDestinationFeature extends AbstractFeature {
-    private HttpDestinationConfig destinationConfig;
-
-    @Override
-    public void initialize(Server server, Bus bus) {
-        Destination destination = server.getDestination();
-        if (destinationConfig != null && destination instanceof AbstractHTTPDestination) {
-            destinationConfig.apply((AbstractHTTPDestination)destination);
-        }
+public class HttpDestinationFeature extends DelegatingFeature<HttpDestinationFeature.Portable> {
+    public HttpDestinationFeature() {
+        super(new Portable());
     }
 
     public void setDestinationConfig(HttpDestinationConfig destinationConfig) {
-        this.destinationConfig = destinationConfig;
+        delegate.setDestinationConfig(destinationConfig);
+    }
+
+    public static class Portable implements AbstractPortableFeature {
+        private HttpDestinationConfig destinationConfig;
+
+        @Override
+        public void initialize(Server server, Bus bus) {
+            Destination destination = server.getDestination();
+            if (destinationConfig != null && destination instanceof AbstractHTTPDestination) {
+                destinationConfig.apply((AbstractHTTPDestination)destination);
+            }
+        }
+
+        public void setDestinationConfig(HttpDestinationConfig destinationConfig) {
+            this.destinationConfig = destinationConfig;
+        }
     }
 }
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/https/CertConstraintsFeature.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/CertConstraintsFeature.java
index 289cbcf..18e9199 100644
--- a/rt/transports/http/src/main/java/org/apache/cxf/transport/https/CertConstraintsFeature.java
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/CertConstraintsFeature.java
@@ -24,7 +24,8 @@ import org.apache.cxf.common.injection.NoJSR250Annotations;
 import org.apache.cxf.configuration.security.CertificateConstraintsType;
 import org.apache.cxf.endpoint.Client;
 import org.apache.cxf.endpoint.Server;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.interceptor.InterceptorProvider;
 
 /**
@@ -55,57 +56,72 @@ import org.apache.cxf.interceptor.InterceptorProvider;
   </pre>
  */
 @NoJSR250Annotations
-public class CertConstraintsFeature extends AbstractFeature {
-    CertificateConstraintsType contraints;
-
+public class CertConstraintsFeature extends DelegatingFeature<CertConstraintsFeature.Portable> {
 
     public CertConstraintsFeature() {
+        super(new Portable());
     }
 
-    @Override
-    public void initialize(Server server, Bus bus) {
-        if (contraints == null) {
-            return;
-        }
-        initializeProvider(server.getEndpoint(), bus);
-        CertConstraints c = CertConstraintsJaxBUtils.createCertConstraints(contraints);
-        server.getEndpoint().put(CertConstraints.class.getName(), c);
+    public void setCertificateConstraints(CertificateConstraintsType c) {
+        delegate.setCertificateConstraints(c);
     }
 
-    @Override
-    public void initialize(Client client, Bus bus) {
-        if (contraints == null) {
-            return;
-        }
-        initializeProvider(client, bus);
-        CertConstraints c = CertConstraintsJaxBUtils.createCertConstraints(contraints);
-        client.getEndpoint().put(CertConstraints.class.getName(), c);
+    public CertificateConstraintsType getCertificateConstraints() {
+        return delegate.getCertificateConstraints();
     }
 
-    @Override
-    public void initialize(Bus bus) {
-        if (contraints == null) {
-            return;
+    public static class Portable implements AbstractPortableFeature {
+        CertificateConstraintsType contraints;
+
+
+        public Portable() {
         }
-        initializeProvider(bus, bus);
-        CertConstraints c = CertConstraintsJaxBUtils.createCertConstraints(contraints);
-        bus.setProperty(CertConstraints.class.getName(), c);
-    }
 
-    @Override
-    protected void initializeProvider(InterceptorProvider provider, Bus bus) {
-        if (contraints == null) {
-            return;
+        @Override
+        public void initialize(Server server, Bus bus) {
+            if (contraints == null) {
+                return;
+            }
+            doInitializeProvider(server.getEndpoint(), bus);
+            CertConstraints c = CertConstraintsJaxBUtils.createCertConstraints(contraints);
+            server.getEndpoint().put(CertConstraints.class.getName(), c);
         }
-        provider.getInInterceptors().add(CertConstraintsInterceptor.INSTANCE);
-        provider.getInFaultInterceptors().add(CertConstraintsInterceptor.INSTANCE);
-    }
 
-    public void setCertificateConstraints(CertificateConstraintsType c) {
-        contraints = c;
-    }
+        @Override
+        public void initialize(Client client, Bus bus) {
+            if (contraints == null) {
+                return;
+            }
+            doInitializeProvider(client, bus);
+            CertConstraints c = CertConstraintsJaxBUtils.createCertConstraints(contraints);
+            client.getEndpoint().put(CertConstraints.class.getName(), c);
+        }
 
-    public CertificateConstraintsType getCertificateConstraints() {
-        return contraints;
+        @Override
+        public void initialize(Bus bus) {
+            if (contraints == null) {
+                return;
+            }
+            doInitializeProvider(bus, bus);
+            CertConstraints c = CertConstraintsJaxBUtils.createCertConstraints(contraints);
+            bus.setProperty(CertConstraints.class.getName(), c);
+        }
+
+        @Override
+        public void doInitializeProvider(InterceptorProvider provider, Bus bus) {
+            if (contraints == null) {
+                return;
+            }
+            provider.getInInterceptors().add(CertConstraintsInterceptor.INSTANCE);
+            provider.getInFaultInterceptors().add(CertConstraintsInterceptor.INSTANCE);
+        }
+
+        public void setCertificateConstraints(CertificateConstraintsType c) {
+            contraints = c;
+        }
+
+        public CertificateConstraintsType getCertificateConstraints() {
+            return contraints;
+        }
     }
 }
diff --git a/rt/transports/jms/src/main/java/org/apache/cxf/transport/jms/ConnectionFactoryFeature.java b/rt/transports/jms/src/main/java/org/apache/cxf/transport/jms/ConnectionFactoryFeature.java
index ee0dfaa..a332e12 100644
--- a/rt/transports/jms/src/main/java/org/apache/cxf/transport/jms/ConnectionFactoryFeature.java
+++ b/rt/transports/jms/src/main/java/org/apache/cxf/transport/jms/ConnectionFactoryFeature.java
@@ -24,7 +24,8 @@ import org.apache.cxf.Bus;
 import org.apache.cxf.common.injection.NoJSR250Annotations;
 import org.apache.cxf.endpoint.Client;
 import org.apache.cxf.endpoint.Server;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.interceptor.Fault;
 import org.apache.cxf.interceptor.InterceptorProvider;
 import org.apache.cxf.message.Message;
@@ -39,44 +40,47 @@ import org.apache.cxf.transport.Destination;
  * configuration that is generated from the old configuration style.
  */
 @NoJSR250Annotations
-public class ConnectionFactoryFeature extends AbstractFeature {
-    private ConnectionFactory connectionFactory;
-
+public class ConnectionFactoryFeature extends DelegatingFeature<ConnectionFactoryFeature.Portable> {
     public ConnectionFactoryFeature(ConnectionFactory cf) {
-        this.connectionFactory = cf;
+        super(new Portable(cf));
     }
 
-    @Override
-    public void initialize(Client client, Bus bus) {
-        client.getEndpoint().getOutInterceptors().add(new JMSConduitConfigOutInterceptor());
-        super.initialize(client, bus);
-    }
-    @Override
-    public void initialize(InterceptorProvider provider, Bus bus) {
-        provider.getOutInterceptors().add(new JMSConduitConfigOutInterceptor());
-        super.initialize(provider, bus);
-    }
+    public static class Portable implements AbstractPortableFeature {
+        private ConnectionFactory connectionFactory;
 
-    @Override
-    public void initialize(Server server, Bus bus) {
-        Destination destination = server.getDestination();
-        if (destination instanceof JMSDestination) {
-            JMSDestination jmsDestination = (JMSDestination)destination;
-            jmsDestination.getJmsConfig().setConnectionFactory(connectionFactory);
+        public Portable(ConnectionFactory cf) {
+            this.connectionFactory = cf;
         }
-        super.initialize(server, bus);
-    }
-    private class JMSConduitConfigOutInterceptor extends AbstractPhaseInterceptor<Message> {
-        JMSConduitConfigOutInterceptor() {
-            super(Phase.PREPARE_SEND);
+
+        @Override
+        public void initialize(Client client, Bus bus) {
+            client.getEndpoint().getOutInterceptors().add(new JMSConduitConfigOutInterceptor());
+        }
+        @Override
+        public void initialize(InterceptorProvider provider, Bus bus) {
+            provider.getOutInterceptors().add(new JMSConduitConfigOutInterceptor());
         }
 
         @Override
-        public void handleMessage(Message message) throws Fault {
-            Conduit conduit = message.getExchange().getConduit(message);
-            if (conduit instanceof JMSConduit) {
-                JMSConduit jmsConduit = (JMSConduit)conduit;
-                jmsConduit.getJmsConfig().setConnectionFactory(connectionFactory);
+        public void initialize(Server server, Bus bus) {
+            Destination destination = server.getDestination();
+            if (destination instanceof JMSDestination) {
+                JMSDestination jmsDestination = (JMSDestination)destination;
+                jmsDestination.getJmsConfig().setConnectionFactory(connectionFactory);
+            }
+        }
+        private class JMSConduitConfigOutInterceptor extends AbstractPhaseInterceptor<Message> {
+            JMSConduitConfigOutInterceptor() {
+                super(Phase.PREPARE_SEND);
+            }
+
+            @Override
+            public void handleMessage(Message message) throws Fault {
+                Conduit conduit = message.getExchange().getConduit(message);
+                if (conduit instanceof JMSConduit) {
+                    JMSConduit jmsConduit = (JMSConduit)conduit;
+                    jmsConduit.getJmsConfig().setConnectionFactory(connectionFactory);
+                }
             }
         }
     }
diff --git a/rt/transports/jms/src/main/java/org/apache/cxf/transport/jms/JMSConfigFeature.java b/rt/transports/jms/src/main/java/org/apache/cxf/transport/jms/JMSConfigFeature.java
index 3119b35..668ca0f 100644
--- a/rt/transports/jms/src/main/java/org/apache/cxf/transport/jms/JMSConfigFeature.java
+++ b/rt/transports/jms/src/main/java/org/apache/cxf/transport/jms/JMSConfigFeature.java
@@ -27,7 +27,8 @@ import org.apache.cxf.common.logging.LogUtils;
 import org.apache.cxf.configuration.ConfigurationException;
 import org.apache.cxf.endpoint.Client;
 import org.apache.cxf.endpoint.Server;
-import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.AbstractPortableFeature;
+import org.apache.cxf.feature.DelegatingFeature;
 import org.apache.cxf.transport.Conduit;
 import org.apache.cxf.transport.Destination;
 
@@ -37,46 +38,58 @@ import org.apache.cxf.transport.Destination;
  * configuration that is generated from the old configuration style.
  */
 @NoJSR250Annotations
-public class JMSConfigFeature extends AbstractFeature {
-    static final Logger LOG = LogUtils.getL7dLogger(JMSConfigFeature.class);
-
-    JMSConfiguration jmsConfig;
-
-    @Override
-    public void initialize(Client client, Bus bus) {
-        checkJmsConfig();
-        Conduit conduit = client.getConduit();
-        if (!(conduit instanceof JMSConduit)) {
-            throw new ConfigurationException(new Message("JMSCONFIGFEATURE_ONLY_JMS", LOG));
-        }
-        JMSConduit jmsConduit = (JMSConduit)conduit;
-        jmsConduit.setJmsConfig(jmsConfig);
-        super.initialize(client, bus);
-    }
-
-    @Override
-    public void initialize(Server server, Bus bus) {
-        checkJmsConfig();
-        Destination destination = server.getDestination();
-        if (!(destination instanceof JMSDestination)) {
-            throw new ConfigurationException(new Message("JMSCONFIGFEATURE_ONLY_JMS", LOG));
-        }
-        JMSDestination jmsDestination = (JMSDestination)destination;
-        jmsDestination.setJmsConfig(jmsConfig);
-        super.initialize(server, bus);
+public class JMSConfigFeature extends DelegatingFeature<JMSConfigFeature.Portable> {
+    public JMSConfigFeature() {
+        super(new Portable());
     }
 
     public JMSConfiguration getJmsConfig() {
-        return jmsConfig;
+        return delegate.getJmsConfig();
     }
 
     public void setJmsConfig(JMSConfiguration jmsConfig) {
-        this.jmsConfig = jmsConfig;
+        delegate.setJmsConfig(jmsConfig);
     }
 
-    private void checkJmsConfig() {
-        if (jmsConfig == null) {
-            throw new ConfigurationException(new Message("JMSCONFIG_REQUIRED", LOG));
+    public static class Portable implements AbstractPortableFeature {
+        static final Logger LOG = LogUtils.getL7dLogger(JMSConfigFeature.class);
+
+        JMSConfiguration jmsConfig;
+
+        @Override
+        public void initialize(Client client, Bus bus) {
+            checkJmsConfig();
+            Conduit conduit = client.getConduit();
+            if (!(conduit instanceof JMSConduit)) {
+                throw new ConfigurationException(new Message("JMSCONFIGFEATURE_ONLY_JMS", LOG));
+            }
+            JMSConduit jmsConduit = (JMSConduit)conduit;
+            jmsConduit.setJmsConfig(jmsConfig);
+        }
+
+        @Override
+        public void initialize(Server server, Bus bus) {
+            checkJmsConfig();
+            Destination destination = server.getDestination();
+            if (!(destination instanceof JMSDestination)) {
+                throw new ConfigurationException(new Message("JMSCONFIGFEATURE_ONLY_JMS", LOG));
+            }
+            JMSDestination jmsDestination = (JMSDestination)destination;
+            jmsDestination.setJmsConfig(jmsConfig);
+        }
+
+        public JMSConfiguration getJmsConfig() {
+            return jmsConfig;
+        }
+
+        public void setJmsConfig(JMSConfiguration jmsConfig) {
+            this.jmsConfig = jmsConfig;
+        }
+
+        private void checkJmsConfig() {
+            if (jmsConfig == null) {
+                throw new ConfigurationException(new Message("JMSCONFIG_REQUIRED", LOG));
+            }
         }
     }
 }
diff --git a/systests/tracing/src/test/java/org/apache/cxf/systest/jaeger/TestSender.java b/systests/tracing/src/test/java/org/apache/cxf/systest/jaeger/TestSender.java
index d4f49a7..1871cfa 100644
--- a/systests/tracing/src/test/java/org/apache/cxf/systest/jaeger/TestSender.java
+++ b/systests/tracing/src/test/java/org/apache/cxf/systest/jaeger/TestSender.java
@@ -20,6 +20,7 @@ package org.apache.cxf.systest.jaeger;
 
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CountDownLatch;
 
 import io.jaegertracing.internal.JaegerSpan;
 import io.jaegertracing.internal.exceptions.SenderException;
@@ -29,9 +30,14 @@ public class TestSender implements Sender {
 
     private static final List<JaegerSpan> SPANS = new CopyOnWriteArrayList<>();
 
+    private static CountDownLatch synchro;
+
     @Override
     public int append(JaegerSpan span) throws SenderException {
         SPANS.add(span);
+        if (synchro != null) {
+            synchro.countDown();
+        }
         return 0;
     }
 
@@ -52,4 +58,8 @@ public class TestSender implements Sender {
     public static void clear() {
         SPANS.clear();
     }
+
+    public static void setSynchro(CountDownLatch newSynchro) {
+        synchro = newSynchro;
+    }
 }
diff --git a/systests/tracing/src/test/java/org/apache/cxf/systest/jaxrs/tracing/opentracing/OpenTracingTracingTest.java b/systests/tracing/src/test/java/org/apache/cxf/systest/jaxrs/tracing/opentracing/OpenTracingTracingTest.java
index 1a89e8d..6cc9227 100644
--- a/systests/tracing/src/test/java/org/apache/cxf/systest/jaxrs/tracing/opentracing/OpenTracingTracingTest.java
+++ b/systests/tracing/src/test/java/org/apache/cxf/systest/jaxrs/tracing/opentracing/OpenTracingTracingTest.java
@@ -24,6 +24,7 @@ import java.util.Collection;
 import java.util.Iterator;
 import java.util.Map.Entry;
 import java.util.Random;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
@@ -62,6 +63,7 @@ import io.opentracing.Tracer;
 import io.opentracing.propagation.Format.Builtin;
 import io.opentracing.propagation.TextMap;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Ignore;
@@ -137,6 +139,11 @@ public class OpenTracingTracingTest extends AbstractBusClientServerTestBase {
         openTracingClientProvider = new OpenTracingClientProvider(tracer);
     }
 
+    @After
+    public void tearDown() {
+        TestSender.setSynchro(null);
+    }
+
     @Test
     public void testThatNewSpanIsCreatedWhenNotProvided() {
         final Response r = createWebClient("/bookstore/books").get();
@@ -194,12 +201,16 @@ public class OpenTracingTracingTest extends AbstractBusClientServerTestBase {
     }
 
     @Test
-    public void testThatNewInnerSpanIsCreatedUsingAsyncInvocation() {
+    public void testThatNewInnerSpanIsCreatedUsingAsyncInvocation() throws InterruptedException {
+        final CountDownLatch synchro = new CountDownLatch(2);
+        TestSender.setSynchro(synchro);
+
         final JaegerSpanContext spanId = fromRandom();
 
         final Response r = withTrace(createWebClient("/bookstore/books/async"), spanId).get();
         assertEquals(Status.OK.getStatusCode(), r.getStatus());
 
+        synchro.await(1, TimeUnit.MINUTES);
         assertThat(TestSender.getAllSpans().size(), equalTo(2));
         assertEquals("Processing books", TestSender.getAllSpans().get(0).getOperationName());
         assertEquals("GET /bookstore/books/async", TestSender.getAllSpans().get(1).getOperationName());
@@ -220,10 +231,14 @@ public class OpenTracingTracingTest extends AbstractBusClientServerTestBase {
     }
 
     @Test
-    public void testThatNewSpanIsCreatedUsingAsyncInvocation() {
+    public void testThatNewSpanIsCreatedUsingAsyncInvocation() throws InterruptedException {
+        final CountDownLatch synchro = new CountDownLatch(2);
+        TestSender.setSynchro(synchro);
+
         final Response r = createWebClient("/bookstore/books/async").get();
         assertEquals(Status.OK.getStatusCode(), r.getStatus());
 
+        synchro.await(1, TimeUnit.MINUTES);
         assertThat(TestSender.getAllSpans().size(), equalTo(2));
         assertThat(TestSender.getAllSpans().get(0).getOperationName(), equalTo("Processing books"));
         assertThat(TestSender.getAllSpans().get(1).getOperationName(), equalTo("GET /bookstore/books/async"));
@@ -231,12 +246,16 @@ public class OpenTracingTracingTest extends AbstractBusClientServerTestBase {
 
     @Test
     public void testThatNewSpanIsCreatedWhenNotProvidedUsingAsyncClient() throws Exception {
+        final CountDownLatch synchro = new CountDownLatch(3);
+        TestSender.setSynchro(synchro);
+
         final WebClient client = createWebClient("/bookstore/books", openTracingClientProvider);
         final Future<Response> f = client.async().get();
 
         final Response r = f.get(1, TimeUnit.SECONDS);
         assertEquals(Status.OK.getStatusCode(), r.getStatus());
 
+        synchro.await(1, TimeUnit.MINUTES);
         assertThat(TestSender.getAllSpans().size(), equalTo(3));
         assertThat(TestSender.getAllSpans().get(0).getOperationName(), equalTo("Get Books"));
         assertThat(TestSender.getAllSpans().get(1).getOperationName(), equalTo("GET /bookstore/books"));
@@ -338,12 +357,16 @@ public class OpenTracingTracingTest extends AbstractBusClientServerTestBase {
 
         final Span span = tracer.buildSpan("test span").start();
         try (Scope scope = tracer.scopeManager().activate(span)) {
+            final CountDownLatch synchro = new CountDownLatch(3);
+            TestSender.setSynchro(synchro);
+
             final Future<Response> f = client.async().get();
 
             final Response r = f.get(1, TimeUnit.HOURS);
             assertEquals(Status.OK.getStatusCode(), r.getStatus());
             assertThat(tracer.activeSpan().context(), equalTo(span.context()));
 
+            synchro.await(1, TimeUnit.MINUTES);
             assertThat(TestSender.getAllSpans().size(), equalTo(3));
             assertThat(TestSender.getAllSpans().get(0).getOperationName(), equalTo("Get Books"));
             assertThat(TestSender.getAllSpans().get(0).getReferences(), not(empty()));
diff --git a/systests/uncategorized/src/test/java/org/apache/cxf/systest/schema_validation/ValidationClientServerTest.java b/systests/uncategorized/src/test/java/org/apache/cxf/systest/schema_validation/ValidationClientServerTest.java
index 4520ec4..ecd12c3 100644
--- a/systests/uncategorized/src/test/java/org/apache/cxf/systest/schema_validation/ValidationClientServerTest.java
+++ b/systests/uncategorized/src/test/java/org/apache/cxf/systest/schema_validation/ValidationClientServerTest.java
@@ -23,6 +23,7 @@ import java.io.Serializable;
 import java.io.StringReader;
 import java.net.URL;
 import java.util.List;
+import java.util.Locale;
 
 import javax.xml.namespace.QName;
 import javax.xml.transform.Source;
@@ -45,6 +46,7 @@ import org.apache.schema_validation.types.OccuringStruct;
 import org.apache.schema_validation.types.SomeRequest;
 import org.apache.schema_validation.types.SomeResponse;
 
+import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
@@ -56,16 +58,24 @@ import static org.junit.Assert.fail;
 public class ValidationClientServerTest extends AbstractBusClientServerTestBase {
     public static final String PORT = ValidationServer.PORT;
 
+    private static Locale oldLocale;
+
     private final QName serviceName = new QName("http://apache.org/schema_validation",
                                                 "SchemaValidationService");
     private final QName portName = new QName("http://apache.org/schema_validation", "SoapPort");
 
-
     @BeforeClass
     public static void startservers() throws Exception {
+        oldLocale = Locale.getDefault();
+        Locale.setDefault(Locale.ENGLISH);
         assertTrue("server did not launch correctly", launchServer(ValidationServer.class, true));
     }
 
+    @AfterClass
+    public static void resetLocale() {
+        Locale.setDefault(oldLocale);
+    }
+
     @Test
     public void testSchemaValidationProviderPayload() throws Exception {
         doProviderTest("PProvider");