CXF-8180/CXF-8181 - UnsupportedOperationException in AbstractEndpointFactory / CXFNonSpringJaxrsServlet Feature registration fails via javax.ws.rs.Application
diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/servlet/CXFNonSpringJaxrsServlet.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/servlet/CXFNonSpringJaxrsServlet.java
index 321a5d4..c8f1647 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/servlet/CXFNonSpringJaxrsServlet.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/servlet/CXFNonSpringJaxrsServlet.java
@@ -165,7 +165,7 @@
         setExtensions(bean, servletConfig);
 
         List<? extends Feature> features = getFeatures(servletConfig, splitChar);
-        bean.setFeatures(features);
+        bean.getFeatures().addAll(features);
 
         bean.create();
     }
@@ -190,9 +190,12 @@
              CastUtils.cast((Map<?, ?>)parseMapSequence(servletConfig.getInitParameter(EXTENSIONS_PARAM))));
         bean.setLanguageMappings(
              CastUtils.cast((Map<?, ?>)parseMapSequence(servletConfig.getInitParameter(LANGUAGES_PARAM))));
-        bean.setProperties(CastUtils.cast(
+        Map<String, Object> properties = CastUtils.cast(
                 parseMapSequence(servletConfig.getInitParameter(PROPERTIES_PARAM)),
-                String.class, Object.class));
+                String.class, Object.class);
+        if (properties != null) {
+            bean.getProperties(true).putAll(properties);
+        }
     }
 
     protected void setAllInterceptors(JAXRSServerFactoryBean bean, ServletConfig servletConfig,
@@ -527,7 +530,7 @@
             List<?> providers = getProviders(servletConfig, splitChar);
             bean.setProviders(providers);
             List<? extends Feature> features = getFeatures(servletConfig, splitChar);
-            bean.setFeatures(features);
+            bean.getFeatures().addAll(features);
 
             bean.setBus(getBus());
             bean.setApplicationInfo(providerApp);
@@ -560,7 +563,7 @@
         List<?> providers = getProviders(servletConfig, splitChar);
         bean.setProviders(providers);
         List<? extends Feature> features = getFeatures(servletConfig, splitChar);
-        bean.setFeatures(features);
+        bean.getFeatures().addAll(features);
 
         bean.setBus(getBus());
         bean.setApplication(getApplication());
diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/ResourceUtils.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/ResourceUtils.java
index 471fa85..10119d7 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/ResourceUtils.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/ResourceUtils.java
@@ -944,7 +944,7 @@
         bean.setStaticSubresourceResolution(staticSubresourceResolution);
         bean.setResourceClasses(resourceClasses);
         bean.setProviders(providers);
-        bean.setFeatures(features);
+        bean.getFeatures().addAll(features);
         for (Map.Entry<Class<?>, ResourceProvider> entry : map.entrySet()) {
             bean.setResourceProvider(entry.getKey(), entry.getValue());
         }
diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookApplicationNonSpring.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookApplicationNonSpring.java
new file mode 100644
index 0000000..1d3c4ed
--- /dev/null
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookApplicationNonSpring.java
@@ -0,0 +1,82 @@
+/**
+ * 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.systest.jaxrs;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.cxf.jaxrs.openapi.OpenApiFeature;
+
+@ApplicationPath("/")
+@GlobalNameBinding
+public class BookApplicationNonSpring extends Application {
+
+    private String defaultName;
+    private long defaultId;
+    @Context
+    private UriInfo uriInfo;
+
+    @Override
+    public Set<Class<?>> getClasses() {
+        Set<Class<?>> classes = new HashSet<>();
+        classes.add(org.apache.cxf.systest.jaxrs.BookStore.class);
+        return classes;
+    }
+
+    @Override
+    public Set<Object> getSingletons() {
+        Set<Object> classes = new HashSet<>();
+        org.apache.cxf.systest.jaxrs.BookStore store =
+            new org.apache.cxf.systest.jaxrs.BookStore(uriInfo);
+        store.setDefaultNameAndId(defaultName, defaultId);
+        classes.add(store);
+        BookExceptionMapper mapper = new org.apache.cxf.systest.jaxrs.BookExceptionMapper();
+        mapper.setToHandle(true);
+        classes.add(mapper);
+        classes.add(new OpenApiFeature());
+        return classes;
+    }
+
+
+    @Override
+    public Map<String, Object> getProperties() {
+        return Collections.<String, Object>singletonMap("book", "cxf");
+    }
+
+    public void setDefaultName(String name) {
+        defaultName = name;
+    }
+
+    public void setDefaultId(List<String> ids) {
+        StringBuilder sb = new StringBuilder();
+        for (String id : ids) {
+            sb.append(id);
+        }
+        defaultId = Long.valueOf(sb.toString());
+    }
+
+}
diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStoreNonSpring.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStoreNonSpring.java
new file mode 100644
index 0000000..48ec617
--- /dev/null
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStoreNonSpring.java
@@ -0,0 +1,37 @@
+/**
+ * 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.systest.jaxrs;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+@Path("/bookstore")
+public class BookStoreNonSpring {
+
+    @GET
+    @Path("/")
+    public Book getBookRoot() {
+        Book book = new Book();
+        book.setName("root");
+        book.setId(124L);
+        return book;
+    }
+
+}
\ No newline at end of file
diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStoreOpenAPI.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStoreOpenAPI.java
new file mode 100644
index 0000000..e87643f
--- /dev/null
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStoreOpenAPI.java
@@ -0,0 +1,40 @@
+/**
+ * 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.systest.jaxrs;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+import org.apache.cxf.feature.Features;
+
+@Path("/bookstore")
+@Features(features = "org.apache.cxf.jaxrs.openapi.OpenApiFeature")
+public class BookStoreOpenAPI {
+
+    @GET
+    @Path("/")
+    public Book getBookRoot() {
+        Book book = new Book();
+        book.setName("root");
+        book.setId(124L);
+        return book;
+    }
+
+}
\ No newline at end of file
diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSNonSpringJaxrsServletTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSNonSpringJaxrsServletTest.java
index a7bcc6d..b60c258 100644
--- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSNonSpringJaxrsServletTest.java
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSNonSpringJaxrsServletTest.java
@@ -19,6 +19,8 @@
 
 package org.apache.cxf.systest.jaxrs;
 
+import javax.ws.rs.core.Response;
+
 import org.apache.cxf.jaxrs.client.WebClient;
 import org.apache.cxf.jaxrs.model.AbstractResourceInfo;
 import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
@@ -34,23 +36,44 @@
  */
 public class JAXRSNonSpringJaxrsServletTest extends AbstractBusClientServerTestBase {
     public static final String PORT = NonSpringJaxrsServletBookServer.PORT;
-    public static final String PORT2 = allocatePort(JAXRSNonSpringJaxrsServletTest.class);
+    public static final String PORT2 = NonSpringJaxrsServletBookServer2.PORT;
 
     @BeforeClass
     public static void startServers() throws Exception {
         AbstractResourceInfo.clearAllMaps();
         assertTrue("server did not launch correctly",
                    launchServer(NonSpringJaxrsServletBookServer.class, true));
+        assertTrue("server did not launch correctly",
+                   launchServer(NonSpringJaxrsServletBookServer2.class, true));
         createStaticBus();
     }
 
     @Test
-    public void testGetBookRoot() throws Exception {
+    public void testFeatureOnResourceClass() throws Exception {
         String address = "http://localhost:" + PORT + "/bookstore/;JSESSIONID=xxx";
         WebClient wc = WebClient.create(address);
         Book book = wc.get(Book.class);
         assertEquals(124L, book.getId());
         assertEquals("root", book.getName());
+
+        // Check OpenAPI feature is working correctly
+        wc = WebClient.create("http://localhost:" + PORT + "/openapi.json");
+        Response openAPIResponse = wc.get();
+        assertEquals(200, openAPIResponse.getStatus());
+    }
+
+    @Test
+    public void testFeatureOnResourceClassUsingApplication() throws Exception {
+        String address = "http://localhost:" + PORT2 + "/bookstore/;JSESSIONID=xxx";
+        WebClient wc = WebClient.create(address);
+        Book book = wc.get(Book.class);
+        assertEquals(124L, book.getId());
+        assertEquals("root", book.getName());
+
+        // Check OpenAPI feature is working correctly
+        wc = WebClient.create("http://localhost:" + PORT2 + "/openapi.json");
+        Response openAPIResponse = wc.get();
+        assertEquals(200, openAPIResponse.getStatus());
     }
 
 }
\ No newline at end of file
diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/NonSpringJaxrsServletBookServer.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/NonSpringJaxrsServletBookServer.java
index 7ff828e..adaf782 100644
--- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/NonSpringJaxrsServletBookServer.java
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/NonSpringJaxrsServletBookServer.java
@@ -33,7 +33,8 @@
     protected void run() {
         server = new org.eclipse.jetty.server.Server(Integer.parseInt(PORT));
 
-        final ServletHolder servletHolder = new ServletHolder(new CXFNonSpringJaxrsServlet(new BookStore()));
+        final ServletHolder servletHolder =
+            new ServletHolder(new CXFNonSpringJaxrsServlet(new BookStoreOpenAPI()));
         final ServletContextHandler context = new ServletContextHandler();
         context.setContextPath("/");
         context.addServlet(servletHolder, "/*");
diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/NonSpringJaxrsServletBookServer2.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/NonSpringJaxrsServletBookServer2.java
new file mode 100644
index 0000000..fc1b609
--- /dev/null
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/NonSpringJaxrsServletBookServer2.java
@@ -0,0 +1,69 @@
+/**
+ * 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.systest.jaxrs;
+
+import org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet;
+import org.apache.cxf.testutil.common.AbstractBusTestServerBase;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+
+public class NonSpringJaxrsServletBookServer2 extends AbstractBusTestServerBase {
+    public static final String PORT = allocatePort(NonSpringJaxrsServletBookServer2.class);
+    private org.eclipse.jetty.server.Server server;
+
+    protected void run() {
+        server = new org.eclipse.jetty.server.Server(Integer.parseInt(PORT));
+
+        final ServletHolder servletHolder =
+            new ServletHolder(new CXFNonSpringJaxrsServlet(new BookApplicationNonSpring()));
+        final ServletContextHandler context = new ServletContextHandler();
+        context.setContextPath("/");
+        context.addServlet(servletHolder, "/*");
+        //servletHolder.setInitParameter("jaxrs.serviceClasses", BookStore.class.getName());
+
+        server.setHandler(context);
+        try {
+            server.start();
+        } catch (Exception e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
+
+    public void tearDown() throws Exception {
+        if (server != null) {
+            server.stop();
+            server.destroy();
+            server = null;
+        }
+    }
+
+    public static void main(String[] args) {
+        try {
+            NonSpringJaxrsServletBookServer2 s = new NonSpringJaxrsServletBookServer2();
+            s.start();
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            System.exit(-1);
+        } finally {
+            System.out.println("done!");
+        }
+    }
+}